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 ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
358 return (index
+1) * This
->bigBlockSize
;
361 /************************************************************************
362 ** Storage32BaseImpl implementation
364 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
365 ULARGE_INTEGER offset
,
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
.u
.HighPart
= 0;
1326 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1329 * add a block to the directory stream
1331 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1334 * memset the empty entry in order to initialize the unused newly
1337 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1342 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1345 entryIndex
= newEntryIndex
+ 1;
1346 entryIndex
< lastEntry
;
1349 StorageImpl_WriteRawDirEntry(
1355 StorageImpl_SaveFileHeader(storage
);
1358 UpdateRawDirEntry(currentData
, newData
);
1360 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1363 *index
= newEntryIndex
;
1368 /***************************************************************************
1372 * Mark a directory entry in the file as free.
1374 static HRESULT
StorageImpl_DestroyDirEntry(
1375 StorageBaseImpl
*base
,
1378 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1379 StorageImpl
*storage
= (StorageImpl
*)base
;
1381 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1383 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1387 /****************************************************************************
1391 * Case insensitive comparison of DirEntry.name by first considering
1394 * Returns <0 when name1 < name2
1395 * >0 when name1 > name2
1396 * 0 when name1 == name2
1398 static LONG
entryNameCmp(
1399 const OLECHAR
*name1
,
1400 const OLECHAR
*name2
)
1402 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1404 while (diff
== 0 && *name1
!= 0)
1407 * We compare the string themselves only when they are of the same length
1409 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1415 /****************************************************************************
1419 * Add a directory entry to a storage
1421 static HRESULT
insertIntoTree(
1422 StorageBaseImpl
*This
,
1423 DirRef parentStorageIndex
,
1424 DirRef newEntryIndex
)
1426 DirEntry currentEntry
;
1430 * Read the inserted entry
1432 StorageBaseImpl_ReadDirEntry(This
,
1437 * Read the storage entry
1439 StorageBaseImpl_ReadDirEntry(This
,
1443 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1446 * The root storage contains some element, therefore, start the research
1447 * for the appropriate location.
1450 DirRef current
, next
, previous
, currentEntryId
;
1453 * Keep a reference to the root of the storage's element tree
1455 currentEntryId
= currentEntry
.dirRootEntry
;
1460 StorageBaseImpl_ReadDirEntry(This
,
1461 currentEntry
.dirRootEntry
,
1464 previous
= currentEntry
.leftChild
;
1465 next
= currentEntry
.rightChild
;
1466 current
= currentEntryId
;
1470 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1474 if (previous
!= DIRENTRY_NULL
)
1476 StorageBaseImpl_ReadDirEntry(This
,
1483 currentEntry
.leftChild
= newEntryIndex
;
1484 StorageBaseImpl_WriteDirEntry(This
,
1492 if (next
!= DIRENTRY_NULL
)
1494 StorageBaseImpl_ReadDirEntry(This
,
1501 currentEntry
.rightChild
= newEntryIndex
;
1502 StorageBaseImpl_WriteDirEntry(This
,
1511 * Trying to insert an item with the same name in the
1512 * subtree structure.
1514 return STG_E_FILEALREADYEXISTS
;
1517 previous
= currentEntry
.leftChild
;
1518 next
= currentEntry
.rightChild
;
1524 * The storage is empty, make the new entry the root of its element tree
1526 currentEntry
.dirRootEntry
= newEntryIndex
;
1527 StorageBaseImpl_WriteDirEntry(This
,
1535 /****************************************************************************
1539 * Find and read the element of a storage with the given name.
1541 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1542 const OLECHAR
*name
, DirEntry
*data
)
1544 DirRef currentEntry
;
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1549 currentEntry
= data
->dirRootEntry
;
1551 while (currentEntry
!= DIRENTRY_NULL
)
1555 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1557 cmp
= entryNameCmp(name
, data
->name
);
1564 currentEntry
= data
->leftChild
;
1567 currentEntry
= data
->rightChild
;
1570 return currentEntry
;
1573 /****************************************************************************
1577 * Find and read the binary tree parent of the element with the given name.
1579 * If there is no such element, find a place where it could be inserted and
1580 * return STG_E_FILENOTFOUND.
1582 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1583 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1589 /* Read the storage entry to find the root of the tree. */
1590 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1592 *parentEntry
= storageEntry
;
1593 *relation
= DIRENTRY_RELATION_DIR
;
1595 childEntry
= parentData
->dirRootEntry
;
1597 while (childEntry
!= DIRENTRY_NULL
)
1601 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1603 cmp
= entryNameCmp(childName
, childData
.name
);
1611 *parentData
= childData
;
1612 *parentEntry
= childEntry
;
1613 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1615 childEntry
= parentData
->leftChild
;
1620 *parentData
= childData
;
1621 *parentEntry
= childEntry
;
1622 *relation
= DIRENTRY_RELATION_NEXT
;
1624 childEntry
= parentData
->rightChild
;
1628 if (childEntry
== DIRENTRY_NULL
)
1629 return STG_E_FILENOTFOUND
;
1635 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1636 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1637 SNB snbExclude
, IStorage
*pstgDest
);
1639 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1640 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1641 SNB snbExclude
, IStorage
*pstgDest
)
1647 IStream
*pstrChild
, *pstrTmp
;
1650 if (srcEntry
== DIRENTRY_NULL
)
1653 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1660 WCHAR
**snb
= snbExclude
;
1662 while ( *snb
!= NULL
&& !skip
)
1664 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1672 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1675 * create a new storage in destination storage
1677 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1678 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1683 * if it already exist, don't create a new one use this one
1685 if (hr
== STG_E_FILEALREADYEXISTS
)
1687 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1688 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1689 NULL
, 0, &pstgTmp
);
1694 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1695 skip_stream
, NULL
, pstgTmp
);
1697 IStorage_Release(pstgTmp
);
1700 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1703 * create a new stream in destination storage. If the stream already
1704 * exist, it will be deleted and a new one will be created.
1706 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1707 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1711 * open child stream storage. This operation must succeed even if the
1712 * stream is already open, so we use internal functions to do it.
1716 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1720 pstrChild
= &streamimpl
->IStream_iface
;
1722 IStream_AddRef(pstrChild
);
1734 * Get the size of the source stream
1736 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1739 * Set the size of the destination stream.
1741 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1746 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1749 IStream_Release( pstrChild
);
1752 IStream_Release( pstrTmp
);
1758 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1759 skip_stream
, snbExclude
, pstgDest
);
1762 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1763 skip_stream
, snbExclude
, pstgDest
);
1768 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1769 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1770 SNB snbExclude
, IStorage
*pstgDest
)
1775 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1778 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1781 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1782 skip_stream
, snbExclude
, pstgDest
);
1787 /*************************************************************************
1790 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1792 DWORD ciidExclude
, /* [in] */
1793 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1794 SNB snbExclude
, /* [unique][in] */
1795 IStorage
* pstgDest
) /* [unique][in] */
1797 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1799 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1802 TRACE("(%p, %d, %p, %p, %p)\n",
1803 iface
, ciidExclude
, rgiidExclude
,
1804 snbExclude
, pstgDest
);
1806 if ( pstgDest
== 0 )
1807 return STG_E_INVALIDPOINTER
;
1809 for(i
= 0; i
< ciidExclude
; ++i
)
1811 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1812 skip_storage
= TRUE
;
1813 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1816 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1821 /* Give up early if it looks like this would be infinitely recursive.
1822 * Oddly enough, this includes some cases that aren't really recursive, like
1823 * copying to a transacted child. */
1824 IStorage
*pstgDestAncestor
= pstgDest
;
1825 IStorage
*pstgDestAncestorChild
= NULL
;
1827 /* Go up the chain from the destination until we find the source storage. */
1828 while (pstgDestAncestor
!= iface
) {
1829 pstgDestAncestorChild
= pstgDest
;
1831 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1833 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1835 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1837 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1839 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1841 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1847 if (pstgDestAncestor
== iface
)
1851 if (pstgDestAncestorChild
&& snbExclude
)
1853 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1855 WCHAR
**snb
= snbExclude
;
1857 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1859 while ( *snb
!= NULL
&& fail
)
1861 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1868 return STG_E_ACCESSDENIED
;
1872 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1873 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1876 /*************************************************************************
1877 * MoveElementTo (IStorage)
1879 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1881 const OLECHAR
*pwcsName
, /* [string][in] */
1882 IStorage
*pstgDest
, /* [unique][in] */
1883 const OLECHAR
*pwcsNewName
,/* [string][in] */
1884 DWORD grfFlags
) /* [in] */
1886 FIXME("(%p %s %p %s %u): stub\n", iface
,
1887 debugstr_w(pwcsName
), pstgDest
,
1888 debugstr_w(pwcsNewName
), grfFlags
);
1892 /*************************************************************************
1895 * Ensures that any changes made to a storage object open in transacted mode
1896 * are reflected in the parent storage
1898 * In a non-transacted mode, this ensures all cached writes are completed.
1900 static HRESULT WINAPI
StorageImpl_Commit(
1902 DWORD grfCommitFlags
)/* [in] */
1904 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1905 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1906 return StorageBaseImpl_Flush(This
);
1909 /*************************************************************************
1912 * Discard all changes that have been made since the last commit operation
1914 static HRESULT WINAPI
StorageImpl_Revert(
1917 TRACE("(%p)\n", iface
);
1921 /*************************************************************************
1922 * DestroyElement (IStorage)
1924 * Strategy: This implementation is built this way for simplicity not for speed.
1925 * I always delete the topmost element of the enumeration and adjust
1926 * the deleted element pointer all the time. This takes longer to
1927 * do but allow to reinvoke DestroyElement whenever we encounter a
1928 * storage object. The optimisation resides in the usage of another
1929 * enumeration strategy that would give all the leaves of a storage
1930 * first. (postfix order)
1932 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1934 const OLECHAR
*pwcsName
)/* [string][in] */
1936 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1939 DirEntry entryToDelete
;
1940 DirRef entryToDeleteRef
;
1943 iface
, debugstr_w(pwcsName
));
1946 return STG_E_INVALIDPOINTER
;
1949 return STG_E_REVERTED
;
1951 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1952 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1953 return STG_E_ACCESSDENIED
;
1955 entryToDeleteRef
= findElement(
1957 This
->storageDirEntry
,
1961 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1963 return STG_E_FILENOTFOUND
;
1966 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1968 hr
= deleteStorageContents(
1973 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1975 hr
= deleteStreamContents(
1985 * Remove the entry from its parent storage
1987 hr
= removeFromTree(
1989 This
->storageDirEntry
,
1993 * Invalidate the entry
1996 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1999 hr
= StorageBaseImpl_Flush(This
);
2005 /******************************************************************************
2006 * Internal stream list handlers
2009 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2011 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2012 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2015 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2017 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2018 list_remove(&(strm
->StrmListEntry
));
2021 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
2023 StgStreamImpl
*strm
;
2025 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
2027 if (strm
->dirEntry
== streamEntry
)
2036 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2038 StorageInternalImpl
*childstg
;
2040 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2042 if (childstg
->base
.storageDirEntry
== storageEntry
)
2051 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2053 struct list
*cur
, *cur2
;
2054 StgStreamImpl
*strm
=NULL
;
2055 StorageInternalImpl
*childstg
=NULL
;
2057 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2058 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2059 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2060 strm
->parentStorage
= NULL
;
2064 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2065 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2066 StorageBaseImpl_Invalidate( &childstg
->base
);
2069 if (stg
->transactedChild
)
2071 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2073 stg
->transactedChild
= NULL
;
2078 /*********************************************************************
2082 * Delete the contents of a storage entry.
2085 static HRESULT
deleteStorageContents(
2086 StorageBaseImpl
*parentStorage
,
2087 DirRef indexToDelete
,
2088 DirEntry entryDataToDelete
)
2090 IEnumSTATSTG
*elements
= 0;
2091 IStorage
*childStorage
= 0;
2092 STATSTG currentElement
;
2094 HRESULT destroyHr
= S_OK
;
2095 StorageInternalImpl
*stg
, *stg2
;
2097 /* Invalidate any open storage objects. */
2098 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2100 if (stg
->base
.storageDirEntry
== indexToDelete
)
2102 StorageBaseImpl_Invalidate(&stg
->base
);
2107 * Open the storage and enumerate it
2109 hr
= IStorage_OpenStorage(
2110 &parentStorage
->IStorage_iface
,
2111 entryDataToDelete
.name
,
2113 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2124 * Enumerate the elements
2126 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2131 * Obtain the next element
2133 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2136 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2138 CoTaskMemFree(currentElement
.pwcsName
);
2142 * We need to Reset the enumeration every time because we delete elements
2143 * and the enumeration could be invalid
2145 IEnumSTATSTG_Reset(elements
);
2147 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2149 IStorage_Release(childStorage
);
2150 IEnumSTATSTG_Release(elements
);
2155 /*********************************************************************
2159 * Perform the deletion of a stream's data
2162 static HRESULT
deleteStreamContents(
2163 StorageBaseImpl
*parentStorage
,
2164 DirRef indexToDelete
,
2165 DirEntry entryDataToDelete
)
2169 ULARGE_INTEGER size
;
2170 StgStreamImpl
*strm
, *strm2
;
2172 /* Invalidate any open stream objects. */
2173 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2175 if (strm
->dirEntry
== indexToDelete
)
2177 TRACE("Stream deleted %p\n", strm
);
2178 strm
->parentStorage
= NULL
;
2179 list_remove(&strm
->StrmListEntry
);
2183 size
.u
.HighPart
= 0;
2186 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2187 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2197 hr
= IStream_SetSize(pis
, size
);
2205 * Release the stream object.
2207 IStream_Release(pis
);
2212 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2216 case DIRENTRY_RELATION_PREVIOUS
:
2217 entry
->leftChild
= new_target
;
2219 case DIRENTRY_RELATION_NEXT
:
2220 entry
->rightChild
= new_target
;
2222 case DIRENTRY_RELATION_DIR
:
2223 entry
->dirRootEntry
= new_target
;
2230 /*************************************************************************
2234 * This method removes a directory entry from its parent storage tree without
2235 * freeing any resources attached to it.
2237 static HRESULT
removeFromTree(
2238 StorageBaseImpl
*This
,
2239 DirRef parentStorageIndex
,
2240 DirRef deletedIndex
)
2242 DirEntry entryToDelete
;
2243 DirEntry parentEntry
;
2244 DirRef parentEntryRef
;
2245 ULONG typeOfRelation
;
2248 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2254 * Find the element that links to the one we want to delete.
2256 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2257 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2262 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2265 * Replace the deleted entry with its left child
2267 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2269 hr
= StorageBaseImpl_WriteDirEntry(
2278 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2281 * We need to reinsert the right child somewhere. We already know it and
2282 * its children are greater than everything in the left tree, so we
2283 * insert it at the rightmost point in the left tree.
2285 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2286 DirEntry newRightChildParentEntry
;
2290 hr
= StorageBaseImpl_ReadDirEntry(
2292 newRightChildParent
,
2293 &newRightChildParentEntry
);
2299 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2300 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2301 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2303 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2305 hr
= StorageBaseImpl_WriteDirEntry(
2307 newRightChildParent
,
2308 &newRightChildParentEntry
);
2318 * Replace the deleted entry with its right child
2320 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2322 hr
= StorageBaseImpl_WriteDirEntry(
2336 /******************************************************************************
2337 * SetElementTimes (IStorage)
2339 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2341 const OLECHAR
*pwcsName
,/* [string][in] */
2342 const FILETIME
*pctime
, /* [in] */
2343 const FILETIME
*patime
, /* [in] */
2344 const FILETIME
*pmtime
) /* [in] */
2346 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2350 /******************************************************************************
2351 * SetStateBits (IStorage)
2353 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2355 DWORD grfStateBits
,/* [in] */
2356 DWORD grfMask
) /* [in] */
2358 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2361 return STG_E_REVERTED
;
2363 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2367 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2368 DirRef index
, const DirEntry
*data
)
2370 StorageImpl
*This
= (StorageImpl
*)base
;
2371 return StorageImpl_WriteDirEntry(This
, index
, data
);
2374 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2375 DirRef index
, DirEntry
*data
)
2377 StorageImpl
*This
= (StorageImpl
*)base
;
2378 return StorageImpl_ReadDirEntry(This
, index
, data
);
2381 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2385 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2387 if (!This
->blockChainCache
[i
])
2389 return &This
->blockChainCache
[i
];
2393 i
= This
->blockChainToEvict
;
2395 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2396 This
->blockChainCache
[i
] = NULL
;
2398 This
->blockChainToEvict
++;
2399 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2400 This
->blockChainToEvict
= 0;
2402 return &This
->blockChainCache
[i
];
2405 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2408 int i
, free_index
=-1;
2410 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2412 if (!This
->blockChainCache
[i
])
2414 if (free_index
== -1) free_index
= i
;
2416 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2418 return &This
->blockChainCache
[i
];
2422 if (free_index
== -1)
2424 free_index
= This
->blockChainToEvict
;
2426 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2427 This
->blockChainCache
[free_index
] = NULL
;
2429 This
->blockChainToEvict
++;
2430 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2431 This
->blockChainToEvict
= 0;
2434 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2435 return &This
->blockChainCache
[free_index
];
2438 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2442 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2444 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2446 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2447 This
->blockChainCache
[i
] = NULL
;
2453 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2454 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2456 StorageImpl
*This
= (StorageImpl
*)base
;
2461 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2462 if (FAILED(hr
)) return hr
;
2464 if (data
.size
.QuadPart
== 0)
2470 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2472 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2479 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2481 SmallBlockChainStream
*stream
;
2483 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2484 if (!stream
) return E_OUTOFMEMORY
;
2486 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2488 SmallBlockChainStream_Destroy(stream
);
2494 BlockChainStream
*stream
= NULL
;
2496 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2497 if (!stream
) return E_OUTOFMEMORY
;
2499 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2505 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2506 ULARGE_INTEGER newsize
)
2508 StorageImpl
*This
= (StorageImpl
*)base
;
2511 SmallBlockChainStream
*smallblock
=NULL
;
2512 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2514 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2515 if (FAILED(hr
)) return hr
;
2517 /* In simple mode keep the stream size above the small block limit */
2518 if (This
->base
.openFlags
& STGM_SIMPLE
)
2519 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2521 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2524 /* Create a block chain object of the appropriate type */
2525 if (data
.size
.QuadPart
== 0)
2527 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2529 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2530 if (!smallblock
) return E_OUTOFMEMORY
;
2534 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2535 bigblock
= *pbigblock
;
2536 if (!bigblock
) return E_OUTOFMEMORY
;
2539 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2541 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2542 if (!smallblock
) return E_OUTOFMEMORY
;
2546 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2547 bigblock
= *pbigblock
;
2548 if (!bigblock
) return E_OUTOFMEMORY
;
2551 /* Change the block chain type if necessary. */
2552 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2554 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2557 SmallBlockChainStream_Destroy(smallblock
);
2561 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2562 *pbigblock
= bigblock
;
2564 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2566 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2571 /* Set the size of the block chain. */
2574 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2575 SmallBlockChainStream_Destroy(smallblock
);
2579 BlockChainStream_SetSize(bigblock
, newsize
);
2582 /* Set the size in the directory entry. */
2583 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2586 data
.size
= newsize
;
2588 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2593 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2594 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2596 StorageImpl
*This
= (StorageImpl
*)base
;
2599 ULARGE_INTEGER newSize
;
2601 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2602 if (FAILED(hr
)) return hr
;
2604 /* Grow the stream if necessary */
2605 newSize
.QuadPart
= 0;
2606 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2608 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2610 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2614 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2615 if (FAILED(hr
)) return hr
;
2618 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2620 SmallBlockChainStream
*stream
;
2622 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2623 if (!stream
) return E_OUTOFMEMORY
;
2625 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2627 SmallBlockChainStream_Destroy(stream
);
2633 BlockChainStream
*stream
;
2635 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2636 if (!stream
) return E_OUTOFMEMORY
;
2638 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2642 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2645 StorageImpl
*This
= (StorageImpl
*)base
;
2646 DirEntry dst_data
, src_data
;
2649 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2652 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2656 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2657 dst_data
.startingBlock
= src_data
.startingBlock
;
2658 dst_data
.size
= src_data
.size
;
2660 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2666 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
2667 ULONG
* result
, BOOL refresh
)
2669 StorageImpl
*This
= (StorageImpl
*)base
;
2671 DWORD oldTransactionSig
= This
->transactionSig
;
2675 ULARGE_INTEGER offset
;
2679 offset
.u
.HighPart
= 0;
2680 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
2681 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
2685 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
2687 if (oldTransactionSig
!= This
->transactionSig
)
2689 /* Someone else wrote to this, so toss all cached information. */
2690 TRACE("signature changed\n");
2692 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
2696 This
->transactionSig
= oldTransactionSig
;
2700 *result
= This
->transactionSig
;
2705 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
2708 StorageImpl
*This
= (StorageImpl
*)base
;
2710 This
->transactionSig
= value
;
2711 StorageImpl_SaveFileHeader(This
);
2716 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
2718 StorageImpl
*This
= (StorageImpl
*)base
;
2720 ULARGE_INTEGER offset
, cb
;
2724 /* Synchronous grab of second priority range, the commit lock, and the
2725 * lock-checking lock. */
2726 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2727 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2731 offset
.QuadPart
= RANGELOCK_COMMIT
;
2735 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2737 if (hr
== STG_E_INVALIDFUNCTION
)
2743 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
2745 StorageImpl
*This
= (StorageImpl
*)base
;
2747 ULARGE_INTEGER offset
, cb
;
2751 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2752 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2756 offset
.QuadPart
= RANGELOCK_COMMIT
;
2760 hr
= ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2762 if (hr
== STG_E_INVALIDFUNCTION
)
2768 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2770 StorageImpl
*This
= (StorageImpl
*) iface
;
2774 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2776 *result
= statstg
.pwcsName
;
2781 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
2783 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2784 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
2787 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
2789 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2790 return IStorage_AddRef(&This
->IStorage_iface
);
2793 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
2795 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2796 return IStorage_Release(&This
->IStorage_iface
);
2799 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
2801 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2802 FIXME("(%p)->(%d): stub\n", This
, timeout
);
2806 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
2808 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2809 FIXME("(%p): stub\n", This
);
2813 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
2815 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2816 FIXME("(%p): stub\n", This
);
2820 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
2822 directwriterlock_QueryInterface
,
2823 directwriterlock_AddRef
,
2824 directwriterlock_Release
,
2825 directwriterlock_WaitForWriteAccess
,
2826 directwriterlock_ReleaseWriteAccess
,
2827 directwriterlock_HaveWriteAccess
2831 * Virtual function table for the IStorage32Impl class.
2833 static const IStorageVtbl Storage32Impl_Vtbl
=
2835 StorageBaseImpl_QueryInterface
,
2836 StorageBaseImpl_AddRef
,
2837 StorageBaseImpl_Release
,
2838 StorageBaseImpl_CreateStream
,
2839 StorageBaseImpl_OpenStream
,
2840 StorageBaseImpl_CreateStorage
,
2841 StorageBaseImpl_OpenStorage
,
2842 StorageBaseImpl_CopyTo
,
2843 StorageBaseImpl_MoveElementTo
,
2846 StorageBaseImpl_EnumElements
,
2847 StorageBaseImpl_DestroyElement
,
2848 StorageBaseImpl_RenameElement
,
2849 StorageBaseImpl_SetElementTimes
,
2850 StorageBaseImpl_SetClass
,
2851 StorageBaseImpl_SetStateBits
,
2852 StorageBaseImpl_Stat
2855 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2857 StorageImpl_Destroy
,
2858 StorageImpl_Invalidate
,
2860 StorageImpl_GetFilename
,
2861 StorageImpl_CreateDirEntry
,
2862 StorageImpl_BaseWriteDirEntry
,
2863 StorageImpl_BaseReadDirEntry
,
2864 StorageImpl_DestroyDirEntry
,
2865 StorageImpl_StreamReadAt
,
2866 StorageImpl_StreamWriteAt
,
2867 StorageImpl_StreamSetSize
,
2868 StorageImpl_StreamLink
,
2869 StorageImpl_GetTransactionSig
,
2870 StorageImpl_SetTransactionSig
,
2871 StorageImpl_LockTransaction
,
2872 StorageImpl_UnlockTransaction
2875 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
2876 ULARGE_INTEGER cb
, DWORD dwLockType
)
2880 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2881 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This
->lockBytes
, offset
, cb
)))
2884 /* otherwise we have to fake it based on an async lock */
2889 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
2891 if (hr
== STG_E_ACCESSDENIED
)
2894 if (delay
< 150) delay
++;
2896 } while (hr
== STG_E_ACCESSDENIED
);
2901 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
2902 ULONG end
, HRESULT fail_hr
)
2905 ULARGE_INTEGER offset
, cb
;
2907 offset
.QuadPart
= start
;
2908 cb
.QuadPart
= 1 + end
- start
;
2910 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2911 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2913 if (hr
== STG_E_ACCESSDENIED
)
2919 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
2923 ULARGE_INTEGER offset
, cb
;
2927 for (i
=start
; i
<=end
; i
++)
2929 offset
.QuadPart
= i
;
2930 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2931 if (hr
!= STG_E_ACCESSDENIED
)
2937 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
2939 if (This
->locked_bytes
[j
] == 0)
2941 This
->locked_bytes
[j
] = i
;
2950 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
2953 ULARGE_INTEGER offset
;
2955 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
2957 if (openFlags
& STGM_NOSNAPSHOT
)
2959 /* STGM_NOSNAPSHOT implies deny write */
2960 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
2961 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
2964 /* Wrap all other locking inside a single lock so we can check ranges safely */
2965 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
2967 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2969 /* If the ILockBytes doesn't support locking that's ok. */
2970 if (FAILED(hr
)) return S_OK
;
2974 /* First check for any conflicting locks. */
2975 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2976 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
2978 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2979 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
2981 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2982 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
2984 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2985 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
2987 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2988 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
2990 /* Then grab our locks. */
2991 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2993 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
2995 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
2998 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2999 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
3001 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
3002 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
3004 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3005 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
3007 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3008 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
3010 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
3011 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
3013 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
3015 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3020 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
3023 DirEntry currentEntry
;
3024 DirRef currentEntryRef
;
3025 BlockChainStream
*blockChainStream
;
3029 ULARGE_INTEGER size
;
3030 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
3032 /* Discard any existing data. */
3034 ILockBytes_SetSize(This
->lockBytes
, size
);
3037 * Initialize all header variables:
3038 * - The big block depot consists of one block and it is at block 0
3039 * - The directory table starts at block 1
3040 * - There is no small block depot
3042 memset( This
->bigBlockDepotStart
,
3044 sizeof(This
->bigBlockDepotStart
));
3046 This
->bigBlockDepotCount
= 1;
3047 This
->bigBlockDepotStart
[0] = 0;
3048 This
->rootStartBlock
= 1;
3049 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
3050 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3051 if (This
->bigBlockSize
== 4096)
3052 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
3054 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
3055 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
3056 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3057 This
->extBigBlockDepotCount
= 0;
3059 StorageImpl_SaveFileHeader(This
);
3062 * Add one block for the big block depot and one block for the directory table
3064 size
.u
.HighPart
= 0;
3065 size
.u
.LowPart
= This
->bigBlockSize
* 3;
3066 ILockBytes_SetSize(This
->lockBytes
, size
);
3069 * Initialize the big block depot
3071 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3072 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
3073 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
3074 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
3079 * Load the header for the file.
3081 hr
= StorageImpl_LoadFileHeader(This
);
3090 * There is no block depot cached yet.
3092 This
->indexBlockDepotCached
= 0xFFFFFFFF;
3093 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
3096 * Start searching for free blocks with block 0.
3098 This
->prevFreeBlock
= 0;
3100 This
->firstFreeSmallBlock
= 0;
3102 /* Read the extended big block depot locations. */
3103 if (This
->extBigBlockDepotCount
!= 0)
3105 ULONG current_block
= This
->extBigBlockDepotStart
;
3106 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
3109 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
3110 if (!This
->extBigBlockDepotLocations
)
3112 return E_OUTOFMEMORY
;
3115 This
->extBigBlockDepotLocationsSize
= cache_size
;
3117 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
3119 if (current_block
== BLOCK_END_OF_CHAIN
)
3121 WARN("File has too few extended big block depot blocks.\n");
3122 return STG_E_DOCFILECORRUPT
;
3124 This
->extBigBlockDepotLocations
[i
] = current_block
;
3125 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
3130 This
->extBigBlockDepotLocations
= NULL
;
3131 This
->extBigBlockDepotLocationsSize
= 0;
3135 * Create the block chain abstractions.
3137 if(!(blockChainStream
=
3138 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
3140 return STG_E_READFAULT
;
3143 BlockChainStream_Destroy(This
->rootBlockChain
);
3144 This
->rootBlockChain
= blockChainStream
;
3146 if(!(blockChainStream
=
3147 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
3150 return STG_E_READFAULT
;
3153 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3154 This
->smallBlockDepotChain
= blockChainStream
;
3157 * Write the root storage entry (memory only)
3161 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3164 * Initialize the directory table
3166 memset(&rootEntry
, 0, sizeof(rootEntry
));
3167 strcpyW(rootEntry
.name
, rootentryW
);
3168 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
3169 rootEntry
.stgType
= STGTY_ROOT
;
3170 rootEntry
.leftChild
= DIRENTRY_NULL
;
3171 rootEntry
.rightChild
= DIRENTRY_NULL
;
3172 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
3173 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
3174 rootEntry
.size
.u
.HighPart
= 0;
3175 rootEntry
.size
.u
.LowPart
= 0;
3177 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
3181 * Find the ID of the root storage.
3183 currentEntryRef
= 0;
3187 hr
= StorageImpl_ReadDirEntry(
3194 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
3195 (currentEntry
.stgType
== STGTY_ROOT
) )
3197 This
->base
.storageDirEntry
= currentEntryRef
;
3203 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
3207 return STG_E_READFAULT
;
3211 * Create the block chain abstraction for the small block root chain.
3213 if(!(blockChainStream
=
3214 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
3216 return STG_E_READFAULT
;
3219 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3220 This
->smallBlockRootChain
= blockChainStream
;
3225 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3227 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3228 This
->blockChainCache
[i
] = NULL
;
3235 static HRESULT
StorageImpl_Construct(
3243 StorageImpl
** result
)
3248 if ( FAILED( validateSTGM(openFlags
) ))
3249 return STG_E_INVALIDFLAG
;
3251 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
3253 return E_OUTOFMEMORY
;
3255 memset(This
, 0, sizeof(StorageImpl
));
3257 list_init(&This
->base
.strmHead
);
3259 list_init(&This
->base
.storageHead
);
3261 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
3262 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
3263 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
3264 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
3265 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
3267 This
->base
.create
= create
;
3269 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
3270 This
->base
.lockingrole
= SWMR_Writer
;
3271 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
3272 This
->base
.lockingrole
= SWMR_Reader
;
3274 This
->base
.lockingrole
= SWMR_None
;
3276 This
->base
.reverted
= FALSE
;
3279 * Initialize the big block cache.
3281 This
->bigBlockSize
= sector_size
;
3282 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
3284 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
3287 This
->lockBytes
= pLkbyt
;
3288 ILockBytes_AddRef(pLkbyt
);
3292 hr
= StorageImpl_GrabLocks(This
, openFlags
);
3295 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
3299 IStorage_Release(&This
->base
.IStorage_iface
);
3304 StorageImpl_Flush(&This
->base
);
3311 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
3313 StorageImpl
*This
= (StorageImpl
*) iface
;
3315 StorageBaseImpl_DeleteAll(&This
->base
);
3317 This
->base
.reverted
= TRUE
;
3320 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3322 StorageImpl
*This
= (StorageImpl
*) iface
;
3324 TRACE("(%p)\n", This
);
3326 StorageImpl_Flush(iface
);
3328 StorageImpl_Invalidate(iface
);
3330 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3332 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3333 BlockChainStream_Destroy(This
->rootBlockChain
);
3334 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3336 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3337 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3339 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
3341 ULARGE_INTEGER offset
, cb
;
3343 if (This
->locked_bytes
[i
] != 0)
3345 offset
.QuadPart
= This
->locked_bytes
[i
];
3346 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3350 if (This
->lockBytes
)
3351 ILockBytes_Release(This
->lockBytes
);
3352 HeapFree(GetProcessHeap(), 0, This
);
3355 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3357 StorageImpl
*This
= (StorageImpl
*)storage
;
3360 TRACE("(%p)\n", This
);
3362 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3365 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3368 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3370 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3371 if (This
->blockChainCache
[i
])
3372 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3375 hr
= ILockBytes_Flush(This
->lockBytes
);
3380 /******************************************************************************
3381 * Storage32Impl_GetNextFreeBigBlock
3383 * Returns the index of the next free big block.
3384 * If the big block depot is filled, this method will enlarge it.
3387 static ULONG
StorageImpl_GetNextFreeBigBlock(
3390 ULONG depotBlockIndexPos
;
3391 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3392 ULONG depotBlockOffset
;
3393 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3394 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3396 ULONG freeBlock
= BLOCK_UNUSED
;
3398 ULARGE_INTEGER neededSize
;
3401 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3402 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3405 * Scan the entire big block depot until we find a block marked free
3407 while (nextBlockIndex
!= BLOCK_UNUSED
)
3409 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3411 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3414 * Grow the primary depot.
3416 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3418 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3421 * Add a block depot.
3423 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3424 This
->bigBlockDepotCount
++;
3425 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3428 * Flag it as a block depot.
3430 StorageImpl_SetNextBlockInChain(This
,
3434 /* Save new header information.
3436 StorageImpl_SaveFileHeader(This
);
3441 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3443 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3446 * Grow the extended depot.
3448 ULONG extIndex
= BLOCK_UNUSED
;
3449 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3450 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3452 if (extBlockOffset
== 0)
3454 /* We need an extended block.
3456 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3457 This
->extBigBlockDepotCount
++;
3458 depotBlockIndexPos
= extIndex
+ 1;
3461 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3464 * Add a block depot and mark it in the extended block.
3466 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3467 This
->bigBlockDepotCount
++;
3468 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3470 /* Flag the block depot.
3472 StorageImpl_SetNextBlockInChain(This
,
3476 /* If necessary, flag the extended depot block.
3478 if (extIndex
!= BLOCK_UNUSED
)
3479 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3481 /* Save header information.
3483 StorageImpl_SaveFileHeader(This
);
3487 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3491 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3492 ( nextBlockIndex
!= BLOCK_UNUSED
))
3494 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3496 if (nextBlockIndex
== BLOCK_UNUSED
)
3498 freeBlock
= (depotIndex
* blocksPerDepot
) +
3499 (depotBlockOffset
/sizeof(ULONG
));
3502 depotBlockOffset
+= sizeof(ULONG
);
3507 depotBlockOffset
= 0;
3511 * make sure that the block physically exists before using it
3513 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3515 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3517 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3518 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3520 This
->prevFreeBlock
= freeBlock
;
3525 /******************************************************************************
3526 * Storage32Impl_AddBlockDepot
3528 * This will create a depot block, essentially it is a block initialized
3531 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3533 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3534 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3535 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3536 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3539 * Initialize blocks as free
3541 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3543 /* Reserve the range lock sector */
3544 if (depotIndex
== rangeLockDepot
)
3546 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3549 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3552 /******************************************************************************
3553 * Storage32Impl_GetExtDepotBlock
3555 * Returns the index of the block that corresponds to the specified depot
3556 * index. This method is only for depot indexes equal or greater than
3557 * COUNT_BBDEPOTINHEADER.
3559 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3561 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3562 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3563 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3564 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3565 ULONG blockIndex
= BLOCK_UNUSED
;
3566 ULONG extBlockIndex
;
3567 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3568 int index
, num_blocks
;
3570 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3572 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3573 return BLOCK_UNUSED
;
3575 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3577 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3579 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3581 num_blocks
= This
->bigBlockSize
/ 4;
3583 for (index
= 0; index
< num_blocks
; index
++)
3585 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3586 This
->extBlockDepotCached
[index
] = blockIndex
;
3589 This
->indexExtBlockDepotCached
= extBlockCount
;
3592 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3597 /******************************************************************************
3598 * Storage32Impl_SetExtDepotBlock
3600 * Associates the specified block index to the specified depot index.
3601 * This method is only for depot indexes equal or greater than
3602 * COUNT_BBDEPOTINHEADER.
3604 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3606 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3607 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3608 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3609 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3610 ULONG extBlockIndex
;
3612 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3614 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3616 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3618 if (extBlockIndex
!= BLOCK_UNUSED
)
3620 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3621 extBlockOffset
* sizeof(ULONG
),
3625 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3627 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3631 /******************************************************************************
3632 * Storage32Impl_AddExtBlockDepot
3634 * Creates an extended depot block.
3636 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3638 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3639 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3640 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3641 ULONG index
= BLOCK_UNUSED
;
3642 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3643 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3644 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3646 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3647 blocksPerDepotBlock
;
3649 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3652 * The first extended block.
3654 This
->extBigBlockDepotStart
= index
;
3659 * Find the last existing extended block.
3661 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3664 * Add the new extended block to the chain.
3666 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3671 * Initialize this block.
3673 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3674 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3676 /* Add the block to our cache. */
3677 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3679 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3680 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3682 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3683 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3685 This
->extBigBlockDepotLocations
= new_cache
;
3686 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3688 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3693 /******************************************************************************
3694 * Storage32Impl_FreeBigBlock
3696 * This method will flag the specified block as free in the big block depot.
3698 static void StorageImpl_FreeBigBlock(
3702 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3704 if (blockIndex
< This
->prevFreeBlock
)
3705 This
->prevFreeBlock
= blockIndex
;
3708 /************************************************************************
3709 * Storage32Impl_GetNextBlockInChain
3711 * This method will retrieve the block index of the next big block in
3714 * Params: This - Pointer to the Storage object.
3715 * blockIndex - Index of the block to retrieve the chain
3717 * nextBlockIndex - receives the return value.
3719 * Returns: This method returns the index of the next block in the chain.
3720 * It will return the constants:
3721 * BLOCK_SPECIAL - If the block given was not part of a
3723 * BLOCK_END_OF_CHAIN - If the block given was the last in
3725 * BLOCK_UNUSED - If the block given was not past of a chain
3727 * BLOCK_EXTBBDEPOT - This block is part of the extended
3730 * See Windows documentation for more details on IStorage methods.
3732 static HRESULT
StorageImpl_GetNextBlockInChain(
3735 ULONG
* nextBlockIndex
)
3737 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3738 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3739 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3740 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3742 ULONG depotBlockIndexPos
;
3743 int index
, num_blocks
;
3745 *nextBlockIndex
= BLOCK_SPECIAL
;
3747 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3749 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3750 This
->bigBlockDepotCount
);
3751 return STG_E_READFAULT
;
3755 * Cache the currently accessed depot block.
3757 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3759 This
->indexBlockDepotCached
= depotBlockCount
;
3761 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3763 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3768 * We have to look in the extended depot.
3770 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3773 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3776 return STG_E_READFAULT
;
3778 num_blocks
= This
->bigBlockSize
/ 4;
3780 for (index
= 0; index
< num_blocks
; index
++)
3782 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3783 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3787 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3792 /******************************************************************************
3793 * Storage32Impl_GetNextExtendedBlock
3795 * Given an extended block this method will return the next extended block.
3798 * The last ULONG of an extended block is the block index of the next
3799 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3803 * - The index of the next extended block
3804 * - BLOCK_UNUSED: there is no next extended block.
3805 * - Any other return values denotes failure.
3807 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3809 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3810 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3812 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3815 return nextBlockIndex
;
3818 /******************************************************************************
3819 * Storage32Impl_SetNextBlockInChain
3821 * This method will write the index of the specified block's next block
3822 * in the big block depot.
3824 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3827 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3828 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3829 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3832 static void StorageImpl_SetNextBlockInChain(
3837 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3838 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3839 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3840 ULONG depotBlockIndexPos
;
3842 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3843 assert(blockIndex
!= nextBlock
);
3845 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
3846 /* This should never happen (storage file format spec forbids it), but
3847 * older versions of Wine may have generated broken files. We don't want to
3848 * assert and potentially lose data, but we do want to know if this ever
3849 * happens in a newly-created file. */
3850 ERR("Using range lock page\n");
3852 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3854 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3859 * We have to look in the extended depot.
3861 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3864 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3867 * Update the cached block depot, if necessary.
3869 if (depotBlockCount
== This
->indexBlockDepotCached
)
3871 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3875 /******************************************************************************
3876 * Storage32Impl_LoadFileHeader
3878 * This method will read in the file header
3880 static HRESULT
StorageImpl_LoadFileHeader(
3884 BYTE headerBigBlock
[HEADER_SIZE
];
3886 ULARGE_INTEGER offset
;
3891 * Get a pointer to the big block of data containing the header.
3893 offset
.u
.HighPart
= 0;
3894 offset
.u
.LowPart
= 0;
3895 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3896 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3897 hr
= STG_E_FILENOTFOUND
;
3900 * Extract the information from the header.
3905 * Check for the "magic number" signature and return an error if it is not
3908 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3910 return STG_E_OLDFORMAT
;
3913 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3915 return STG_E_INVALIDHEADER
;
3918 StorageUtl_ReadWord(
3920 OFFSET_BIGBLOCKSIZEBITS
,
3921 &This
->bigBlockSizeBits
);
3923 StorageUtl_ReadWord(
3925 OFFSET_SMALLBLOCKSIZEBITS
,
3926 &This
->smallBlockSizeBits
);
3928 StorageUtl_ReadDWord(
3930 OFFSET_BBDEPOTCOUNT
,
3931 &This
->bigBlockDepotCount
);
3933 StorageUtl_ReadDWord(
3935 OFFSET_ROOTSTARTBLOCK
,
3936 &This
->rootStartBlock
);
3938 StorageUtl_ReadDWord(
3940 OFFSET_TRANSACTIONSIG
,
3941 &This
->transactionSig
);
3943 StorageUtl_ReadDWord(
3945 OFFSET_SMALLBLOCKLIMIT
,
3946 &This
->smallBlockLimit
);
3948 StorageUtl_ReadDWord(
3950 OFFSET_SBDEPOTSTART
,
3951 &This
->smallBlockDepotStart
);
3953 StorageUtl_ReadDWord(
3955 OFFSET_EXTBBDEPOTSTART
,
3956 &This
->extBigBlockDepotStart
);
3958 StorageUtl_ReadDWord(
3960 OFFSET_EXTBBDEPOTCOUNT
,
3961 &This
->extBigBlockDepotCount
);
3963 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3965 StorageUtl_ReadDWord(
3967 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3968 &(This
->bigBlockDepotStart
[index
]));
3972 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3974 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3975 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3978 * Right now, the code is making some assumptions about the size of the
3979 * blocks, just make sure they are what we're expecting.
3981 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3982 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3983 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3985 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3986 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3987 hr
= STG_E_INVALIDHEADER
;
3996 /******************************************************************************
3997 * Storage32Impl_SaveFileHeader
3999 * This method will save to the file the header
4001 static void StorageImpl_SaveFileHeader(
4004 BYTE headerBigBlock
[HEADER_SIZE
];
4007 ULARGE_INTEGER offset
;
4008 DWORD bytes_read
, bytes_written
;
4009 DWORD major_version
, dirsectorcount
;
4012 * Get a pointer to the big block of data containing the header.
4014 offset
.u
.HighPart
= 0;
4015 offset
.u
.LowPart
= 0;
4016 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
4017 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
4018 hr
= STG_E_FILENOTFOUND
;
4020 if (This
->bigBlockSizeBits
== 0x9)
4022 else if (This
->bigBlockSizeBits
== 0xc)
4026 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
4031 * If the block read failed, the file is probably new.
4036 * Initialize for all unknown fields.
4038 memset(headerBigBlock
, 0, HEADER_SIZE
);
4041 * Initialize the magic number.
4043 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
4047 * Write the information to the header.
4049 StorageUtl_WriteWord(
4051 OFFSET_MINORVERSION
,
4054 StorageUtl_WriteWord(
4056 OFFSET_MAJORVERSION
,
4059 StorageUtl_WriteWord(
4061 OFFSET_BYTEORDERMARKER
,
4064 StorageUtl_WriteWord(
4066 OFFSET_BIGBLOCKSIZEBITS
,
4067 This
->bigBlockSizeBits
);
4069 StorageUtl_WriteWord(
4071 OFFSET_SMALLBLOCKSIZEBITS
,
4072 This
->smallBlockSizeBits
);
4074 if (major_version
>= 4)
4076 if (This
->rootBlockChain
)
4077 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
4079 /* This file is being created, and it will start out with one block. */
4083 /* This field must be 0 in versions older than 4 */
4086 StorageUtl_WriteDWord(
4088 OFFSET_DIRSECTORCOUNT
,
4091 StorageUtl_WriteDWord(
4093 OFFSET_BBDEPOTCOUNT
,
4094 This
->bigBlockDepotCount
);
4096 StorageUtl_WriteDWord(
4098 OFFSET_ROOTSTARTBLOCK
,
4099 This
->rootStartBlock
);
4101 StorageUtl_WriteDWord(
4103 OFFSET_TRANSACTIONSIG
,
4104 This
->transactionSig
);
4106 StorageUtl_WriteDWord(
4108 OFFSET_SMALLBLOCKLIMIT
,
4109 This
->smallBlockLimit
);
4111 StorageUtl_WriteDWord(
4113 OFFSET_SBDEPOTSTART
,
4114 This
->smallBlockDepotStart
);
4116 StorageUtl_WriteDWord(
4118 OFFSET_SBDEPOTCOUNT
,
4119 This
->smallBlockDepotChain
?
4120 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
4122 StorageUtl_WriteDWord(
4124 OFFSET_EXTBBDEPOTSTART
,
4125 This
->extBigBlockDepotStart
);
4127 StorageUtl_WriteDWord(
4129 OFFSET_EXTBBDEPOTCOUNT
,
4130 This
->extBigBlockDepotCount
);
4132 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
4134 StorageUtl_WriteDWord(
4136 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
4137 (This
->bigBlockDepotStart
[index
]));
4141 * Write the big block back to the file.
4143 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
4146 /******************************************************************************
4147 * StorageImpl_ReadRawDirEntry
4149 * This method will read the raw data from a directory entry in the file.
4151 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4153 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
4155 ULARGE_INTEGER offset
;
4159 offset
.u
.HighPart
= 0;
4160 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
4162 hr
= BlockChainStream_ReadAt(
4163 This
->rootBlockChain
,
4169 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
4170 return STG_E_READFAULT
;
4175 /******************************************************************************
4176 * StorageImpl_WriteRawDirEntry
4178 * This method will write the raw data from a directory entry in the file.
4180 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4182 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
4184 ULARGE_INTEGER offset
;
4187 offset
.u
.HighPart
= 0;
4188 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
4190 return BlockChainStream_WriteAt(
4191 This
->rootBlockChain
,
4198 /******************************************************************************
4201 * Update raw directory entry data from the fields in newData.
4203 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4205 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
4207 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
4210 buffer
+ OFFSET_PS_NAME
,
4212 DIRENTRY_NAME_BUFFER_LEN
);
4214 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
4216 StorageUtl_WriteWord(
4218 OFFSET_PS_NAMELENGTH
,
4219 newData
->sizeOfNameString
);
4221 StorageUtl_WriteDWord(
4223 OFFSET_PS_LEFTCHILD
,
4224 newData
->leftChild
);
4226 StorageUtl_WriteDWord(
4228 OFFSET_PS_RIGHTCHILD
,
4229 newData
->rightChild
);
4231 StorageUtl_WriteDWord(
4234 newData
->dirRootEntry
);
4236 StorageUtl_WriteGUID(
4241 StorageUtl_WriteDWord(
4244 newData
->ctime
.dwLowDateTime
);
4246 StorageUtl_WriteDWord(
4248 OFFSET_PS_CTIMEHIGH
,
4249 newData
->ctime
.dwHighDateTime
);
4251 StorageUtl_WriteDWord(
4254 newData
->mtime
.dwLowDateTime
);
4256 StorageUtl_WriteDWord(
4258 OFFSET_PS_MTIMEHIGH
,
4259 newData
->ctime
.dwHighDateTime
);
4261 StorageUtl_WriteDWord(
4263 OFFSET_PS_STARTBLOCK
,
4264 newData
->startingBlock
);
4266 StorageUtl_WriteDWord(
4269 newData
->size
.u
.LowPart
);
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 buffer
->size
.u
.HighPart
= 0;
4359 /*********************************************************************
4360 * Write the specified directory entry to the file
4362 HRESULT
StorageImpl_WriteDirEntry(
4365 const DirEntry
* buffer
)
4367 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4369 UpdateRawDirEntry(currentEntry
, buffer
);
4371 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4374 static HRESULT
StorageImpl_ReadBigBlock(
4380 ULARGE_INTEGER ulOffset
;
4384 ulOffset
.u
.HighPart
= 0;
4385 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4387 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4389 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4391 /* File ends during this block; fill the rest with 0's. */
4392 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4395 if (out_read
) *out_read
= read
;
4400 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4406 ULARGE_INTEGER ulOffset
;
4410 ulOffset
.u
.HighPart
= 0;
4411 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4412 ulOffset
.u
.LowPart
+= offset
;
4414 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4415 *value
= lendian32toh(tmp
);
4416 return (read
== sizeof(DWORD
));
4419 static BOOL
StorageImpl_WriteBigBlock(
4424 ULARGE_INTEGER ulOffset
;
4427 ulOffset
.u
.HighPart
= 0;
4428 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4430 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4431 return (wrote
== This
->bigBlockSize
);
4434 static BOOL
StorageImpl_WriteDWordToBigBlock(
4440 ULARGE_INTEGER ulOffset
;
4443 ulOffset
.u
.HighPart
= 0;
4444 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4445 ulOffset
.u
.LowPart
+= offset
;
4447 value
= htole32(value
);
4448 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4449 return (wrote
== sizeof(DWORD
));
4452 /******************************************************************************
4453 * Storage32Impl_SmallBlocksToBigBlocks
4455 * This method will convert a small block chain to a big block chain.
4456 * The small block chain will be destroyed.
4458 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4460 SmallBlockChainStream
** ppsbChain
)
4462 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4463 ULARGE_INTEGER size
, offset
;
4464 ULONG cbRead
, cbWritten
;
4465 ULARGE_INTEGER cbTotalRead
;
4466 DirRef streamEntryRef
;
4467 HRESULT resWrite
= S_OK
;
4469 DirEntry streamEntry
;
4471 BlockChainStream
*bbTempChain
= NULL
;
4472 BlockChainStream
*bigBlockChain
= NULL
;
4475 * Create a temporary big block chain that doesn't have
4476 * an associated directory entry. This temporary chain will be
4477 * used to copy data from small blocks to big blocks.
4479 bbTempChain
= BlockChainStream_Construct(This
,
4482 if(!bbTempChain
) return NULL
;
4484 * Grow the big block chain.
4486 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4487 BlockChainStream_SetSize(bbTempChain
, size
);
4490 * Copy the contents of the small block chain to the big block chain
4491 * by small block size increments.
4493 offset
.u
.LowPart
= 0;
4494 offset
.u
.HighPart
= 0;
4495 cbTotalRead
.QuadPart
= 0;
4497 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4500 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4502 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4505 if (FAILED(resRead
))
4510 cbTotalRead
.QuadPart
+= cbRead
;
4512 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4518 if (FAILED(resWrite
))
4521 offset
.u
.LowPart
+= cbRead
;
4525 resRead
= STG_E_READFAULT
;
4528 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4529 HeapFree(GetProcessHeap(),0,buffer
);
4531 size
.u
.HighPart
= 0;
4534 if (FAILED(resRead
) || FAILED(resWrite
))
4536 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4537 BlockChainStream_SetSize(bbTempChain
, size
);
4538 BlockChainStream_Destroy(bbTempChain
);
4543 * Destroy the small block chain.
4545 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4546 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4547 SmallBlockChainStream_Destroy(*ppsbChain
);
4551 * Change the directory entry. This chain is now a big block chain
4552 * and it doesn't reside in the small blocks chain anymore.
4554 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4556 streamEntry
.startingBlock
= bbHeadOfChain
;
4558 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4561 * Destroy the temporary entryless big block chain.
4562 * Create a new big block chain associated with this entry.
4564 BlockChainStream_Destroy(bbTempChain
);
4565 bigBlockChain
= BlockChainStream_Construct(This
,
4569 return bigBlockChain
;
4572 /******************************************************************************
4573 * Storage32Impl_BigBlocksToSmallBlocks
4575 * This method will convert a big block chain to a small block chain.
4576 * The big block chain will be destroyed on success.
4578 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4580 BlockChainStream
** ppbbChain
,
4581 ULARGE_INTEGER newSize
)
4583 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4584 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4585 DirRef streamEntryRef
;
4586 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4587 DirEntry streamEntry
;
4589 SmallBlockChainStream
* sbTempChain
;
4591 TRACE("%p %p\n", This
, ppbbChain
);
4593 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4599 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4600 size
= BlockChainStream_GetSize(*ppbbChain
);
4601 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4603 offset
.u
.HighPart
= 0;
4604 offset
.u
.LowPart
= 0;
4605 cbTotalRead
.QuadPart
= 0;
4606 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4607 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4609 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4610 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4618 cbTotalRead
.QuadPart
+= cbRead
;
4620 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4621 cbRead
, buffer
, &cbWritten
);
4623 if(FAILED(resWrite
))
4626 offset
.u
.LowPart
+= cbRead
;
4630 resRead
= STG_E_READFAULT
;
4634 HeapFree(GetProcessHeap(), 0, buffer
);
4636 size
.u
.HighPart
= 0;
4639 if(FAILED(resRead
) || FAILED(resWrite
))
4641 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4642 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4643 SmallBlockChainStream_Destroy(sbTempChain
);
4647 /* destroy the original big block chain */
4648 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4649 BlockChainStream_SetSize(*ppbbChain
, size
);
4650 BlockChainStream_Destroy(*ppbbChain
);
4653 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4654 streamEntry
.startingBlock
= sbHeadOfChain
;
4655 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4657 SmallBlockChainStream_Destroy(sbTempChain
);
4658 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4661 static HRESULT
StorageBaseImpl_CopyStream(
4662 StorageBaseImpl
*dst
, DirRef dst_entry
,
4663 StorageBaseImpl
*src
, DirRef src_entry
)
4668 ULARGE_INTEGER bytes_copied
;
4669 ULONG bytestocopy
, bytesread
, byteswritten
;
4671 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4675 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4677 bytes_copied
.QuadPart
= 0;
4678 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4680 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4682 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4684 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4687 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4688 data
, &byteswritten
);
4691 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4692 bytes_copied
.QuadPart
+= byteswritten
;
4700 static HRESULT
StorageBaseImpl_DupStorageTree(
4701 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
4702 StorageBaseImpl
*src
, DirRef src_entry
)
4706 BOOL has_stream
=FALSE
;
4708 if (src_entry
== DIRENTRY_NULL
)
4710 *dst_entry
= DIRENTRY_NULL
;
4714 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
4717 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
4718 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
4719 data
.size
.QuadPart
= 0;
4721 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
4725 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
4728 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
4731 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
4733 if (SUCCEEDED(hr
) && has_stream
)
4734 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
4739 static HRESULT
StorageBaseImpl_CopyStorageTree(
4740 StorageBaseImpl
*dst
, DirRef dst_entry
,
4741 StorageBaseImpl
*src
, DirRef src_entry
)
4744 DirEntry src_data
, dst_data
;
4745 DirRef new_root_entry
;
4747 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
4751 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
4756 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
4757 dst_data
.clsid
= src_data
.clsid
;
4758 dst_data
.ctime
= src_data
.ctime
;
4759 dst_data
.mtime
= src_data
.mtime
;
4760 dst_data
.dirRootEntry
= new_root_entry
;
4764 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
4769 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
4773 ULARGE_INTEGER zero
;
4775 if (entry
== DIRENTRY_NULL
)
4780 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
4782 if (SUCCEEDED(hr
) && include_siblings
)
4783 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
4785 if (SUCCEEDED(hr
) && include_siblings
)
4786 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
4789 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
4791 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
4792 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
4795 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
4800 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4802 DirRef result
=This
->firstFreeEntry
;
4804 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4807 if (result
== This
->entries_size
)
4809 ULONG new_size
= This
->entries_size
* 2;
4810 TransactedDirEntry
*new_entries
;
4812 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4813 if (!new_entries
) return DIRENTRY_NULL
;
4815 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4816 HeapFree(GetProcessHeap(), 0, This
->entries
);
4818 This
->entries
= new_entries
;
4819 This
->entries_size
= new_size
;
4822 This
->entries
[result
].inuse
= TRUE
;
4824 This
->firstFreeEntry
= result
+1;
4829 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4830 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4832 DirRef stubEntryRef
;
4833 TransactedDirEntry
*entry
;
4835 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4837 if (stubEntryRef
!= DIRENTRY_NULL
)
4839 entry
= &This
->entries
[stubEntryRef
];
4841 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4843 entry
->read
= FALSE
;
4846 return stubEntryRef
;
4849 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4850 TransactedSnapshotImpl
*This
, DirRef entry
)
4855 if (!This
->entries
[entry
].read
)
4857 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4858 This
->entries
[entry
].transactedParentEntry
,
4861 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4863 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4865 if (data
.leftChild
== DIRENTRY_NULL
)
4869 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4871 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4873 if (data
.rightChild
== DIRENTRY_NULL
)
4877 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4879 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4881 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4887 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4888 This
->entries
[entry
].read
= TRUE
;
4895 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4896 TransactedSnapshotImpl
*This
, DirRef entry
)
4900 if (!This
->entries
[entry
].stream_dirty
)
4902 DirEntry new_entrydata
;
4904 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4905 new_entrydata
.name
[0] = 'S';
4906 new_entrydata
.sizeOfNameString
= 1;
4907 new_entrydata
.stgType
= STGTY_STREAM
;
4908 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4909 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4910 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4911 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4913 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4914 &This
->entries
[entry
].stream_entry
);
4916 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4918 hr
= StorageBaseImpl_CopyStream(
4919 This
->scratch
, This
->entries
[entry
].stream_entry
,
4920 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4923 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4927 This
->entries
[entry
].stream_dirty
= TRUE
;
4929 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4931 /* Since this entry is modified, and we aren't using its stream data, we
4932 * no longer care about the original entry. */
4934 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4936 if (delete_ref
!= DIRENTRY_NULL
)
4937 This
->entries
[delete_ref
].deleted
= TRUE
;
4939 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4946 /* Find the first entry in a depth-first traversal. */
4947 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4948 TransactedSnapshotImpl
* This
, DirRef parent
)
4950 DirRef cursor
, prev
;
4951 TransactedDirEntry
*entry
;
4954 entry
= &This
->entries
[cursor
];
4957 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4960 cursor
= entry
->data
.leftChild
;
4961 entry
= &This
->entries
[cursor
];
4962 entry
->parent
= prev
;
4964 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4967 cursor
= entry
->data
.rightChild
;
4968 entry
= &This
->entries
[cursor
];
4969 entry
->parent
= prev
;
4971 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4974 cursor
= entry
->data
.dirRootEntry
;
4975 entry
= &This
->entries
[cursor
];
4976 entry
->parent
= prev
;
4985 /* Find the next entry in a depth-first traversal. */
4986 static DirRef
TransactedSnapshotImpl_FindNextChild(
4987 TransactedSnapshotImpl
* This
, DirRef current
)
4990 TransactedDirEntry
*parent_entry
;
4992 parent
= This
->entries
[current
].parent
;
4993 parent_entry
= &This
->entries
[parent
];
4995 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4997 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4999 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5000 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5003 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5005 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5006 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5013 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5014 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5015 TransactedSnapshotImpl
* This
, DirRef entry
)
5017 return entry
!= DIRENTRY_NULL
&&
5018 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5021 /* Destroy the entries created by CopyTree. */
5022 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5023 TransactedSnapshotImpl
* This
, DirRef stop
)
5026 TransactedDirEntry
*entry
;
5027 ULARGE_INTEGER zero
;
5031 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5034 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5036 if (cursor
== DIRENTRY_NULL
)
5039 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5041 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5043 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5045 entry
= &This
->entries
[cursor
];
5047 if (entry
->stream_dirty
)
5048 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5049 entry
->newTransactedParentEntry
, zero
);
5051 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5052 entry
->newTransactedParentEntry
);
5054 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5057 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5061 /* Make a copy of our edited tree that we can use in the parent. */
5062 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5065 TransactedDirEntry
*entry
;
5068 cursor
= This
->base
.storageDirEntry
;
5069 entry
= &This
->entries
[cursor
];
5070 entry
->parent
= DIRENTRY_NULL
;
5071 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5073 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5076 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5078 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5079 entry
= &This
->entries
[cursor
];
5081 while (cursor
!= DIRENTRY_NULL
)
5083 /* Make a copy of this entry in the transacted parent. */
5085 (!entry
->dirty
&& !entry
->stream_dirty
&&
5086 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5087 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5088 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5089 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5094 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5096 newData
.size
.QuadPart
= 0;
5097 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5099 if (newData
.leftChild
!= DIRENTRY_NULL
)
5100 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5102 if (newData
.rightChild
!= DIRENTRY_NULL
)
5103 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5105 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5106 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5108 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5109 &entry
->newTransactedParentEntry
);
5112 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5116 if (entry
->stream_dirty
)
5118 hr
= StorageBaseImpl_CopyStream(
5119 This
->transactedParent
, entry
->newTransactedParentEntry
,
5120 This
->scratch
, entry
->stream_entry
);
5122 else if (entry
->data
.size
.QuadPart
)
5124 hr
= StorageBaseImpl_StreamLink(
5125 This
->transactedParent
, entry
->newTransactedParentEntry
,
5126 entry
->transactedParentEntry
);
5131 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5132 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5137 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5138 entry
= &This
->entries
[cursor
];
5144 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5146 DWORD grfCommitFlags
) /* [in] */
5148 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5149 TransactedDirEntry
*root_entry
;
5150 DirRef i
, dir_root_ref
;
5152 ULARGE_INTEGER zero
;
5154 ULONG transactionSig
;
5158 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5160 /* Cannot commit a read-only transacted storage */
5161 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5162 return STG_E_ACCESSDENIED
;
5164 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5165 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5168 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5171 if (transactionSig
!= This
->lastTransactionSig
)
5173 ERR("file was externally modified\n");
5174 hr
= STG_E_NOTCURRENT
;
5179 This
->lastTransactionSig
= transactionSig
+1;
5180 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5183 else if (hr
== E_NOTIMPL
)
5186 if (FAILED(hr
)) goto end
;
5188 /* To prevent data loss, we create the new structure in the file before we
5189 * delete the old one, so that in case of errors the old data is intact. We
5190 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5191 * needed in the rare situation where we have just enough free disk space to
5192 * overwrite the existing data. */
5194 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5196 if (!root_entry
->read
)
5199 hr
= TransactedSnapshotImpl_CopyTree(This
);
5200 if (FAILED(hr
)) goto end
;
5202 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5203 dir_root_ref
= DIRENTRY_NULL
;
5205 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5207 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5209 /* Update the storage to use the new data in one step. */
5211 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5212 root_entry
->transactedParentEntry
, &data
);
5216 data
.dirRootEntry
= dir_root_ref
;
5217 data
.clsid
= root_entry
->data
.clsid
;
5218 data
.ctime
= root_entry
->data
.ctime
;
5219 data
.mtime
= root_entry
->data
.mtime
;
5221 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5222 root_entry
->transactedParentEntry
, &data
);
5225 /* Try to flush after updating the root storage, but if the flush fails, keep
5226 * going, on the theory that it'll either succeed later or the subsequent
5227 * writes will fail. */
5228 StorageBaseImpl_Flush(This
->transactedParent
);
5232 /* Destroy the old now-orphaned data. */
5233 for (i
=0; i
<This
->entries_size
; i
++)
5235 TransactedDirEntry
*entry
= &This
->entries
[i
];
5240 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5241 entry
->transactedParentEntry
, zero
);
5242 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5243 entry
->transactedParentEntry
);
5244 memset(entry
, 0, sizeof(TransactedDirEntry
));
5245 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5247 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5249 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5250 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5251 entry
->transactedParentEntry
);
5252 if (entry
->stream_dirty
)
5254 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5255 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5256 entry
->stream_dirty
= FALSE
;
5258 entry
->dirty
= FALSE
;
5259 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5266 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5270 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5272 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5278 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
5281 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5282 ULARGE_INTEGER zero
;
5285 TRACE("(%p)\n", iface
);
5287 /* Destroy the open objects. */
5288 StorageBaseImpl_DeleteAll(&This
->base
);
5290 /* Clear out the scratch file. */
5292 for (i
=0; i
<This
->entries_size
; i
++)
5294 if (This
->entries
[i
].stream_dirty
)
5296 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
5299 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
5303 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
5305 This
->firstFreeEntry
= 0;
5306 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
5311 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
5313 if (!This
->reverted
)
5315 TRACE("Storage invalidated (stg=%p)\n", This
);
5317 This
->reverted
= TRUE
;
5319 StorageBaseImpl_DeleteAll(This
);
5323 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
5325 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5327 IStorage_Revert(&This
->base
.IStorage_iface
);
5328 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5329 IStorage_Release(&This
->scratch
->IStorage_iface
);
5330 HeapFree(GetProcessHeap(), 0, This
->entries
);
5331 HeapFree(GetProcessHeap(), 0, This
);
5334 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
5336 /* We only need to flush when committing. */
5340 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5342 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5344 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5347 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
5348 const DirEntry
*newData
, DirRef
*index
)
5350 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5352 TransactedDirEntry
*new_entry
;
5354 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
5355 if (new_ref
== DIRENTRY_NULL
)
5356 return E_OUTOFMEMORY
;
5358 new_entry
= &This
->entries
[new_ref
];
5360 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
5361 new_entry
->read
= TRUE
;
5362 new_entry
->dirty
= TRUE
;
5363 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
5367 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
5372 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
5373 DirRef index
, const DirEntry
*data
)
5375 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5378 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5380 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5381 if (FAILED(hr
)) return hr
;
5383 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
5385 if (index
!= This
->base
.storageDirEntry
)
5387 This
->entries
[index
].dirty
= TRUE
;
5389 if (data
->size
.QuadPart
== 0 &&
5390 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5392 /* Since this entry is modified, and we aren't using its stream data, we
5393 * no longer care about the original entry. */
5395 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5397 if (delete_ref
!= DIRENTRY_NULL
)
5398 This
->entries
[delete_ref
].deleted
= TRUE
;
5400 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5407 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
5408 DirRef index
, DirEntry
*data
)
5410 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5413 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5414 if (FAILED(hr
)) return hr
;
5416 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
5418 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5423 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5426 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5428 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
5429 This
->entries
[index
].data
.size
.QuadPart
!= 0)
5431 /* If we deleted this entry while it has stream data. We must have left the
5432 * data because some other entry is using it, and we need to leave the
5433 * original entry alone. */
5434 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
5435 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
5439 This
->entries
[index
].deleted
= TRUE
;
5445 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
5446 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5448 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5450 if (This
->entries
[index
].stream_dirty
)
5452 return StorageBaseImpl_StreamReadAt(This
->scratch
,
5453 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
5455 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
5457 /* This stream doesn't live in the parent, and we haven't allocated storage
5464 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
5465 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
5469 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
5470 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5472 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5475 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5476 if (FAILED(hr
)) return hr
;
5478 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5479 if (FAILED(hr
)) return hr
;
5481 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
5482 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5484 if (SUCCEEDED(hr
) && size
!= 0)
5485 This
->entries
[index
].data
.size
.QuadPart
= max(
5486 This
->entries
[index
].data
.size
.QuadPart
,
5487 offset
.QuadPart
+ size
);
5492 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5493 DirRef index
, ULARGE_INTEGER newsize
)
5495 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5498 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5499 if (FAILED(hr
)) return hr
;
5501 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5504 if (newsize
.QuadPart
== 0)
5506 /* Destroy any parent references or entries in the scratch file. */
5507 if (This
->entries
[index
].stream_dirty
)
5509 ULARGE_INTEGER zero
;
5511 StorageBaseImpl_StreamSetSize(This
->scratch
,
5512 This
->entries
[index
].stream_entry
, zero
);
5513 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5514 This
->entries
[index
].stream_entry
);
5515 This
->entries
[index
].stream_dirty
= FALSE
;
5517 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5520 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5522 if (delete_ref
!= DIRENTRY_NULL
)
5523 This
->entries
[delete_ref
].deleted
= TRUE
;
5525 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5530 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5531 if (FAILED(hr
)) return hr
;
5533 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5534 This
->entries
[index
].stream_entry
, newsize
);
5538 This
->entries
[index
].data
.size
= newsize
;
5543 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5544 DirRef dst
, DirRef src
)
5546 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5548 TransactedDirEntry
*dst_entry
, *src_entry
;
5550 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5551 if (FAILED(hr
)) return hr
;
5553 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5554 if (FAILED(hr
)) return hr
;
5556 dst_entry
= &This
->entries
[dst
];
5557 src_entry
= &This
->entries
[src
];
5559 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5560 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5561 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5562 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5563 dst_entry
->data
.size
= src_entry
->data
.size
;
5568 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
5569 ULONG
* result
, BOOL refresh
)
5574 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
5580 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5585 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5590 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5592 StorageBaseImpl_QueryInterface
,
5593 StorageBaseImpl_AddRef
,
5594 StorageBaseImpl_Release
,
5595 StorageBaseImpl_CreateStream
,
5596 StorageBaseImpl_OpenStream
,
5597 StorageBaseImpl_CreateStorage
,
5598 StorageBaseImpl_OpenStorage
,
5599 StorageBaseImpl_CopyTo
,
5600 StorageBaseImpl_MoveElementTo
,
5601 TransactedSnapshotImpl_Commit
,
5602 TransactedSnapshotImpl_Revert
,
5603 StorageBaseImpl_EnumElements
,
5604 StorageBaseImpl_DestroyElement
,
5605 StorageBaseImpl_RenameElement
,
5606 StorageBaseImpl_SetElementTimes
,
5607 StorageBaseImpl_SetClass
,
5608 StorageBaseImpl_SetStateBits
,
5609 StorageBaseImpl_Stat
5612 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5614 TransactedSnapshotImpl_Destroy
,
5615 TransactedSnapshotImpl_Invalidate
,
5616 TransactedSnapshotImpl_Flush
,
5617 TransactedSnapshotImpl_GetFilename
,
5618 TransactedSnapshotImpl_CreateDirEntry
,
5619 TransactedSnapshotImpl_WriteDirEntry
,
5620 TransactedSnapshotImpl_ReadDirEntry
,
5621 TransactedSnapshotImpl_DestroyDirEntry
,
5622 TransactedSnapshotImpl_StreamReadAt
,
5623 TransactedSnapshotImpl_StreamWriteAt
,
5624 TransactedSnapshotImpl_StreamSetSize
,
5625 TransactedSnapshotImpl_StreamLink
,
5626 TransactedSnapshotImpl_GetTransactionSig
,
5627 TransactedSnapshotImpl_SetTransactionSig
,
5628 TransactedSnapshotImpl_LockTransaction
,
5629 TransactedSnapshotImpl_UnlockTransaction
5632 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5633 TransactedSnapshotImpl
** result
)
5637 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5642 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5644 /* This is OK because the property set storage functions use the IStorage functions. */
5645 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5646 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5648 list_init(&(*result
)->base
.strmHead
);
5650 list_init(&(*result
)->base
.storageHead
);
5652 (*result
)->base
.ref
= 1;
5654 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5656 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5657 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5659 /* Create a new temporary storage to act as the scratch file. */
5660 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5662 (*result
)->scratch
= impl_from_IStorage(scratch
);
5666 ULONG num_entries
= 20;
5668 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5669 (*result
)->entries_size
= num_entries
;
5670 (*result
)->firstFreeEntry
= 0;
5672 if ((*result
)->entries
)
5674 /* parentStorage already has 1 reference, which we take over here. */
5675 (*result
)->transactedParent
= parentStorage
;
5677 parentStorage
->transactedChild
= &(*result
)->base
;
5679 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5683 IStorage_Release(scratch
);
5689 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5694 return E_OUTOFMEMORY
;
5697 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
5699 if (!This
->reverted
)
5701 TRACE("Storage invalidated (stg=%p)\n", This
);
5703 This
->reverted
= TRUE
;
5705 StorageBaseImpl_DeleteAll(This
);
5709 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
5711 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5713 TransactedSharedImpl_Invalidate(&This
->base
);
5714 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5715 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
5716 HeapFree(GetProcessHeap(), 0, This
);
5719 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
5721 /* We only need to flush when committing. */
5725 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5727 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5729 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5732 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
5733 const DirEntry
*newData
, DirRef
*index
)
5735 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5737 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
5741 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
5742 DirRef index
, const DirEntry
*data
)
5744 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5746 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
5750 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
5751 DirRef index
, DirEntry
*data
)
5753 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5755 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
5759 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5762 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5764 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
5768 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
5769 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5771 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5773 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
5774 index
, offset
, size
, buffer
, bytesRead
);
5777 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
5778 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5780 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5782 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
5783 index
, offset
, size
, buffer
, bytesWritten
);
5786 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
5787 DirRef index
, ULARGE_INTEGER newsize
)
5789 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5791 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
5795 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
5796 DirRef dst
, DirRef src
)
5798 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5800 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
5804 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
5805 ULONG
* result
, BOOL refresh
)
5810 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
5816 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5821 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5826 static HRESULT WINAPI
TransactedSharedImpl_Commit(
5828 DWORD grfCommitFlags
) /* [in] */
5830 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5831 DirRef new_storage_ref
, prev_storage_ref
;
5832 DirEntry src_data
, dst_data
;
5834 ULONG transactionSig
;
5836 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5838 /* Cannot commit a read-only transacted storage */
5839 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5840 return STG_E_ACCESSDENIED
;
5842 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5843 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5846 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5849 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
5850 hr
= STG_E_NOTCURRENT
;
5853 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
5855 else if (hr
== E_NOTIMPL
)
5859 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
5861 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5863 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
5866 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5869 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5873 prev_storage_ref
= dst_data
.dirRootEntry
;
5874 dst_data
.dirRootEntry
= new_storage_ref
;
5875 dst_data
.clsid
= src_data
.clsid
;
5876 dst_data
.ctime
= src_data
.ctime
;
5877 dst_data
.mtime
= src_data
.mtime
;
5878 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5883 /* Try to flush after updating the root storage, but if the flush fails, keep
5884 * going, on the theory that it'll either succeed later or the subsequent
5885 * writes will fail. */
5886 StorageBaseImpl_Flush(This
->transactedParent
);
5888 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
5892 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5894 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5897 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
5901 This
->lastTransactionSig
= transactionSig
+1;
5908 static HRESULT WINAPI
TransactedSharedImpl_Revert(
5911 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5913 TRACE("(%p)\n", iface
);
5915 /* Destroy the open objects. */
5916 StorageBaseImpl_DeleteAll(&This
->base
);
5918 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
5921 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
5923 StorageBaseImpl_QueryInterface
,
5924 StorageBaseImpl_AddRef
,
5925 StorageBaseImpl_Release
,
5926 StorageBaseImpl_CreateStream
,
5927 StorageBaseImpl_OpenStream
,
5928 StorageBaseImpl_CreateStorage
,
5929 StorageBaseImpl_OpenStorage
,
5930 StorageBaseImpl_CopyTo
,
5931 StorageBaseImpl_MoveElementTo
,
5932 TransactedSharedImpl_Commit
,
5933 TransactedSharedImpl_Revert
,
5934 StorageBaseImpl_EnumElements
,
5935 StorageBaseImpl_DestroyElement
,
5936 StorageBaseImpl_RenameElement
,
5937 StorageBaseImpl_SetElementTimes
,
5938 StorageBaseImpl_SetClass
,
5939 StorageBaseImpl_SetStateBits
,
5940 StorageBaseImpl_Stat
5943 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
5945 TransactedSharedImpl_Destroy
,
5946 TransactedSharedImpl_Invalidate
,
5947 TransactedSharedImpl_Flush
,
5948 TransactedSharedImpl_GetFilename
,
5949 TransactedSharedImpl_CreateDirEntry
,
5950 TransactedSharedImpl_WriteDirEntry
,
5951 TransactedSharedImpl_ReadDirEntry
,
5952 TransactedSharedImpl_DestroyDirEntry
,
5953 TransactedSharedImpl_StreamReadAt
,
5954 TransactedSharedImpl_StreamWriteAt
,
5955 TransactedSharedImpl_StreamSetSize
,
5956 TransactedSharedImpl_StreamLink
,
5957 TransactedSharedImpl_GetTransactionSig
,
5958 TransactedSharedImpl_SetTransactionSig
,
5959 TransactedSharedImpl_LockTransaction
,
5960 TransactedSharedImpl_UnlockTransaction
5963 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
5964 TransactedSharedImpl
** result
)
5968 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
5973 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
5975 /* This is OK because the property set storage functions use the IStorage functions. */
5976 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5977 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
5979 list_init(&(*result
)->base
.strmHead
);
5981 list_init(&(*result
)->base
.storageHead
);
5983 (*result
)->base
.ref
= 1;
5985 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5987 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
5993 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5994 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5998 stgo
.ulSectorSize
= 4096;
5999 stgo
.pwcsTemplateFile
= NULL
;
6001 /* Create a new temporary storage to act as the scratch file. */
6002 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6003 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6004 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6008 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6009 parentStorage
, parentStorage
->storageDirEntry
);
6013 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6015 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6016 (*result
)->transactedParent
= parentStorage
;
6020 IStorage_Release(scratch
);
6023 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6026 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6031 return E_OUTOFMEMORY
;
6034 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6035 BOOL toplevel
, StorageBaseImpl
** result
)
6037 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6039 if (parentStorage
->openFlags
& fixme_flags
)
6041 fixme_flags
&= ~parentStorage
->openFlags
;
6042 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6045 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6046 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6047 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6049 /* Need to create a temp file for the snapshot */
6050 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6053 return TransactedSnapshotImpl_Construct(parentStorage
,
6054 (TransactedSnapshotImpl
**)result
);
6057 static HRESULT
Storage_Construct(
6065 StorageBaseImpl
** result
)
6067 StorageImpl
*newStorage
;
6068 StorageBaseImpl
*newTransactedStorage
;
6071 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6072 if (FAILED(hr
)) goto end
;
6074 if (openFlags
& STGM_TRANSACTED
)
6076 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6078 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6080 *result
= newTransactedStorage
;
6083 *result
= &newStorage
->base
;
6089 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
6091 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6093 if (!This
->base
.reverted
)
6095 TRACE("Storage invalidated (stg=%p)\n", This
);
6097 This
->base
.reverted
= TRUE
;
6099 This
->parentStorage
= NULL
;
6101 StorageBaseImpl_DeleteAll(&This
->base
);
6103 list_remove(&This
->ParentListEntry
);
6107 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
6109 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6111 StorageInternalImpl_Invalidate(&This
->base
);
6113 HeapFree(GetProcessHeap(), 0, This
);
6116 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
6118 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6120 return StorageBaseImpl_Flush(This
->parentStorage
);
6123 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6125 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6127 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
6130 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
6131 const DirEntry
*newData
, DirRef
*index
)
6133 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6135 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
6139 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
6140 DirRef index
, const DirEntry
*data
)
6142 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6144 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
6148 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
6149 DirRef index
, DirEntry
*data
)
6151 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6153 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6157 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6160 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6162 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
6166 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
6167 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6169 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6171 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
6172 index
, offset
, size
, buffer
, bytesRead
);
6175 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
6176 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6178 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6180 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
6181 index
, offset
, size
, buffer
, bytesWritten
);
6184 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
6185 DirRef index
, ULARGE_INTEGER newsize
)
6187 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6189 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
6193 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
6194 DirRef dst
, DirRef src
)
6196 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6198 return StorageBaseImpl_StreamLink(This
->parentStorage
,
6202 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
6203 ULONG
* result
, BOOL refresh
)
6208 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
6214 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6219 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6224 /******************************************************************************
6226 ** Storage32InternalImpl_Commit
6229 static HRESULT WINAPI
StorageInternalImpl_Commit(
6231 DWORD grfCommitFlags
) /* [in] */
6233 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
6234 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6235 return StorageBaseImpl_Flush(This
);
6238 /******************************************************************************
6240 ** Storage32InternalImpl_Revert
6243 static HRESULT WINAPI
StorageInternalImpl_Revert(
6246 FIXME("(%p): stub\n", iface
);
6250 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
6252 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
6253 HeapFree(GetProcessHeap(), 0, This
);
6256 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
6257 IEnumSTATSTG
* iface
,
6261 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6264 return E_INVALIDARG
;
6268 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
6269 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
6272 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
6276 return E_NOINTERFACE
;
6279 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
6280 IEnumSTATSTG
* iface
)
6282 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6283 return InterlockedIncrement(&This
->ref
);
6286 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
6287 IEnumSTATSTG
* iface
)
6289 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6293 newRef
= InterlockedDecrement(&This
->ref
);
6297 IEnumSTATSTGImpl_Destroy(This
);
6303 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
6304 IEnumSTATSTGImpl
* This
,
6307 DirRef result
= DIRENTRY_NULL
;
6311 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
6313 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6314 This
->parentStorage
->storageDirEntry
, &entry
);
6315 searchNode
= entry
.dirRootEntry
;
6317 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
6319 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
6323 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
6327 searchNode
= entry
.rightChild
;
6331 result
= searchNode
;
6332 memcpy(result_name
, entry
.name
, sizeof(result_name
));
6333 searchNode
= entry
.leftChild
;
6341 if (result
!= DIRENTRY_NULL
)
6342 memcpy(This
->name
, result_name
, sizeof(result_name
));
6348 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
6349 IEnumSTATSTG
* iface
,
6352 ULONG
* pceltFetched
)
6354 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6356 DirEntry currentEntry
;
6357 STATSTG
* currentReturnStruct
= rgelt
;
6358 ULONG objectFetched
= 0;
6359 DirRef currentSearchNode
;
6362 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
6363 return E_INVALIDARG
;
6365 if (This
->parentStorage
->reverted
)
6366 return STG_E_REVERTED
;
6369 * To avoid the special case, get another pointer to a ULONG value if
6370 * the caller didn't supply one.
6372 if (pceltFetched
==0)
6373 pceltFetched
= &objectFetched
;
6376 * Start the iteration, we will iterate until we hit the end of the
6377 * linked list or until we hit the number of items to iterate through
6381 while ( *pceltFetched
< celt
)
6383 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6385 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6389 * Read the entry from the storage.
6391 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6396 * Copy the information to the return buffer.
6398 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
6399 currentReturnStruct
,
6404 * Step to the next item in the iteration
6407 currentReturnStruct
++;
6410 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
6417 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
6418 IEnumSTATSTG
* iface
,
6421 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6423 ULONG objectFetched
= 0;
6424 DirRef currentSearchNode
;
6427 if (This
->parentStorage
->reverted
)
6428 return STG_E_REVERTED
;
6430 while ( (objectFetched
< celt
) )
6432 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6434 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6440 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
6446 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
6447 IEnumSTATSTG
* iface
)
6449 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6451 if (This
->parentStorage
->reverted
)
6452 return STG_E_REVERTED
;
6459 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
6460 IEnumSTATSTG
* iface
,
6461 IEnumSTATSTG
** ppenum
)
6463 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6464 IEnumSTATSTGImpl
* newClone
;
6466 if (This
->parentStorage
->reverted
)
6467 return STG_E_REVERTED
;
6470 return E_INVALIDARG
;
6472 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
6473 This
->storageDirEntry
);
6477 return E_OUTOFMEMORY
;
6481 * The new clone enumeration must point to the same current node as
6484 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
6486 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
6492 * Virtual function table for the IEnumSTATSTGImpl class.
6494 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
6496 IEnumSTATSTGImpl_QueryInterface
,
6497 IEnumSTATSTGImpl_AddRef
,
6498 IEnumSTATSTGImpl_Release
,
6499 IEnumSTATSTGImpl_Next
,
6500 IEnumSTATSTGImpl_Skip
,
6501 IEnumSTATSTGImpl_Reset
,
6502 IEnumSTATSTGImpl_Clone
6505 /******************************************************************************
6506 ** IEnumSTATSTGImpl implementation
6509 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
6510 StorageBaseImpl
* parentStorage
,
6511 DirRef storageDirEntry
)
6513 IEnumSTATSTGImpl
* newEnumeration
;
6515 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
6519 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
6520 newEnumeration
->ref
= 1;
6521 newEnumeration
->name
[0] = 0;
6524 * We want to nail-down the reference to the storage in case the
6525 * enumeration out-lives the storage in the client application.
6527 newEnumeration
->parentStorage
= parentStorage
;
6528 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
6530 newEnumeration
->storageDirEntry
= storageDirEntry
;
6533 return newEnumeration
;
6537 * Virtual function table for the Storage32InternalImpl class.
6539 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
6541 StorageBaseImpl_QueryInterface
,
6542 StorageBaseImpl_AddRef
,
6543 StorageBaseImpl_Release
,
6544 StorageBaseImpl_CreateStream
,
6545 StorageBaseImpl_OpenStream
,
6546 StorageBaseImpl_CreateStorage
,
6547 StorageBaseImpl_OpenStorage
,
6548 StorageBaseImpl_CopyTo
,
6549 StorageBaseImpl_MoveElementTo
,
6550 StorageInternalImpl_Commit
,
6551 StorageInternalImpl_Revert
,
6552 StorageBaseImpl_EnumElements
,
6553 StorageBaseImpl_DestroyElement
,
6554 StorageBaseImpl_RenameElement
,
6555 StorageBaseImpl_SetElementTimes
,
6556 StorageBaseImpl_SetClass
,
6557 StorageBaseImpl_SetStateBits
,
6558 StorageBaseImpl_Stat
6561 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
6563 StorageInternalImpl_Destroy
,
6564 StorageInternalImpl_Invalidate
,
6565 StorageInternalImpl_Flush
,
6566 StorageInternalImpl_GetFilename
,
6567 StorageInternalImpl_CreateDirEntry
,
6568 StorageInternalImpl_WriteDirEntry
,
6569 StorageInternalImpl_ReadDirEntry
,
6570 StorageInternalImpl_DestroyDirEntry
,
6571 StorageInternalImpl_StreamReadAt
,
6572 StorageInternalImpl_StreamWriteAt
,
6573 StorageInternalImpl_StreamSetSize
,
6574 StorageInternalImpl_StreamLink
,
6575 StorageInternalImpl_GetTransactionSig
,
6576 StorageInternalImpl_SetTransactionSig
,
6577 StorageInternalImpl_LockTransaction
,
6578 StorageInternalImpl_UnlockTransaction
6581 /******************************************************************************
6582 ** Storage32InternalImpl implementation
6585 static StorageInternalImpl
* StorageInternalImpl_Construct(
6586 StorageBaseImpl
* parentStorage
,
6588 DirRef storageDirEntry
)
6590 StorageInternalImpl
* newStorage
;
6592 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
6596 list_init(&newStorage
->base
.strmHead
);
6598 list_init(&newStorage
->base
.storageHead
);
6601 * Initialize the virtual function table.
6603 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
6604 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
6605 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
6606 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
6608 newStorage
->base
.reverted
= FALSE
;
6610 newStorage
->base
.ref
= 1;
6612 newStorage
->parentStorage
= parentStorage
;
6615 * Keep a reference to the directory entry of this storage
6617 newStorage
->base
.storageDirEntry
= storageDirEntry
;
6619 newStorage
->base
.create
= FALSE
;
6627 /******************************************************************************
6628 ** StorageUtl implementation
6631 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6635 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6636 *value
= lendian16toh(tmp
);
6639 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6641 value
= htole16(value
);
6642 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6645 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6649 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6650 *value
= lendian32toh(tmp
);
6653 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6655 value
= htole32(value
);
6656 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6659 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6660 ULARGE_INTEGER
* value
)
6662 #ifdef WORDS_BIGENDIAN
6665 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6666 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6667 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6669 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6673 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6674 const ULARGE_INTEGER
*value
)
6676 #ifdef WORDS_BIGENDIAN
6679 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6680 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6681 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6683 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6687 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6689 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6690 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6691 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6693 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6696 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6698 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6699 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6700 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6702 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6705 void StorageUtl_CopyDirEntryToSTATSTG(
6706 StorageBaseImpl
* storage
,
6707 STATSTG
* destination
,
6708 const DirEntry
* source
,
6712 * The copy of the string occurs only when the flag is not set
6714 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6716 /* Use the filename for the root storage. */
6717 destination
->pwcsName
= 0;
6718 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6720 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6721 (source
->name
[0] == 0) )
6723 destination
->pwcsName
= 0;
6727 destination
->pwcsName
=
6728 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6730 strcpyW(destination
->pwcsName
, source
->name
);
6733 switch (source
->stgType
)
6737 destination
->type
= STGTY_STORAGE
;
6740 destination
->type
= STGTY_STREAM
;
6743 destination
->type
= STGTY_STREAM
;
6747 destination
->cbSize
= source
->size
;
6749 currentReturnStruct->mtime = {0}; TODO
6750 currentReturnStruct->ctime = {0};
6751 currentReturnStruct->atime = {0};
6753 destination
->grfMode
= 0;
6754 destination
->grfLocksSupported
= 0;
6755 destination
->clsid
= source
->clsid
;
6756 destination
->grfStateBits
= 0;
6757 destination
->reserved
= 0;
6760 /******************************************************************************
6761 ** BlockChainStream implementation
6764 /* Read and save the index of all blocks in this stream. */
6765 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6767 ULONG next_sector
, next_offset
;
6769 struct BlockChainRun
*last_run
;
6771 if (This
->indexCacheLen
== 0)
6775 next_sector
= BlockChainStream_GetHeadOfChain(This
);
6779 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6780 next_offset
= last_run
->lastOffset
+1;
6781 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
6782 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
6784 if (FAILED(hr
)) return hr
;
6787 while (next_sector
!= BLOCK_END_OF_CHAIN
)
6789 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
6791 /* Add the current block to the cache. */
6792 if (This
->indexCacheSize
== 0)
6794 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
6795 if (!This
->indexCache
) return E_OUTOFMEMORY
;
6796 This
->indexCacheSize
= 16;
6798 else if (This
->indexCacheSize
== This
->indexCacheLen
)
6800 struct BlockChainRun
*new_cache
;
6803 new_size
= This
->indexCacheSize
* 2;
6804 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
6805 if (!new_cache
) return E_OUTOFMEMORY
;
6806 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
6808 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6809 This
->indexCache
= new_cache
;
6810 This
->indexCacheSize
= new_size
;
6813 This
->indexCacheLen
++;
6814 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6815 last_run
->firstSector
= next_sector
;
6816 last_run
->firstOffset
= next_offset
;
6819 last_run
->lastOffset
= next_offset
;
6821 /* Find the next block. */
6823 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
6824 if (FAILED(hr
)) return hr
;
6827 if (This
->indexCacheLen
)
6829 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
6830 This
->numBlocks
= last_run
->lastOffset
+1;
6834 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6835 This
->numBlocks
= 0;
6841 /* Locate the nth block in this stream. */
6842 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
6844 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
6845 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
6847 if (offset
>= This
->numBlocks
)
6848 return BLOCK_END_OF_CHAIN
;
6850 while (min_run
< max_run
)
6852 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
6853 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
6855 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
6856 max_run
= run_to_check
-1;
6858 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
6860 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
6861 min_run
= run_to_check
+1;
6864 /* Block is in this run. */
6865 min_run
= max_run
= run_to_check
;
6868 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
6871 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
6872 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
6874 BlockChainBlock
*result
=NULL
;
6878 if (This
->cachedBlocks
[i
].index
== index
)
6880 *sector
= This
->cachedBlocks
[i
].sector
;
6881 *block
= &This
->cachedBlocks
[i
];
6885 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6886 if (*sector
== BLOCK_END_OF_CHAIN
)
6887 return STG_E_DOCFILECORRUPT
;
6891 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6892 result
= &This
->cachedBlocks
[0];
6893 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6894 result
= &This
->cachedBlocks
[1];
6897 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6898 if (This
->blockToEvict
== 2)
6899 This
->blockToEvict
= 0;
6904 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6905 return STG_E_WRITEFAULT
;
6906 result
->dirty
= FALSE
;
6909 result
->read
= FALSE
;
6910 result
->index
= index
;
6911 result
->sector
= *sector
;
6918 BlockChainStream
* BlockChainStream_Construct(
6919 StorageImpl
* parentStorage
,
6920 ULONG
* headOfStreamPlaceHolder
,
6923 BlockChainStream
* newStream
;
6925 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6927 newStream
->parentStorage
= parentStorage
;
6928 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6929 newStream
->ownerDirEntry
= dirEntry
;
6930 newStream
->indexCache
= NULL
;
6931 newStream
->indexCacheLen
= 0;
6932 newStream
->indexCacheSize
= 0;
6933 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6934 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6935 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6936 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6937 newStream
->blockToEvict
= 0;
6939 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6941 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6942 HeapFree(GetProcessHeap(), 0, newStream
);
6949 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6952 if (!This
) return S_OK
;
6955 if (This
->cachedBlocks
[i
].dirty
)
6957 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6958 This
->cachedBlocks
[i
].dirty
= FALSE
;
6960 return STG_E_WRITEFAULT
;
6966 void BlockChainStream_Destroy(BlockChainStream
* This
)
6970 BlockChainStream_Flush(This
);
6971 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6973 HeapFree(GetProcessHeap(), 0, This
);
6976 /******************************************************************************
6977 * BlockChainStream_GetHeadOfChain
6979 * Returns the head of this stream chain.
6980 * Some special chains don't have directory entries, their heads are kept in
6981 * This->headOfStreamPlaceHolder.
6984 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6986 DirEntry chainEntry
;
6989 if (This
->headOfStreamPlaceHolder
!= 0)
6990 return *(This
->headOfStreamPlaceHolder
);
6992 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6994 hr
= StorageImpl_ReadDirEntry(
6995 This
->parentStorage
,
6996 This
->ownerDirEntry
,
7001 return chainEntry
.startingBlock
;
7005 return BLOCK_END_OF_CHAIN
;
7008 /******************************************************************************
7009 * BlockChainStream_GetCount
7011 * Returns the number of blocks that comprises this chain.
7012 * This is not the size of the stream as the last block may not be full!
7014 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
7016 return This
->numBlocks
;
7019 /******************************************************************************
7020 * BlockChainStream_ReadAt
7022 * Reads a specified number of bytes from this chain at the specified offset.
7023 * bytesRead may be NULL.
7024 * Failure will be returned if the specified number of bytes has not been read.
7026 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7027 ULARGE_INTEGER offset
,
7032 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7033 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
7034 ULONG bytesToReadInBuffer
;
7037 ULARGE_INTEGER stream_size
;
7039 BlockChainBlock
*cachedBlock
;
7041 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7044 * Find the first block in the stream that contains part of the buffer.
7046 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7050 stream_size
= BlockChainStream_GetSize(This
);
7051 if (stream_size
.QuadPart
> offset
.QuadPart
)
7052 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7057 * Start reading the buffer.
7059 bufferWalker
= buffer
;
7063 ULARGE_INTEGER ulOffset
;
7067 * Calculate how many bytes we can copy from this big block.
7069 bytesToReadInBuffer
=
7070 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7072 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7079 /* Not in cache, and we're going to read past the end of the block. */
7080 ulOffset
.u
.HighPart
= 0;
7081 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7084 StorageImpl_ReadAt(This
->parentStorage
,
7087 bytesToReadInBuffer
,
7092 if (!cachedBlock
->read
)
7095 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7096 return STG_E_READFAULT
;
7098 cachedBlock
->read
= TRUE
;
7101 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7102 bytesReadAt
= bytesToReadInBuffer
;
7105 blockNoInSequence
++;
7106 bufferWalker
+= bytesReadAt
;
7107 size
-= bytesReadAt
;
7108 *bytesRead
+= bytesReadAt
;
7109 offsetInBlock
= 0; /* There is no offset on the next block */
7111 if (bytesToReadInBuffer
!= bytesReadAt
)
7118 /******************************************************************************
7119 * BlockChainStream_WriteAt
7121 * Writes the specified number of bytes to this chain at the specified offset.
7122 * Will fail if not all specified number of bytes have been written.
7124 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7125 ULARGE_INTEGER offset
,
7128 ULONG
* bytesWritten
)
7130 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7131 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
7134 const BYTE
* bufferWalker
;
7136 BlockChainBlock
*cachedBlock
;
7139 bufferWalker
= buffer
;
7143 ULARGE_INTEGER ulOffset
;
7144 DWORD bytesWrittenAt
;
7147 * Calculate how many bytes we can copy to this big block.
7150 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7152 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7154 /* BlockChainStream_SetSize should have already been called to ensure we have
7155 * enough blocks in the chain to write into */
7158 ERR("not enough blocks in chain to write data\n");
7164 /* Not in cache, and we're going to write past the end of the block. */
7165 ulOffset
.u
.HighPart
= 0;
7166 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7169 StorageImpl_WriteAt(This
->parentStorage
,
7177 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7180 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7181 return STG_E_READFAULT
;
7184 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7185 bytesWrittenAt
= bytesToWrite
;
7186 cachedBlock
->read
= TRUE
;
7187 cachedBlock
->dirty
= TRUE
;
7190 blockNoInSequence
++;
7191 bufferWalker
+= bytesWrittenAt
;
7192 size
-= bytesWrittenAt
;
7193 *bytesWritten
+= bytesWrittenAt
;
7194 offsetInBlock
= 0; /* There is no offset on the next block */
7196 if (bytesWrittenAt
!= bytesToWrite
)
7200 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7203 /******************************************************************************
7204 * BlockChainStream_Shrink
7206 * Shrinks this chain in the big block depot.
7208 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7209 ULARGE_INTEGER newSize
)
7216 * Figure out how many blocks are needed to contain the new size
7218 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7220 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
7226 * Go to the new end of chain
7228 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7230 /* Mark the new end of chain */
7231 StorageImpl_SetNextBlockInChain(
7232 This
->parentStorage
,
7234 BLOCK_END_OF_CHAIN
);
7236 This
->tailIndex
= blockIndex
;
7240 if (This
->headOfStreamPlaceHolder
!= 0)
7242 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7246 DirEntry chainEntry
;
7247 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7249 StorageImpl_ReadDirEntry(
7250 This
->parentStorage
,
7251 This
->ownerDirEntry
,
7254 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7256 StorageImpl_WriteDirEntry(
7257 This
->parentStorage
,
7258 This
->ownerDirEntry
,
7262 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7265 This
->numBlocks
= numBlocks
;
7268 * Mark the extra blocks as free
7270 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7272 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7273 StorageImpl_FreeBigBlock(This
->parentStorage
,
7274 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7275 if (last_run
->lastOffset
== last_run
->firstOffset
)
7276 This
->indexCacheLen
--;
7278 last_run
->lastOffset
--;
7282 * Reset the last accessed block cache.
7286 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7288 This
->cachedBlocks
[i
].index
= 0xffffffff;
7289 This
->cachedBlocks
[i
].dirty
= FALSE
;
7296 /******************************************************************************
7297 * BlockChainStream_Enlarge
7299 * Grows this chain in the big block depot.
7301 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7302 ULARGE_INTEGER newSize
)
7304 ULONG blockIndex
, currentBlock
;
7306 ULONG oldNumBlocks
= 0;
7308 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7311 * Empty chain. Create the head.
7313 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7315 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7316 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7318 BLOCK_END_OF_CHAIN
);
7320 if (This
->headOfStreamPlaceHolder
!= 0)
7322 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7326 DirEntry chainEntry
;
7327 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7329 StorageImpl_ReadDirEntry(
7330 This
->parentStorage
,
7331 This
->ownerDirEntry
,
7334 chainEntry
.startingBlock
= blockIndex
;
7336 StorageImpl_WriteDirEntry(
7337 This
->parentStorage
,
7338 This
->ownerDirEntry
,
7342 This
->tailIndex
= blockIndex
;
7343 This
->numBlocks
= 1;
7347 * Figure out how many blocks are needed to contain this stream
7349 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7351 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
7355 * Go to the current end of chain
7357 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7359 currentBlock
= blockIndex
;
7361 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7364 currentBlock
= blockIndex
;
7366 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7371 This
->tailIndex
= currentBlock
;
7374 currentBlock
= This
->tailIndex
;
7375 oldNumBlocks
= This
->numBlocks
;
7378 * Add new blocks to the chain
7380 if (oldNumBlocks
< newNumBlocks
)
7382 while (oldNumBlocks
< newNumBlocks
)
7384 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7386 StorageImpl_SetNextBlockInChain(
7387 This
->parentStorage
,
7391 StorageImpl_SetNextBlockInChain(
7392 This
->parentStorage
,
7394 BLOCK_END_OF_CHAIN
);
7396 currentBlock
= blockIndex
;
7400 This
->tailIndex
= blockIndex
;
7401 This
->numBlocks
= newNumBlocks
;
7404 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7410 /******************************************************************************
7411 * BlockChainStream_SetSize
7413 * Sets the size of this stream. The big block depot will be updated.
7414 * The file will grow if we grow the chain.
7416 * TODO: Free the actual blocks in the file when we shrink the chain.
7417 * Currently, the blocks are still in the file. So the file size
7418 * doesn't shrink even if we shrink streams.
7420 BOOL
BlockChainStream_SetSize(
7421 BlockChainStream
* This
,
7422 ULARGE_INTEGER newSize
)
7424 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7426 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7429 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7431 BlockChainStream_Shrink(This
, newSize
);
7435 BlockChainStream_Enlarge(This
, newSize
);
7441 /******************************************************************************
7442 * BlockChainStream_GetSize
7444 * Returns the size of this chain.
7445 * Will return the block count if this chain doesn't have a directory entry.
7447 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7449 DirEntry chainEntry
;
7451 if(This
->headOfStreamPlaceHolder
== NULL
)
7454 * This chain has a directory entry so use the size value from there.
7456 StorageImpl_ReadDirEntry(
7457 This
->parentStorage
,
7458 This
->ownerDirEntry
,
7461 return chainEntry
.size
;
7466 * this chain is a chain that does not have a directory entry, figure out the
7467 * size by making the product number of used blocks times the
7470 ULARGE_INTEGER result
;
7471 result
.u
.HighPart
= 0;
7474 BlockChainStream_GetCount(This
) *
7475 This
->parentStorage
->bigBlockSize
;
7481 /******************************************************************************
7482 ** SmallBlockChainStream implementation
7485 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7486 StorageImpl
* parentStorage
,
7487 ULONG
* headOfStreamPlaceHolder
,
7490 SmallBlockChainStream
* newStream
;
7492 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7494 newStream
->parentStorage
= parentStorage
;
7495 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7496 newStream
->ownerDirEntry
= dirEntry
;
7501 void SmallBlockChainStream_Destroy(
7502 SmallBlockChainStream
* This
)
7504 HeapFree(GetProcessHeap(), 0, This
);
7507 /******************************************************************************
7508 * SmallBlockChainStream_GetHeadOfChain
7510 * Returns the head of this chain of small blocks.
7512 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7513 SmallBlockChainStream
* This
)
7515 DirEntry chainEntry
;
7518 if (This
->headOfStreamPlaceHolder
!= NULL
)
7519 return *(This
->headOfStreamPlaceHolder
);
7521 if (This
->ownerDirEntry
)
7523 hr
= StorageImpl_ReadDirEntry(
7524 This
->parentStorage
,
7525 This
->ownerDirEntry
,
7530 return chainEntry
.startingBlock
;
7535 return BLOCK_END_OF_CHAIN
;
7538 /******************************************************************************
7539 * SmallBlockChainStream_GetNextBlockInChain
7541 * Returns the index of the next small block in this chain.
7544 * - BLOCK_END_OF_CHAIN: end of this chain
7545 * - BLOCK_UNUSED: small block 'blockIndex' is free
7547 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7548 SmallBlockChainStream
* This
,
7550 ULONG
* nextBlockInChain
)
7552 ULARGE_INTEGER offsetOfBlockInDepot
;
7557 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7559 offsetOfBlockInDepot
.u
.HighPart
= 0;
7560 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7563 * Read those bytes in the buffer from the small block file.
7565 res
= BlockChainStream_ReadAt(
7566 This
->parentStorage
->smallBlockDepotChain
,
7567 offsetOfBlockInDepot
,
7572 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7573 res
= STG_E_READFAULT
;
7577 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7584 /******************************************************************************
7585 * SmallBlockChainStream_SetNextBlockInChain
7587 * Writes the index of the next block of the specified block in the small
7589 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7590 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7592 static void SmallBlockChainStream_SetNextBlockInChain(
7593 SmallBlockChainStream
* This
,
7597 ULARGE_INTEGER offsetOfBlockInDepot
;
7601 offsetOfBlockInDepot
.u
.HighPart
= 0;
7602 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7604 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7607 * Read those bytes in the buffer from the small block file.
7609 BlockChainStream_WriteAt(
7610 This
->parentStorage
->smallBlockDepotChain
,
7611 offsetOfBlockInDepot
,
7617 /******************************************************************************
7618 * SmallBlockChainStream_FreeBlock
7620 * Flag small block 'blockIndex' as free in the small block depot.
7622 static void SmallBlockChainStream_FreeBlock(
7623 SmallBlockChainStream
* This
,
7626 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7629 /******************************************************************************
7630 * SmallBlockChainStream_GetNextFreeBlock
7632 * Returns the index of a free small block. The small block depot will be
7633 * enlarged if necessary. The small block chain will also be enlarged if
7636 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7637 SmallBlockChainStream
* This
)
7639 ULARGE_INTEGER offsetOfBlockInDepot
;
7642 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7643 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7645 ULONG smallBlocksPerBigBlock
;
7647 ULONG blocksRequired
;
7648 ULARGE_INTEGER old_size
, size_required
;
7650 offsetOfBlockInDepot
.u
.HighPart
= 0;
7653 * Scan the small block depot for a free block
7655 while (nextBlockIndex
!= BLOCK_UNUSED
)
7657 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7659 res
= BlockChainStream_ReadAt(
7660 This
->parentStorage
->smallBlockDepotChain
,
7661 offsetOfBlockInDepot
,
7667 * If we run out of space for the small block depot, enlarge it
7669 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7671 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7673 if (nextBlockIndex
!= BLOCK_UNUSED
)
7679 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7681 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7682 ULARGE_INTEGER newSize
, offset
;
7685 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
7686 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7689 * Initialize all the small blocks to free
7691 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7692 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
7693 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7694 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7696 StorageImpl_SaveFileHeader(This
->parentStorage
);
7700 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7702 smallBlocksPerBigBlock
=
7703 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7706 * Verify if we have to allocate big blocks to contain small blocks
7708 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7710 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
7712 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7714 if (size_required
.QuadPart
> old_size
.QuadPart
)
7716 BlockChainStream_SetSize(
7717 This
->parentStorage
->smallBlockRootChain
,
7720 StorageImpl_ReadDirEntry(
7721 This
->parentStorage
,
7722 This
->parentStorage
->base
.storageDirEntry
,
7725 rootEntry
.size
= size_required
;
7727 StorageImpl_WriteDirEntry(
7728 This
->parentStorage
,
7729 This
->parentStorage
->base
.storageDirEntry
,
7736 /******************************************************************************
7737 * SmallBlockChainStream_ReadAt
7739 * Reads a specified number of bytes from this chain at the specified offset.
7740 * bytesRead may be NULL.
7741 * Failure will be returned if the specified number of bytes has not been read.
7743 HRESULT
SmallBlockChainStream_ReadAt(
7744 SmallBlockChainStream
* This
,
7745 ULARGE_INTEGER offset
,
7751 ULARGE_INTEGER offsetInBigBlockFile
;
7752 ULONG blockNoInSequence
=
7753 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7755 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7756 ULONG bytesToReadInBuffer
;
7758 ULONG bytesReadFromBigBlockFile
;
7760 ULARGE_INTEGER stream_size
;
7763 * This should never happen on a small block file.
7765 assert(offset
.u
.HighPart
==0);
7769 stream_size
= SmallBlockChainStream_GetSize(This
);
7770 if (stream_size
.QuadPart
> offset
.QuadPart
)
7771 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7776 * Find the first block in the stream that contains part of the buffer.
7778 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7780 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7782 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7785 blockNoInSequence
--;
7789 * Start reading the buffer.
7791 bufferWalker
= buffer
;
7793 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7796 * Calculate how many bytes we can copy from this small block.
7798 bytesToReadInBuffer
=
7799 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7802 * Calculate the offset of the small block in the small block file.
7804 offsetInBigBlockFile
.u
.HighPart
= 0;
7805 offsetInBigBlockFile
.u
.LowPart
=
7806 blockIndex
* This
->parentStorage
->smallBlockSize
;
7808 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
7811 * Read those bytes in the buffer from the small block file.
7812 * The small block has already been identified so it shouldn't fail
7813 * unless the file is corrupt.
7815 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7816 offsetInBigBlockFile
,
7817 bytesToReadInBuffer
,
7819 &bytesReadFromBigBlockFile
);
7824 if (!bytesReadFromBigBlockFile
)
7825 return STG_E_DOCFILECORRUPT
;
7828 * Step to the next big block.
7830 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7832 return STG_E_DOCFILECORRUPT
;
7834 bufferWalker
+= bytesReadFromBigBlockFile
;
7835 size
-= bytesReadFromBigBlockFile
;
7836 *bytesRead
+= bytesReadFromBigBlockFile
;
7837 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7843 /******************************************************************************
7844 * SmallBlockChainStream_WriteAt
7846 * Writes the specified number of bytes to this chain at the specified offset.
7847 * Will fail if not all specified number of bytes have been written.
7849 HRESULT
SmallBlockChainStream_WriteAt(
7850 SmallBlockChainStream
* This
,
7851 ULARGE_INTEGER offset
,
7854 ULONG
* bytesWritten
)
7856 ULARGE_INTEGER offsetInBigBlockFile
;
7857 ULONG blockNoInSequence
=
7858 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7860 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7861 ULONG bytesToWriteInBuffer
;
7863 ULONG bytesWrittenToBigBlockFile
;
7864 const BYTE
* bufferWalker
;
7868 * This should never happen on a small block file.
7870 assert(offset
.u
.HighPart
==0);
7873 * Find the first block in the stream that contains part of the buffer.
7875 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7877 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7879 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7880 return STG_E_DOCFILECORRUPT
;
7881 blockNoInSequence
--;
7885 * Start writing the buffer.
7888 bufferWalker
= buffer
;
7889 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7892 * Calculate how many bytes we can copy to this small block.
7894 bytesToWriteInBuffer
=
7895 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7898 * Calculate the offset of the small block in the small block file.
7900 offsetInBigBlockFile
.u
.HighPart
= 0;
7901 offsetInBigBlockFile
.u
.LowPart
=
7902 blockIndex
* This
->parentStorage
->smallBlockSize
;
7904 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
7907 * Write those bytes in the buffer to the small block file.
7909 res
= BlockChainStream_WriteAt(
7910 This
->parentStorage
->smallBlockRootChain
,
7911 offsetInBigBlockFile
,
7912 bytesToWriteInBuffer
,
7914 &bytesWrittenToBigBlockFile
);
7919 * Step to the next big block.
7921 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7924 bufferWalker
+= bytesWrittenToBigBlockFile
;
7925 size
-= bytesWrittenToBigBlockFile
;
7926 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7927 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7930 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7933 /******************************************************************************
7934 * SmallBlockChainStream_Shrink
7936 * Shrinks this chain in the small block depot.
7938 static BOOL
SmallBlockChainStream_Shrink(
7939 SmallBlockChainStream
* This
,
7940 ULARGE_INTEGER newSize
)
7942 ULONG blockIndex
, extraBlock
;
7946 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7948 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7951 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7954 * Go to the new end of chain
7956 while (count
< numBlocks
)
7958 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7965 * If the count is 0, we have a special case, the head of the chain was
7970 DirEntry chainEntry
;
7972 StorageImpl_ReadDirEntry(This
->parentStorage
,
7973 This
->ownerDirEntry
,
7976 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7978 StorageImpl_WriteDirEntry(This
->parentStorage
,
7979 This
->ownerDirEntry
,
7983 * We start freeing the chain at the head block.
7985 extraBlock
= blockIndex
;
7989 /* Get the next block before marking the new end */
7990 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7994 /* Mark the new end of chain */
7995 SmallBlockChainStream_SetNextBlockInChain(
7998 BLOCK_END_OF_CHAIN
);
8002 * Mark the extra blocks as free
8004 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8006 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8009 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8010 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8011 extraBlock
= blockIndex
;
8017 /******************************************************************************
8018 * SmallBlockChainStream_Enlarge
8020 * Grows this chain in the small block depot.
8022 static BOOL
SmallBlockChainStream_Enlarge(
8023 SmallBlockChainStream
* This
,
8024 ULARGE_INTEGER newSize
)
8026 ULONG blockIndex
, currentBlock
;
8028 ULONG oldNumBlocks
= 0;
8030 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8033 * Empty chain. Create the head.
8035 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8037 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8038 SmallBlockChainStream_SetNextBlockInChain(
8041 BLOCK_END_OF_CHAIN
);
8043 if (This
->headOfStreamPlaceHolder
!= NULL
)
8045 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8049 DirEntry chainEntry
;
8051 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8054 chainEntry
.startingBlock
= blockIndex
;
8056 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8061 currentBlock
= blockIndex
;
8064 * Figure out how many blocks are needed to contain this stream
8066 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8068 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8072 * Go to the current end of chain
8074 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8077 currentBlock
= blockIndex
;
8078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8083 * Add new blocks to the chain
8085 while (oldNumBlocks
< newNumBlocks
)
8087 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8088 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8090 SmallBlockChainStream_SetNextBlockInChain(
8093 BLOCK_END_OF_CHAIN
);
8095 currentBlock
= blockIndex
;
8102 /******************************************************************************
8103 * SmallBlockChainStream_SetSize
8105 * Sets the size of this stream.
8106 * The file will grow if we grow the chain.
8108 * TODO: Free the actual blocks in the file when we shrink the chain.
8109 * Currently, the blocks are still in the file. So the file size
8110 * doesn't shrink even if we shrink streams.
8112 BOOL
SmallBlockChainStream_SetSize(
8113 SmallBlockChainStream
* This
,
8114 ULARGE_INTEGER newSize
)
8116 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8118 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8121 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8123 SmallBlockChainStream_Shrink(This
, newSize
);
8127 SmallBlockChainStream_Enlarge(This
, newSize
);
8133 /******************************************************************************
8134 * SmallBlockChainStream_GetCount
8136 * Returns the number of small blocks that comprises this chain.
8137 * This is not the size of the stream as the last block may not be full!
8140 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8145 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8147 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8151 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8152 blockIndex
, &blockIndex
)))
8159 /******************************************************************************
8160 * SmallBlockChainStream_GetSize
8162 * Returns the size of this chain.
8164 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8166 DirEntry chainEntry
;
8168 if(This
->headOfStreamPlaceHolder
!= NULL
)
8170 ULARGE_INTEGER result
;
8171 result
.u
.HighPart
= 0;
8173 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8174 This
->parentStorage
->smallBlockSize
;
8179 StorageImpl_ReadDirEntry(
8180 This
->parentStorage
,
8181 This
->ownerDirEntry
,
8184 return chainEntry
.size
;
8187 static HRESULT
create_storagefile(
8191 STGOPTIONS
* pStgOptions
,
8195 StorageBaseImpl
* newStorage
= 0;
8196 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8197 HRESULT hr
= STG_E_INVALIDFLAG
;
8201 DWORD fileAttributes
;
8202 WCHAR tempFileName
[MAX_PATH
];
8205 return STG_E_INVALIDPOINTER
;
8207 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8208 return STG_E_INVALIDPARAMETER
;
8210 /* if no share mode given then DENY_NONE is the default */
8211 if (STGM_SHARE_MODE(grfMode
) == 0)
8212 grfMode
|= STGM_SHARE_DENY_NONE
;
8214 if ( FAILED( validateSTGM(grfMode
) ))
8217 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8218 switch(STGM_ACCESS_MODE(grfMode
))
8221 case STGM_READWRITE
:
8227 /* in direct mode, can only use SHARE_EXCLUSIVE */
8228 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8231 /* but in transacted mode, any share mode is valid */
8234 * Generate a unique name.
8238 WCHAR tempPath
[MAX_PATH
];
8239 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8241 memset(tempPath
, 0, sizeof(tempPath
));
8242 memset(tempFileName
, 0, sizeof(tempFileName
));
8244 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8247 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8248 pwcsName
= tempFileName
;
8251 hr
= STG_E_INSUFFICIENTMEMORY
;
8255 creationMode
= TRUNCATE_EXISTING
;
8259 creationMode
= GetCreationModeFromSTGM(grfMode
);
8263 * Interpret the STGM value grfMode
8265 shareMode
= GetShareModeFromSTGM(grfMode
);
8266 accessMode
= GetAccessModeFromSTGM(grfMode
);
8268 if (grfMode
& STGM_DELETEONRELEASE
)
8269 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8271 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8275 hFile
= CreateFileW(pwcsName
,
8283 if (hFile
== INVALID_HANDLE_VALUE
)
8285 if(GetLastError() == ERROR_FILE_EXISTS
)
8286 hr
= STG_E_FILEALREADYEXISTS
;
8293 * Allocate and initialize the new IStorage32object.
8295 hr
= Storage_Construct(
8302 pStgOptions
->ulSectorSize
,
8310 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8311 IStorage_Release(&newStorage
->IStorage_iface
);
8314 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8319 /******************************************************************************
8320 * StgCreateDocfile [OLE32.@]
8321 * Creates a new compound file storage object
8324 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8325 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8326 * reserved [ ?] unused?, usually 0
8327 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8330 * S_OK if the file was successfully created
8331 * some STG_E_ value if error
8333 * if pwcsName is NULL, create file with new unique name
8334 * the function can returns
8335 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8338 HRESULT WINAPI
StgCreateDocfile(
8342 IStorage
**ppstgOpen
)
8344 STGOPTIONS stgoptions
= {1, 0, 512};
8346 TRACE("(%s, %x, %d, %p)\n",
8347 debugstr_w(pwcsName
), grfMode
,
8348 reserved
, ppstgOpen
);
8351 return STG_E_INVALIDPOINTER
;
8353 return STG_E_INVALIDPARAMETER
;
8355 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8358 /******************************************************************************
8359 * StgCreateStorageEx [OLE32.@]
8361 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8363 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8364 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8366 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8368 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8369 return STG_E_INVALIDPARAMETER
;
8372 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8374 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8375 return STG_E_INVALIDPARAMETER
;
8378 if (stgfmt
== STGFMT_FILE
)
8380 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8381 return STG_E_INVALIDPARAMETER
;
8384 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8386 STGOPTIONS defaultOptions
= {1, 0, 512};
8388 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8389 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8393 ERR("Invalid stgfmt argument\n");
8394 return STG_E_INVALIDPARAMETER
;
8397 /******************************************************************************
8398 * StgCreatePropSetStg [OLE32.@]
8400 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8401 IPropertySetStorage
**propset
)
8403 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8405 return STG_E_INVALIDPARAMETER
;
8407 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8410 /******************************************************************************
8411 * StgOpenStorageEx [OLE32.@]
8413 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8415 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8416 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8418 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8420 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8421 return STG_E_INVALIDPARAMETER
;
8427 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8428 return STG_E_INVALIDPARAMETER
;
8430 case STGFMT_STORAGE
:
8433 case STGFMT_DOCFILE
:
8434 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8436 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8437 return STG_E_INVALIDPARAMETER
;
8439 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8443 WARN("STGFMT_ANY assuming storage\n");
8447 return STG_E_INVALIDPARAMETER
;
8450 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8454 /******************************************************************************
8455 * StgOpenStorage [OLE32.@]
8457 HRESULT WINAPI
StgOpenStorage(
8458 const OLECHAR
*pwcsName
,
8459 IStorage
*pstgPriority
,
8463 IStorage
**ppstgOpen
)
8465 StorageBaseImpl
* newStorage
= 0;
8470 LPWSTR temp_name
= NULL
;
8472 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8473 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8474 snbExclude
, reserved
, ppstgOpen
);
8478 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8479 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8480 if (FAILED(hr
)) goto end
;
8481 pwcsName
= temp_name
;
8482 TRACE("using filename %s\n", debugstr_w(temp_name
));
8487 hr
= STG_E_INVALIDNAME
;
8493 hr
= STG_E_INVALIDPOINTER
;
8499 hr
= STG_E_INVALIDPARAMETER
;
8503 if (grfMode
& STGM_PRIORITY
)
8505 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8506 return STG_E_INVALIDFLAG
;
8507 if (grfMode
& STGM_DELETEONRELEASE
)
8508 return STG_E_INVALIDFUNCTION
;
8509 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8510 return STG_E_INVALIDFLAG
;
8511 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8512 grfMode
|= STGM_SHARE_DENY_NONE
;
8516 * Validate the sharing mode
8518 if (grfMode
& STGM_DIRECT_SWMR
)
8520 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8521 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8523 hr
= STG_E_INVALIDFLAG
;
8527 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8528 switch(STGM_SHARE_MODE(grfMode
))
8530 case STGM_SHARE_EXCLUSIVE
:
8531 case STGM_SHARE_DENY_WRITE
:
8534 hr
= STG_E_INVALIDFLAG
;
8538 if ( FAILED( validateSTGM(grfMode
) ) ||
8539 (grfMode
&STGM_CREATE
))
8541 hr
= STG_E_INVALIDFLAG
;
8545 /* shared reading requires transacted or single writer mode */
8546 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8547 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8548 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8550 hr
= STG_E_INVALIDFLAG
;
8555 * Interpret the STGM value grfMode
8557 shareMode
= GetShareModeFromSTGM(grfMode
);
8558 accessMode
= GetAccessModeFromSTGM(grfMode
);
8562 hFile
= CreateFileW( pwcsName
,
8567 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8570 if (hFile
==INVALID_HANDLE_VALUE
)
8572 DWORD last_error
= GetLastError();
8578 case ERROR_FILE_NOT_FOUND
:
8579 hr
= STG_E_FILENOTFOUND
;
8582 case ERROR_PATH_NOT_FOUND
:
8583 hr
= STG_E_PATHNOTFOUND
;
8586 case ERROR_ACCESS_DENIED
:
8587 case ERROR_WRITE_PROTECT
:
8588 hr
= STG_E_ACCESSDENIED
;
8591 case ERROR_SHARING_VIOLATION
:
8592 hr
= STG_E_SHAREVIOLATION
;
8603 * Refuse to open the file if it's too small to be a structured storage file
8604 * FIXME: verify the file when reading instead of here
8606 if (GetFileSize(hFile
, NULL
) < 0x100)
8609 hr
= STG_E_FILEALREADYEXISTS
;
8614 * Allocate and initialize the new IStorage32object.
8616 hr
= Storage_Construct(
8629 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8631 if(hr
== STG_E_INVALIDHEADER
)
8632 hr
= STG_E_FILEALREADYEXISTS
;
8636 *ppstgOpen
= &newStorage
->IStorage_iface
;
8639 CoTaskMemFree(temp_name
);
8640 if (pstgPriority
) IStorage_Release(pstgPriority
);
8641 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8645 /******************************************************************************
8646 * StgCreateDocfileOnILockBytes [OLE32.@]
8648 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8652 IStorage
** ppstgOpen
)
8654 StorageBaseImpl
* newStorage
= 0;
8657 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8658 return STG_E_INVALIDPOINTER
;
8661 * Allocate and initialize the new IStorage object.
8663 hr
= Storage_Construct(
8678 *ppstgOpen
= &newStorage
->IStorage_iface
;
8683 /******************************************************************************
8684 * StgOpenStorageOnILockBytes [OLE32.@]
8686 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8688 IStorage
*pstgPriority
,
8692 IStorage
**ppstgOpen
)
8694 StorageBaseImpl
* newStorage
= 0;
8697 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8698 return STG_E_INVALIDPOINTER
;
8700 if ( FAILED( validateSTGM(grfMode
) ))
8701 return STG_E_INVALIDFLAG
;
8706 * Allocate and initialize the new IStorage object.
8708 hr
= Storage_Construct(
8723 *ppstgOpen
= &newStorage
->IStorage_iface
;
8728 /******************************************************************************
8729 * StgSetTimes [ole32.@]
8730 * StgSetTimes [OLE32.@]
8734 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8735 FILETIME
const *patime
, FILETIME
const *pmtime
)
8737 IStorage
*stg
= NULL
;
8740 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8742 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8746 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8747 IStorage_Release(stg
);
8753 /******************************************************************************
8754 * StgIsStorageILockBytes [OLE32.@]
8756 * Determines if the ILockBytes contains a storage object.
8758 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8760 BYTE sig
[sizeof(STORAGE_magic
)];
8761 ULARGE_INTEGER offset
;
8764 offset
.u
.HighPart
= 0;
8765 offset
.u
.LowPart
= 0;
8767 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8769 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8775 /******************************************************************************
8776 * WriteClassStg [OLE32.@]
8778 * This method will store the specified CLSID in the specified storage object
8780 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8783 return E_INVALIDARG
;
8786 return STG_E_INVALIDPOINTER
;
8788 return IStorage_SetClass(pStg
, rclsid
);
8791 /***********************************************************************
8792 * ReadClassStg (OLE32.@)
8794 * This method reads the CLSID previously written to a storage object with
8795 * the WriteClassStg.
8798 * pstg [I] IStorage pointer
8799 * pclsid [O] Pointer to where the CLSID is written
8803 * Failure: HRESULT code.
8805 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8810 TRACE("(%p, %p)\n", pstg
, pclsid
);
8812 if(!pstg
|| !pclsid
)
8813 return E_INVALIDARG
;
8816 * read a STATSTG structure (contains the clsid) from the storage
8818 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8821 *pclsid
=pstatstg
.clsid
;
8826 /***********************************************************************
8827 * OleLoadFromStream (OLE32.@)
8829 * This function loads an object from stream
8831 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8835 LPPERSISTSTREAM xstm
;
8837 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8839 res
=ReadClassStm(pStm
,&clsid
);
8842 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8845 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8847 IUnknown_Release((IUnknown
*)*ppvObj
);
8850 res
=IPersistStream_Load(xstm
,pStm
);
8851 IPersistStream_Release(xstm
);
8852 /* FIXME: all refcounts ok at this point? I think they should be:
8855 * xstm : 0 (released)
8860 /***********************************************************************
8861 * OleSaveToStream (OLE32.@)
8863 * This function saves an object with the IPersistStream interface on it
8864 * to the specified stream.
8866 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8872 TRACE("(%p,%p)\n",pPStm
,pStm
);
8874 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8876 if (SUCCEEDED(res
)){
8878 res
=WriteClassStm(pStm
,&clsid
);
8882 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8885 TRACE("Finished Save\n");
8889 /****************************************************************************
8890 * This method validate a STGM parameter that can contain the values below
8892 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8893 * The stgm values contained in 0xffff0000 are bitmasks.
8895 * STGM_DIRECT 0x00000000
8896 * STGM_TRANSACTED 0x00010000
8897 * STGM_SIMPLE 0x08000000
8899 * STGM_READ 0x00000000
8900 * STGM_WRITE 0x00000001
8901 * STGM_READWRITE 0x00000002
8903 * STGM_SHARE_DENY_NONE 0x00000040
8904 * STGM_SHARE_DENY_READ 0x00000030
8905 * STGM_SHARE_DENY_WRITE 0x00000020
8906 * STGM_SHARE_EXCLUSIVE 0x00000010
8908 * STGM_PRIORITY 0x00040000
8909 * STGM_DELETEONRELEASE 0x04000000
8911 * STGM_CREATE 0x00001000
8912 * STGM_CONVERT 0x00020000
8913 * STGM_FAILIFTHERE 0x00000000
8915 * STGM_NOSCRATCH 0x00100000
8916 * STGM_NOSNAPSHOT 0x00200000
8918 static HRESULT
validateSTGM(DWORD stgm
)
8920 DWORD access
= STGM_ACCESS_MODE(stgm
);
8921 DWORD share
= STGM_SHARE_MODE(stgm
);
8922 DWORD create
= STGM_CREATE_MODE(stgm
);
8924 if (stgm
&~STGM_KNOWN_FLAGS
)
8926 ERR("unknown flags %08x\n", stgm
);
8934 case STGM_READWRITE
:
8942 case STGM_SHARE_DENY_NONE
:
8943 case STGM_SHARE_DENY_READ
:
8944 case STGM_SHARE_DENY_WRITE
:
8945 case STGM_SHARE_EXCLUSIVE
:
8948 if (!(stgm
& STGM_TRANSACTED
))
8958 case STGM_FAILIFTHERE
:
8965 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8967 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8971 * STGM_CREATE | STGM_CONVERT
8972 * if both are false, STGM_FAILIFTHERE is set to TRUE
8974 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8978 * STGM_NOSCRATCH requires STGM_TRANSACTED
8980 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8984 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8985 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8987 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8988 (!(stgm
& STGM_TRANSACTED
) ||
8989 share
== STGM_SHARE_EXCLUSIVE
||
8990 share
== STGM_SHARE_DENY_WRITE
) )
8996 /****************************************************************************
8997 * GetShareModeFromSTGM
8999 * This method will return a share mode flag from a STGM value.
9000 * The STGM value is assumed valid.
9002 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
9004 switch (STGM_SHARE_MODE(stgm
))
9007 assert(stgm
& STGM_TRANSACTED
);
9009 case STGM_SHARE_DENY_NONE
:
9010 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
9011 case STGM_SHARE_DENY_READ
:
9012 return FILE_SHARE_WRITE
;
9013 case STGM_SHARE_DENY_WRITE
:
9014 case STGM_SHARE_EXCLUSIVE
:
9015 return FILE_SHARE_READ
;
9017 ERR("Invalid share mode!\n");
9022 /****************************************************************************
9023 * GetAccessModeFromSTGM
9025 * This method will return an access mode flag from a STGM value.
9026 * The STGM value is assumed valid.
9028 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
9030 switch (STGM_ACCESS_MODE(stgm
))
9033 return GENERIC_READ
;
9035 case STGM_READWRITE
:
9036 return GENERIC_READ
| GENERIC_WRITE
;
9038 ERR("Invalid access mode!\n");
9043 /****************************************************************************
9044 * GetCreationModeFromSTGM
9046 * This method will return a creation mode flag from a STGM value.
9047 * The STGM value is assumed valid.
9049 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9051 switch(STGM_CREATE_MODE(stgm
))
9054 return CREATE_ALWAYS
;
9056 FIXME("STGM_CONVERT not implemented!\n");
9058 case STGM_FAILIFTHERE
:
9061 ERR("Invalid create mode!\n");
9067 /*************************************************************************
9068 * OLECONVERT_LoadOLE10 [Internal]
9070 * Loads the OLE10 STREAM to memory
9073 * pOleStream [I] The OLESTREAM
9074 * pData [I] Data Structure for the OLESTREAM Data
9078 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9079 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9082 * This function is used by OleConvertOLESTREAMToIStorage only.
9084 * Memory allocated for pData must be freed by the caller
9086 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9089 HRESULT hRes
= S_OK
;
9093 pData
->pData
= NULL
;
9094 pData
->pstrOleObjFileName
= NULL
;
9096 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9099 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9100 if(dwSize
!= sizeof(pData
->dwOleID
))
9102 hRes
= CONVERT10_E_OLESTREAM_GET
;
9104 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9106 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9117 /* Get the TypeID... more info needed for this field */
9118 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9119 if(dwSize
!= sizeof(pData
->dwTypeID
))
9121 hRes
= CONVERT10_E_OLESTREAM_GET
;
9126 if(pData
->dwTypeID
!= 0)
9128 /* Get the length of the OleTypeName */
9129 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9130 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9132 hRes
= CONVERT10_E_OLESTREAM_GET
;
9137 if(pData
->dwOleTypeNameLength
> 0)
9139 /* Get the OleTypeName */
9140 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9141 if(dwSize
!= pData
->dwOleTypeNameLength
)
9143 hRes
= CONVERT10_E_OLESTREAM_GET
;
9149 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9150 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9152 hRes
= CONVERT10_E_OLESTREAM_GET
;
9156 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9157 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9158 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9159 if(pData
->pstrOleObjFileName
)
9161 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9162 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9164 hRes
= CONVERT10_E_OLESTREAM_GET
;
9168 hRes
= CONVERT10_E_OLESTREAM_GET
;
9173 /* Get the Width of the Metafile */
9174 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9175 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9177 hRes
= CONVERT10_E_OLESTREAM_GET
;
9181 /* Get the Height of the Metafile */
9182 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9183 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9185 hRes
= CONVERT10_E_OLESTREAM_GET
;
9191 /* Get the Length of the Data */
9192 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9193 if(dwSize
!= sizeof(pData
->dwDataLength
))
9195 hRes
= CONVERT10_E_OLESTREAM_GET
;
9199 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9201 if(!bStrem1
) /* if it is a second OLE stream data */
9203 pData
->dwDataLength
-= 8;
9204 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9205 if(dwSize
!= sizeof(pData
->strUnknown
))
9207 hRes
= CONVERT10_E_OLESTREAM_GET
;
9213 if(pData
->dwDataLength
> 0)
9215 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9217 /* Get Data (ex. IStorage, Metafile, or BMP) */
9220 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9221 if(dwSize
!= pData
->dwDataLength
)
9223 hRes
= CONVERT10_E_OLESTREAM_GET
;
9228 hRes
= CONVERT10_E_OLESTREAM_GET
;
9237 /*************************************************************************
9238 * OLECONVERT_SaveOLE10 [Internal]
9240 * Saves the OLE10 STREAM From memory
9243 * pData [I] Data Structure for the OLESTREAM Data
9244 * pOleStream [I] The OLESTREAM to save
9248 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9251 * This function is used by OleConvertIStorageToOLESTREAM only.
9254 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9257 HRESULT hRes
= S_OK
;
9261 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9262 if(dwSize
!= sizeof(pData
->dwOleID
))
9264 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9269 /* Set the TypeID */
9270 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9271 if(dwSize
!= sizeof(pData
->dwTypeID
))
9273 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9277 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9279 /* Set the Length of the OleTypeName */
9280 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9281 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9283 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9288 if(pData
->dwOleTypeNameLength
> 0)
9290 /* Set the OleTypeName */
9291 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9292 if(dwSize
!= pData
->dwOleTypeNameLength
)
9294 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9301 /* Set the width of the Metafile */
9302 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9303 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9305 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9311 /* Set the height of the Metafile */
9312 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9313 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9315 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9321 /* Set the length of the Data */
9322 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9323 if(dwSize
!= sizeof(pData
->dwDataLength
))
9325 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9331 if(pData
->dwDataLength
> 0)
9333 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9334 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9335 if(dwSize
!= pData
->dwDataLength
)
9337 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9345 /*************************************************************************
9346 * OLECONVERT_GetOLE20FromOLE10[Internal]
9348 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9349 * opens it, and copies the content to the dest IStorage for
9350 * OleConvertOLESTREAMToIStorage
9354 * pDestStorage [I] The IStorage to copy the data to
9355 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9356 * nBufferLength [I] The size of the buffer
9365 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9369 IStorage
*pTempStorage
;
9370 DWORD dwNumOfBytesWritten
;
9371 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9372 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9374 /* Create a temp File */
9375 GetTempPathW(MAX_PATH
, wstrTempDir
);
9376 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9377 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9379 if(hFile
!= INVALID_HANDLE_VALUE
)
9381 /* Write IStorage Data to File */
9382 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9385 /* Open and copy temp storage to the Dest Storage */
9386 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9389 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9390 IStorage_Release(pTempStorage
);
9392 DeleteFileW(wstrTempFile
);
9397 /*************************************************************************
9398 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9400 * Saves the OLE10 STREAM From memory
9403 * pStorage [I] The Src IStorage to copy
9404 * pData [I] The Dest Memory to write to.
9407 * The size in bytes allocated for pData
9410 * Memory allocated for pData must be freed by the caller
9412 * Used by OleConvertIStorageToOLESTREAM only.
9415 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9419 DWORD nDataLength
= 0;
9420 IStorage
*pTempStorage
;
9421 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9422 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9426 /* Create temp Storage */
9427 GetTempPathW(MAX_PATH
, wstrTempDir
);
9428 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9429 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9433 /* Copy Src Storage to the Temp Storage */
9434 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9435 IStorage_Release(pTempStorage
);
9437 /* Open Temp Storage as a file and copy to memory */
9438 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9439 if(hFile
!= INVALID_HANDLE_VALUE
)
9441 nDataLength
= GetFileSize(hFile
, NULL
);
9442 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9443 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9446 DeleteFileW(wstrTempFile
);
9451 /*************************************************************************
9452 * STORAGE_CreateOleStream [Internal]
9454 * Creates the "\001OLE" stream in the IStorage if necessary.
9457 * storage [I] Dest storage to create the stream in
9458 * flags [I] flags to be set for newly created stream
9461 * HRESULT return value
9465 * This stream is still unknown, MS Word seems to have extra data
9466 * but since the data is stored in the OLESTREAM there should be
9467 * no need to recreate the stream. If the stream is manually
9468 * deleted it will create it with this default data.
9471 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9473 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9474 static const DWORD version_magic
= 0x02000001;
9478 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9481 struct empty_1ole_stream
{
9482 DWORD version_magic
;
9484 DWORD update_options
;
9486 DWORD mon_stream_size
;
9488 struct empty_1ole_stream stream_data
;
9490 stream_data
.version_magic
= version_magic
;
9491 stream_data
.flags
= flags
;
9492 stream_data
.update_options
= 0;
9493 stream_data
.reserved
= 0;
9494 stream_data
.mon_stream_size
= 0;
9496 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9497 IStream_Release(stream
);
9503 /* write a string to a stream, preceded by its length */
9504 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9511 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9512 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9517 str
= CoTaskMemAlloc( len
);
9518 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9519 r
= IStream_Write( stm
, str
, len
, NULL
);
9520 CoTaskMemFree( str
);
9524 /* read a string preceded by its length from a stream */
9525 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9528 DWORD len
, count
= 0;
9532 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9535 if( count
!= sizeof(len
) )
9536 return E_OUTOFMEMORY
;
9538 TRACE("%d bytes\n",len
);
9540 str
= CoTaskMemAlloc( len
);
9542 return E_OUTOFMEMORY
;
9544 r
= IStream_Read( stm
, str
, len
, &count
);
9549 CoTaskMemFree( str
);
9550 return E_OUTOFMEMORY
;
9553 TRACE("Read string %s\n",debugstr_an(str
,len
));
9555 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9556 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9559 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9562 CoTaskMemFree( str
);
9570 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9571 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9575 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9577 static const BYTE unknown1
[12] =
9578 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9579 0xFF, 0xFF, 0xFF, 0xFF};
9580 static const BYTE unknown2
[16] =
9581 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9584 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9585 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9586 debugstr_w(szProgIDName
));
9588 /* Create a CompObj stream */
9589 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9590 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9594 /* Write CompObj Structure to stream */
9595 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9597 if( SUCCEEDED( r
) )
9598 r
= WriteClassStm( pstm
, clsid
);
9600 if( SUCCEEDED( r
) )
9601 r
= STREAM_WriteString( pstm
, lpszUserType
);
9602 if( SUCCEEDED( r
) )
9603 r
= STREAM_WriteString( pstm
, szClipName
);
9604 if( SUCCEEDED( r
) )
9605 r
= STREAM_WriteString( pstm
, szProgIDName
);
9606 if( SUCCEEDED( r
) )
9607 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9609 IStream_Release( pstm
);
9614 /***********************************************************************
9615 * WriteFmtUserTypeStg (OLE32.@)
9617 HRESULT WINAPI
WriteFmtUserTypeStg(
9618 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9622 WCHAR szwClipName
[0x40];
9624 LPWSTR wstrProgID
= NULL
;
9627 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9629 /* get the clipboard format name */
9632 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9633 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9637 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9639 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9645 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9647 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9649 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9650 cf
? szwClipName
: NULL
, wstrProgID
);
9652 CoTaskMemFree(wstrProgID
);
9658 /******************************************************************************
9659 * ReadFmtUserTypeStg [OLE32.@]
9661 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9665 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9666 unsigned char unknown1
[12];
9667 unsigned char unknown2
[16];
9669 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9672 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9674 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9675 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9678 WARN("Failed to open stream r = %08x\n", r
);
9682 /* read the various parts of the structure */
9683 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9684 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9686 r
= ReadClassStm( stm
, &clsid
);
9690 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9694 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9698 r
= STREAM_ReadString( stm
, &szProgIDName
);
9702 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9703 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9706 /* ok, success... now we just need to store what we found */
9708 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9710 if( lplpszUserType
)
9712 *lplpszUserType
= szCLSIDName
;
9717 CoTaskMemFree( szCLSIDName
);
9718 CoTaskMemFree( szOleTypeName
);
9719 CoTaskMemFree( szProgIDName
);
9720 IStream_Release( stm
);
9726 /*************************************************************************
9727 * OLECONVERT_CreateCompObjStream [Internal]
9729 * Creates a "\001CompObj" is the destination IStorage if necessary.
9732 * pStorage [I] The dest IStorage to create the CompObj Stream
9734 * strOleTypeName [I] The ProgID
9738 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9741 * This function is used by OleConvertOLESTREAMToIStorage only.
9743 * The stream data is stored in the OLESTREAM and there should be
9744 * no need to recreate the stream. If the stream is manually
9745 * deleted it will attempt to create it by querying the registry.
9749 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9752 HRESULT hStorageRes
, hRes
= S_OK
;
9753 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9754 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9755 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9757 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9758 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9760 /* Initialize the CompObj structure */
9761 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9762 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9763 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9766 /* Create a CompObj stream if it doesn't exist */
9767 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9768 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9769 if(hStorageRes
== S_OK
)
9771 /* copy the OleTypeName to the compobj struct */
9772 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9773 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9775 /* copy the OleTypeName to the compobj struct */
9776 /* Note: in the test made, these were Identical */
9777 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9778 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9781 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9782 bufferW
, OLESTREAM_MAX_STR_LEN
);
9783 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9789 /* Get the CLSID Default Name from the Registry */
9790 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9791 if(hErr
== ERROR_SUCCESS
)
9793 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9794 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9795 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9796 if(hErr
== ERROR_SUCCESS
)
9798 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9804 /* Write CompObj Structure to stream */
9805 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9807 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9809 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9810 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9812 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9814 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9815 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9817 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9819 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9820 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9822 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9824 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9825 IStream_Release(pStream
);
9831 /*************************************************************************
9832 * OLECONVERT_CreateOlePresStream[Internal]
9834 * Creates the "\002OlePres000" Stream with the Metafile data
9837 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9838 * dwExtentX [I] Width of the Metafile
9839 * dwExtentY [I] Height of the Metafile
9840 * pData [I] Metafile data
9841 * dwDataLength [I] Size of the Metafile data
9845 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9848 * This function is used by OleConvertOLESTREAMToIStorage only.
9851 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9855 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9856 BYTE pOlePresStreamHeader
[] =
9858 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9859 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9860 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9861 0x00, 0x00, 0x00, 0x00
9864 BYTE pOlePresStreamHeaderEmpty
[] =
9866 0x00, 0x00, 0x00, 0x00,
9867 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9868 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9869 0x00, 0x00, 0x00, 0x00
9872 /* Create the OlePres000 Stream */
9873 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9874 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9879 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9881 memset(&OlePres
, 0, sizeof(OlePres
));
9882 /* Do we have any metafile data to save */
9883 if(dwDataLength
> 0)
9885 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9886 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9890 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9891 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9893 /* Set width and height of the metafile */
9894 OlePres
.dwExtentX
= dwExtentX
;
9895 OlePres
.dwExtentY
= -dwExtentY
;
9897 /* Set Data and Length */
9898 if(dwDataLength
> sizeof(METAFILEPICT16
))
9900 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9901 OlePres
.pData
= &(pData
[8]);
9903 /* Save OlePres000 Data to Stream */
9904 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9905 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9906 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9907 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9908 if(OlePres
.dwSize
> 0)
9910 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9912 IStream_Release(pStream
);
9916 /*************************************************************************
9917 * OLECONVERT_CreateOle10NativeStream [Internal]
9919 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9922 * pStorage [I] Dest storage to create the stream in
9923 * pData [I] Ole10 Native Data (ex. bmp)
9924 * dwDataLength [I] Size of the Ole10 Native Data
9930 * This function is used by OleConvertOLESTREAMToIStorage only.
9932 * Might need to verify the data and return appropriate error message
9935 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9939 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9941 /* Create the Ole10Native Stream */
9942 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9943 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9947 /* Write info to stream */
9948 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9949 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9950 IStream_Release(pStream
);
9955 /*************************************************************************
9956 * OLECONVERT_GetOLE10ProgID [Internal]
9958 * Finds the ProgID (or OleTypeID) from the IStorage
9961 * pStorage [I] The Src IStorage to get the ProgID
9962 * strProgID [I] the ProgID string to get
9963 * dwSize [I] the size of the string
9967 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9970 * This function is used by OleConvertIStorageToOLESTREAM only.
9974 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9978 LARGE_INTEGER iSeekPos
;
9979 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9980 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9982 /* Open the CompObj Stream */
9983 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9984 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9988 /*Get the OleType from the CompObj Stream */
9989 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9990 iSeekPos
.u
.HighPart
= 0;
9992 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9993 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9994 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9995 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9996 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9997 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9998 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10000 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10003 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10005 IStream_Release(pStream
);
10010 LPOLESTR wstrProgID
;
10012 /* Get the OleType from the registry */
10013 REFCLSID clsid
= &(stat
.clsid
);
10014 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10015 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10018 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10019 CoTaskMemFree(wstrProgID
);
10026 /*************************************************************************
10027 * OLECONVERT_GetOle10PresData [Internal]
10029 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10032 * pStorage [I] Src IStroage
10033 * pOleStream [I] Dest OleStream Mem Struct
10039 * This function is used by OleConvertIStorageToOLESTREAM only.
10041 * Memory allocated for pData must be freed by the caller
10045 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10050 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10052 /* Initialize Default data for OLESTREAM */
10053 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10054 pOleStreamData
[0].dwTypeID
= 2;
10055 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10056 pOleStreamData
[1].dwTypeID
= 0;
10057 pOleStreamData
[0].dwMetaFileWidth
= 0;
10058 pOleStreamData
[0].dwMetaFileHeight
= 0;
10059 pOleStreamData
[0].pData
= NULL
;
10060 pOleStreamData
[1].pData
= NULL
;
10062 /* Open Ole10Native Stream */
10063 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10064 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10068 /* Read Size and Data */
10069 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10070 if(pOleStreamData
->dwDataLength
> 0)
10072 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10073 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10075 IStream_Release(pStream
);
10081 /*************************************************************************
10082 * OLECONVERT_GetOle20PresData[Internal]
10084 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10087 * pStorage [I] Src IStroage
10088 * pOleStreamData [I] Dest OleStream Mem Struct
10094 * This function is used by OleConvertIStorageToOLESTREAM only.
10096 * Memory allocated for pData must be freed by the caller
10098 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10102 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10103 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10105 /* Initialize Default data for OLESTREAM */
10106 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10107 pOleStreamData
[0].dwTypeID
= 2;
10108 pOleStreamData
[0].dwMetaFileWidth
= 0;
10109 pOleStreamData
[0].dwMetaFileHeight
= 0;
10110 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10111 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10112 pOleStreamData
[1].dwTypeID
= 0;
10113 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10114 pOleStreamData
[1].strOleTypeName
[0] = 0;
10115 pOleStreamData
[1].dwMetaFileWidth
= 0;
10116 pOleStreamData
[1].dwMetaFileHeight
= 0;
10117 pOleStreamData
[1].pData
= NULL
;
10118 pOleStreamData
[1].dwDataLength
= 0;
10121 /* Open OlePress000 stream */
10122 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10123 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10126 LARGE_INTEGER iSeekPos
;
10127 METAFILEPICT16 MetaFilePict
;
10128 static const char strMetafilePictName
[] = "METAFILEPICT";
10130 /* Set the TypeID for a Metafile */
10131 pOleStreamData
[1].dwTypeID
= 5;
10133 /* Set the OleTypeName to Metafile */
10134 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10135 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10137 iSeekPos
.u
.HighPart
= 0;
10138 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10140 /* Get Presentation Data */
10141 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10142 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10143 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10144 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10146 /*Set width and Height */
10147 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10148 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10149 if(olePress
.dwSize
> 0)
10152 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10154 /* Set MetaFilePict struct */
10155 MetaFilePict
.mm
= 8;
10156 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10157 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10158 MetaFilePict
.hMF
= 0;
10160 /* Get Metafile Data */
10161 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10162 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10163 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10165 IStream_Release(pStream
);
10169 /*************************************************************************
10170 * OleConvertOLESTREAMToIStorage [OLE32.@]
10172 * Read info on MSDN
10175 * DVTARGETDEVICE parameter is not handled
10176 * Still unsure of some mem fields for OLE 10 Stream
10177 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10178 * and "\001OLE" streams
10181 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10182 LPOLESTREAM pOleStream
,
10184 const DVTARGETDEVICE
* ptd
)
10188 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10190 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10192 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10196 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10199 if(pstg
== NULL
|| pOleStream
== NULL
)
10201 hRes
= E_INVALIDARG
;
10206 /* Load the OLESTREAM to Memory */
10207 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10212 /* Load the OLESTREAM to Memory (part 2)*/
10213 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10219 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10221 /* Do we have the IStorage Data in the OLESTREAM */
10222 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10224 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10225 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10229 /* It must be an original OLE 1.0 source */
10230 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10235 /* It must be an original OLE 1.0 source */
10236 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10239 /* Create CompObj Stream if necessary */
10240 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10243 /*Create the Ole Stream if necessary */
10244 STORAGE_CreateOleStream(pstg
, 0);
10249 /* Free allocated memory */
10250 for(i
=0; i
< 2; i
++)
10252 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10253 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10254 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10259 /*************************************************************************
10260 * OleConvertIStorageToOLESTREAM [OLE32.@]
10262 * Read info on MSDN
10264 * Read info on MSDN
10267 * Still unsure of some mem fields for OLE 10 Stream
10268 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10269 * and "\001OLE" streams.
10272 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10274 LPOLESTREAM pOleStream
)
10277 HRESULT hRes
= S_OK
;
10279 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10280 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10282 TRACE("%p %p\n", pstg
, pOleStream
);
10284 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10286 if(pstg
== NULL
|| pOleStream
== NULL
)
10288 hRes
= E_INVALIDARG
;
10292 /* Get the ProgID */
10293 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10294 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10298 /* Was it originally Ole10 */
10299 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10302 IStream_Release(pStream
);
10303 /* Get Presentation Data for Ole10Native */
10304 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10308 /* Get Presentation Data (OLE20) */
10309 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10312 /* Save OLESTREAM */
10313 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10316 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10321 /* Free allocated memory */
10322 for(i
=0; i
< 2; i
++)
10324 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10330 enum stream_1ole_flags
{
10331 OleStream_LinkedObject
= 0x00000001,
10332 OleStream_Convert
= 0x00000004
10335 /***********************************************************************
10336 * GetConvertStg (OLE32.@)
10338 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10340 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10341 static const DWORD version_magic
= 0x02000001;
10346 TRACE("%p\n", stg
);
10348 if (!stg
) return E_INVALIDARG
;
10350 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10351 if (FAILED(hr
)) return hr
;
10353 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10354 IStream_Release(stream
);
10355 if (FAILED(hr
)) return hr
;
10357 if (header
[0] != version_magic
)
10359 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10363 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10366 /***********************************************************************
10367 * SetConvertStg (OLE32.@)
10369 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10371 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10372 DWORD flags
= convert
? OleStream_Convert
: 0;
10377 TRACE("(%p, %d)\n", storage
, convert
);
10379 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10382 if (hr
!= STG_E_FILENOTFOUND
)
10385 return STORAGE_CreateOleStream(storage
, flags
);
10388 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10391 IStream_Release(stream
);
10395 /* update flag if differs */
10396 if ((header
[1] ^ flags
) & OleStream_Convert
)
10398 LARGE_INTEGER pos
= {{0}};
10400 if (header
[1] & OleStream_Convert
)
10401 flags
= header
[1] & ~OleStream_Convert
;
10403 flags
= header
[1] | OleStream_Convert
;
10405 pos
.QuadPart
= sizeof(DWORD
);
10406 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10409 IStream_Release(stream
);
10413 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10416 IStream_Release(stream
);
10420 /******************************************************************************
10421 * StgIsStorageFile [OLE32.@]
10422 * Verify if the file contains a storage object
10428 * S_OK if file has magic bytes as a storage object
10429 * S_FALSE if file is not storage
10432 StgIsStorageFile(LPCOLESTR fn
)
10438 TRACE("%s\n", debugstr_w(fn
));
10439 hf
= CreateFileW(fn
, GENERIC_READ
,
10440 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10441 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10443 if (hf
== INVALID_HANDLE_VALUE
)
10444 return STG_E_FILENOTFOUND
;
10446 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10448 WARN(" unable to read file\n");
10455 if (bytes_read
!= 8) {
10456 TRACE(" too short\n");
10460 if (!memcmp(magic
,STORAGE_magic
,8)) {
10461 TRACE(" -> YES\n");
10465 TRACE(" -> Invalid header.\n");
10469 /***********************************************************************
10470 * WriteClassStm (OLE32.@)
10472 * Writes a CLSID to a stream.
10475 * pStm [I] Stream to write to.
10476 * rclsid [I] CLSID to write.
10480 * Failure: HRESULT code.
10482 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10484 TRACE("(%p,%p)\n",pStm
,rclsid
);
10486 if (!pStm
|| !rclsid
)
10487 return E_INVALIDARG
;
10489 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10492 /***********************************************************************
10493 * ReadClassStm (OLE32.@)
10495 * Reads a CLSID from a stream.
10498 * pStm [I] Stream to read from.
10499 * rclsid [O] CLSID to read.
10503 * Failure: HRESULT code.
10505 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10510 TRACE("(%p,%p)\n",pStm
,pclsid
);
10512 if (!pStm
|| !pclsid
)
10513 return E_INVALIDARG
;
10515 /* clear the output args */
10516 *pclsid
= CLSID_NULL
;
10518 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10523 if (nbByte
!= sizeof(CLSID
))
10524 return STG_E_READFAULT
;