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 void StorageImpl_Destroy(StorageBaseImpl
* iface
);
106 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
107 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
108 static HRESULT
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
, ULONG
*read
);
109 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
111 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
112 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
113 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
, ULARGE_INTEGER cb
, DWORD dwLockType
);
115 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
);
116 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
117 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
118 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
119 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
121 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
122 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
123 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
125 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
126 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
127 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
128 ULONG blockIndex
, ULONG offset
, DWORD value
);
129 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
130 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
132 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
133 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
135 typedef struct TransactedDirEntry
137 /* If applicable, a reference to the original DirEntry in the transacted
138 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
139 DirRef transactedParentEntry
;
141 /* True if this entry is being used. */
144 /* True if data is up to date. */
147 /* True if this entry has been modified. */
150 /* True if this entry's stream has been modified. */
153 /* True if this entry has been deleted in the transacted storage, but the
154 * delete has not yet been committed. */
157 /* If this entry's stream has been modified, a reference to where the stream
158 * is stored in the snapshot file. */
161 /* This directory entry's data, including any changes that have been made. */
164 /* A reference to the parent of this node. This is only valid while we are
165 * committing changes. */
168 /* A reference to a newly-created entry in the transacted parent. This is
169 * always equal to transactedParentEntry except when committing changes. */
170 DirRef newTransactedParentEntry
;
171 } TransactedDirEntry
;
173 /****************************************************************************
174 * Transacted storage object.
176 typedef struct TransactedSnapshotImpl
178 struct StorageBaseImpl base
;
181 * Modified streams are temporarily saved to the scratch file.
183 StorageBaseImpl
*scratch
;
185 /* The directory structure is kept here, so that we can track how these
186 * entries relate to those in the parent storage. */
187 TransactedDirEntry
*entries
;
189 ULONG firstFreeEntry
;
192 * Changes are committed to the transacted parent.
194 StorageBaseImpl
*transactedParent
;
196 /* The transaction signature from when we last committed */
197 ULONG lastTransactionSig
;
198 } TransactedSnapshotImpl
;
200 typedef struct TransactedSharedImpl
202 struct StorageBaseImpl base
;
205 * Snapshot and uncommitted changes go here.
207 TransactedSnapshotImpl
*scratch
;
210 * Changes are committed to the transacted parent.
212 StorageBaseImpl
*transactedParent
;
214 /* The transaction signature from when we last committed */
215 ULONG lastTransactionSig
;
216 } TransactedSharedImpl
;
218 /* Generic function to create a transacted wrapper for a direct storage object. */
219 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, BOOL toplevel
, StorageBaseImpl
** result
);
221 /* OLESTREAM memory structure to use for Get and Put Routines */
222 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
227 DWORD dwOleTypeNameLength
;
228 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
229 CHAR
*pstrOleObjFileName
;
230 DWORD dwOleObjFileNameLength
;
231 DWORD dwMetaFileWidth
;
232 DWORD dwMetaFileHeight
;
233 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
236 }OLECONVERT_OLESTREAM_DATA
;
238 /* CompObj Stream structure */
239 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
244 DWORD dwCLSIDNameLength
;
245 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
246 DWORD dwOleTypeNameLength
;
247 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
248 DWORD dwProgIDNameLength
;
249 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
251 }OLECONVERT_ISTORAGE_COMPOBJ
;
254 /* Ole Presentation Stream structure */
255 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
263 }OLECONVERT_ISTORAGE_OLEPRES
;
267 /***********************************************************************
268 * Forward declaration of internal functions used by the method DestroyElement
270 static HRESULT
deleteStorageContents(
271 StorageBaseImpl
*parentStorage
,
272 DirRef indexToDelete
,
273 DirEntry entryDataToDelete
);
275 static HRESULT
deleteStreamContents(
276 StorageBaseImpl
*parentStorage
,
277 DirRef indexToDelete
,
278 DirEntry entryDataToDelete
);
280 static HRESULT
removeFromTree(
281 StorageBaseImpl
*This
,
282 DirRef parentStorageIndex
,
283 DirRef deletedIndex
);
285 /***********************************************************************
286 * Declaration of the functions used to manipulate DirEntry
289 static HRESULT
insertIntoTree(
290 StorageBaseImpl
*This
,
291 DirRef parentStorageIndex
,
292 DirRef newEntryIndex
);
294 static LONG
entryNameCmp(
295 const OLECHAR
*name1
,
296 const OLECHAR
*name2
);
298 static DirRef
findElement(
299 StorageBaseImpl
*storage
,
304 static HRESULT
findTreeParent(
305 StorageBaseImpl
*storage
,
307 const OLECHAR
*childName
,
308 DirEntry
*parentData
,
312 /***********************************************************************
313 * Declaration of miscellaneous functions...
315 static HRESULT
validateSTGM(DWORD stgmValue
);
317 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
318 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
319 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
321 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
324 /****************************************************************************
325 * IEnumSTATSTGImpl definitions.
327 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
328 * This class allows iterating through the content of a storage and to find
329 * specific items inside it.
331 struct IEnumSTATSTGImpl
333 IEnumSTATSTG IEnumSTATSTG_iface
;
335 LONG ref
; /* Reference count */
336 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
337 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
339 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
342 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
344 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
348 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
349 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
351 /************************************************************************
355 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
357 return (index
+1) * This
->bigBlockSize
;
360 /************************************************************************
361 ** Storage32BaseImpl implementation
363 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
364 ULARGE_INTEGER offset
,
369 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
372 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
373 ULARGE_INTEGER offset
,
378 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
381 /************************************************************************
382 * Storage32BaseImpl_QueryInterface (IUnknown)
384 * This method implements the common QueryInterface for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
394 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
401 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
402 IsEqualGUID(&IID_IStorage
, riid
))
404 *ppvObject
= &This
->IStorage_iface
;
406 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
408 *ppvObject
= &This
->IPropertySetStorage_iface
;
410 /* locking interface is reported for writer only */
411 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
413 *ppvObject
= &This
->IDirectWriterLock_iface
;
416 return E_NOINTERFACE
;
418 IStorage_AddRef(iface
);
423 /************************************************************************
424 * Storage32BaseImpl_AddRef (IUnknown)
426 * This method implements the common AddRef for all IStorage32
427 * implementations contained in this file.
429 * See Windows documentation for more details on IUnknown methods.
431 static ULONG WINAPI
StorageBaseImpl_AddRef(
434 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
435 ULONG ref
= InterlockedIncrement(&This
->ref
);
437 TRACE("(%p) AddRef to %d\n", This
, ref
);
442 /************************************************************************
443 * Storage32BaseImpl_Release (IUnknown)
445 * This method implements the common Release for all IStorage32
446 * implementations contained in this file.
448 * See Windows documentation for more details on IUnknown methods.
450 static ULONG WINAPI
StorageBaseImpl_Release(
453 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
455 ULONG ref
= InterlockedDecrement(&This
->ref
);
457 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
462 * Since we are using a system of base-classes, we want to call the
463 * destructor of the appropriate derived class. To do this, we are
464 * using virtual functions to implement the destructor.
466 StorageBaseImpl_Destroy(This
);
472 /************************************************************************
473 * Storage32BaseImpl_OpenStream (IStorage)
475 * This method will open the specified stream object from the current storage.
477 * See Windows documentation for more details on IStorage methods.
479 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
481 const OLECHAR
* pwcsName
, /* [string][in] */
482 void* reserved1
, /* [unique][in] */
483 DWORD grfMode
, /* [in] */
484 DWORD reserved2
, /* [in] */
485 IStream
** ppstm
) /* [out] */
487 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
488 StgStreamImpl
* newStream
;
489 DirEntry currentEntry
;
490 DirRef streamEntryRef
;
491 HRESULT res
= STG_E_UNKNOWN
;
493 TRACE("(%p, %s, %p, %x, %d, %p)\n",
494 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
496 if ( (pwcsName
==NULL
) || (ppstm
==0) )
504 if ( FAILED( validateSTGM(grfMode
) ) ||
505 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
507 res
= STG_E_INVALIDFLAG
;
514 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
516 res
= STG_E_INVALIDFUNCTION
;
522 res
= STG_E_REVERTED
;
527 * Check that we're compatible with the parent's storage mode, but
528 * only if we are not in transacted mode
530 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
531 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
533 res
= STG_E_INVALIDFLAG
;
539 * Search for the element with the given name
541 streamEntryRef
= findElement(
543 This
->storageDirEntry
,
548 * If it was found, construct the stream object and return a pointer to it.
550 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
551 (currentEntry
.stgType
==STGTY_STREAM
) )
553 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
555 /* A single stream cannot be opened a second time. */
556 res
= STG_E_ACCESSDENIED
;
560 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
564 newStream
->grfMode
= grfMode
;
565 *ppstm
= &newStream
->IStream_iface
;
567 IStream_AddRef(*ppstm
);
577 res
= STG_E_FILENOTFOUND
;
581 TRACE("<-- IStream %p\n", *ppstm
);
582 TRACE("<-- %08x\n", res
);
586 /************************************************************************
587 * Storage32BaseImpl_OpenStorage (IStorage)
589 * This method will open a new storage object from the current storage.
591 * See Windows documentation for more details on IStorage methods.
593 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
595 const OLECHAR
* pwcsName
, /* [string][unique][in] */
596 IStorage
* pstgPriority
, /* [unique][in] */
597 DWORD grfMode
, /* [in] */
598 SNB snbExclude
, /* [unique][in] */
599 DWORD reserved
, /* [in] */
600 IStorage
** ppstg
) /* [out] */
602 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
603 StorageInternalImpl
* newStorage
;
604 StorageBaseImpl
* newTransactedStorage
;
605 DirEntry currentEntry
;
606 DirRef storageEntryRef
;
607 HRESULT res
= STG_E_UNKNOWN
;
609 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
610 iface
, debugstr_w(pwcsName
), pstgPriority
,
611 grfMode
, snbExclude
, reserved
, ppstg
);
613 if ((pwcsName
==NULL
) || (ppstg
==0) )
619 if (This
->openFlags
& STGM_SIMPLE
)
621 res
= STG_E_INVALIDFUNCTION
;
626 if (snbExclude
!= NULL
)
628 res
= STG_E_INVALIDPARAMETER
;
632 if ( FAILED( validateSTGM(grfMode
) ))
634 res
= STG_E_INVALIDFLAG
;
641 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
642 (grfMode
& STGM_DELETEONRELEASE
) ||
643 (grfMode
& STGM_PRIORITY
) )
645 res
= STG_E_INVALIDFUNCTION
;
650 return STG_E_REVERTED
;
653 * Check that we're compatible with the parent's storage mode,
654 * but only if we are not transacted
656 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
657 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
659 res
= STG_E_ACCESSDENIED
;
666 storageEntryRef
= findElement(
668 This
->storageDirEntry
,
672 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
673 (currentEntry
.stgType
==STGTY_STORAGE
) )
675 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
677 /* A single storage cannot be opened a second time. */
678 res
= STG_E_ACCESSDENIED
;
682 newStorage
= StorageInternalImpl_Construct(
689 if (grfMode
& STGM_TRANSACTED
)
691 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
695 HeapFree(GetProcessHeap(), 0, newStorage
);
699 *ppstg
= &newTransactedStorage
->IStorage_iface
;
703 *ppstg
= &newStorage
->base
.IStorage_iface
;
706 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
712 res
= STG_E_INSUFFICIENTMEMORY
;
716 res
= STG_E_FILENOTFOUND
;
719 TRACE("<-- %08x\n", res
);
723 /************************************************************************
724 * Storage32BaseImpl_EnumElements (IStorage)
726 * This method will create an enumerator object that can be used to
727 * retrieve information about all the elements in the storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
733 DWORD reserved1
, /* [in] */
734 void* reserved2
, /* [size_is][unique][in] */
735 DWORD reserved3
, /* [in] */
736 IEnumSTATSTG
** ppenum
) /* [out] */
738 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
739 IEnumSTATSTGImpl
* newEnum
;
741 TRACE("(%p, %d, %p, %d, %p)\n",
742 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
748 return STG_E_REVERTED
;
750 newEnum
= IEnumSTATSTGImpl_Construct(
752 This
->storageDirEntry
);
756 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
760 return E_OUTOFMEMORY
;
763 /************************************************************************
764 * Storage32BaseImpl_Stat (IStorage)
766 * This method will retrieve information about this storage object.
768 * See Windows documentation for more details on IStorage methods.
770 static HRESULT WINAPI
StorageBaseImpl_Stat(
772 STATSTG
* pstatstg
, /* [out] */
773 DWORD grfStatFlag
) /* [in] */
775 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
776 DirEntry currentEntry
;
777 HRESULT res
= STG_E_UNKNOWN
;
779 TRACE("(%p, %p, %x)\n",
780 iface
, pstatstg
, grfStatFlag
);
790 res
= STG_E_REVERTED
;
794 res
= StorageBaseImpl_ReadDirEntry(
796 This
->storageDirEntry
,
801 StorageUtl_CopyDirEntryToSTATSTG(
807 pstatstg
->grfMode
= This
->openFlags
;
808 pstatstg
->grfStateBits
= This
->stateBits
;
814 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
816 TRACE("<-- %08x\n", res
);
820 /************************************************************************
821 * Storage32BaseImpl_RenameElement (IStorage)
823 * This method will rename the specified element.
825 * See Windows documentation for more details on IStorage methods.
827 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
829 const OLECHAR
* pwcsOldName
, /* [in] */
830 const OLECHAR
* pwcsNewName
) /* [in] */
832 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
833 DirEntry currentEntry
;
834 DirRef currentEntryRef
;
836 TRACE("(%p, %s, %s)\n",
837 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
840 return STG_E_REVERTED
;
842 currentEntryRef
= findElement(This
,
843 This
->storageDirEntry
,
847 if (currentEntryRef
!= DIRENTRY_NULL
)
850 * There is already an element with the new name
852 return STG_E_FILEALREADYEXISTS
;
856 * Search for the old element name
858 currentEntryRef
= findElement(This
,
859 This
->storageDirEntry
,
863 if (currentEntryRef
!= DIRENTRY_NULL
)
865 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
866 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
868 WARN("Element is already open; cannot rename.\n");
869 return STG_E_ACCESSDENIED
;
872 /* Remove the element from its current position in the tree */
873 removeFromTree(This
, This
->storageDirEntry
,
876 /* Change the name of the element */
877 strcpyW(currentEntry
.name
, pwcsNewName
);
879 /* Delete any sibling links */
880 currentEntry
.leftChild
= DIRENTRY_NULL
;
881 currentEntry
.rightChild
= DIRENTRY_NULL
;
883 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
886 /* Insert the element in a new position in the tree */
887 insertIntoTree(This
, This
->storageDirEntry
,
893 * There is no element with the old name
895 return STG_E_FILENOTFOUND
;
898 return StorageBaseImpl_Flush(This
);
901 /************************************************************************
902 * Storage32BaseImpl_CreateStream (IStorage)
904 * This method will create a stream object within this storage
906 * See Windows documentation for more details on IStorage methods.
908 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
910 const OLECHAR
* pwcsName
, /* [string][in] */
911 DWORD grfMode
, /* [in] */
912 DWORD reserved1
, /* [in] */
913 DWORD reserved2
, /* [in] */
914 IStream
** ppstm
) /* [out] */
916 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
917 StgStreamImpl
* newStream
;
918 DirEntry currentEntry
, newStreamEntry
;
919 DirRef currentEntryRef
, newStreamEntryRef
;
922 TRACE("(%p, %s, %x, %d, %d, %p)\n",
923 iface
, debugstr_w(pwcsName
), grfMode
,
924 reserved1
, reserved2
, ppstm
);
927 return STG_E_INVALIDPOINTER
;
930 return STG_E_INVALIDNAME
;
932 if (reserved1
|| reserved2
)
933 return STG_E_INVALIDPARAMETER
;
935 if ( FAILED( validateSTGM(grfMode
) ))
936 return STG_E_INVALIDFLAG
;
938 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
939 return STG_E_INVALIDFLAG
;
942 return STG_E_REVERTED
;
947 if ((grfMode
& STGM_DELETEONRELEASE
) ||
948 (grfMode
& STGM_TRANSACTED
))
949 return STG_E_INVALIDFUNCTION
;
952 * Don't worry about permissions in transacted mode, as we can always write
953 * changes; we just can't always commit them.
955 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
956 /* Can't create a stream on read-only storage */
957 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
958 return STG_E_ACCESSDENIED
;
960 /* Can't create a stream with greater access than the parent. */
961 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
962 return STG_E_ACCESSDENIED
;
965 if(This
->openFlags
& STGM_SIMPLE
)
966 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
970 currentEntryRef
= findElement(This
,
971 This
->storageDirEntry
,
975 if (currentEntryRef
!= DIRENTRY_NULL
)
978 * An element with this name already exists
980 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
982 IStorage_DestroyElement(iface
, pwcsName
);
985 return STG_E_FILEALREADYEXISTS
;
989 * memset the empty entry
991 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
993 newStreamEntry
.sizeOfNameString
=
994 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
996 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
997 return STG_E_INVALIDNAME
;
999 strcpyW(newStreamEntry
.name
, pwcsName
);
1001 newStreamEntry
.stgType
= STGTY_STREAM
;
1002 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1003 newStreamEntry
.size
.u
.LowPart
= 0;
1004 newStreamEntry
.size
.u
.HighPart
= 0;
1006 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
1007 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
1008 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
1010 /* call CoFileTime to get the current time
1011 newStreamEntry.ctime
1012 newStreamEntry.mtime
1015 /* newStreamEntry.clsid */
1018 * Create an entry with the new data
1020 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
1025 * Insert the new entry in the parent storage's tree.
1027 hr
= insertIntoTree(
1029 This
->storageDirEntry
,
1033 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1038 * Open the stream to return it.
1040 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1044 *ppstm
= &newStream
->IStream_iface
;
1045 IStream_AddRef(*ppstm
);
1049 return STG_E_INSUFFICIENTMEMORY
;
1052 return StorageBaseImpl_Flush(This
);
1055 /************************************************************************
1056 * Storage32BaseImpl_SetClass (IStorage)
1058 * This method will write the specified CLSID in the directory entry of this
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1065 REFCLSID clsid
) /* [in] */
1067 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1069 DirEntry currentEntry
;
1071 TRACE("(%p, %p)\n", iface
, clsid
);
1074 return STG_E_REVERTED
;
1076 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1077 This
->storageDirEntry
,
1079 if (SUCCEEDED(hRes
))
1081 currentEntry
.clsid
= *clsid
;
1083 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1084 This
->storageDirEntry
,
1088 if (SUCCEEDED(hRes
))
1089 hRes
= StorageBaseImpl_Flush(This
);
1094 /************************************************************************
1095 ** Storage32Impl implementation
1098 /************************************************************************
1099 * Storage32BaseImpl_CreateStorage (IStorage)
1101 * This method will create the storage object within the provided storage.
1103 * See Windows documentation for more details on IStorage methods.
1105 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1107 const OLECHAR
*pwcsName
, /* [string][in] */
1108 DWORD grfMode
, /* [in] */
1109 DWORD reserved1
, /* [in] */
1110 DWORD reserved2
, /* [in] */
1111 IStorage
**ppstg
) /* [out] */
1113 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1115 DirEntry currentEntry
;
1117 DirRef currentEntryRef
;
1121 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1122 iface
, debugstr_w(pwcsName
), grfMode
,
1123 reserved1
, reserved2
, ppstg
);
1126 return STG_E_INVALIDPOINTER
;
1128 if (This
->openFlags
& STGM_SIMPLE
)
1130 return STG_E_INVALIDFUNCTION
;
1134 return STG_E_INVALIDNAME
;
1138 if ( FAILED( validateSTGM(grfMode
) ) ||
1139 (grfMode
& STGM_DELETEONRELEASE
) )
1141 WARN("bad grfMode: 0x%x\n", grfMode
);
1142 return STG_E_INVALIDFLAG
;
1146 return STG_E_REVERTED
;
1149 * Check that we're compatible with the parent's storage mode
1151 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1152 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1154 WARN("access denied\n");
1155 return STG_E_ACCESSDENIED
;
1158 currentEntryRef
= findElement(This
,
1159 This
->storageDirEntry
,
1163 if (currentEntryRef
!= DIRENTRY_NULL
)
1166 * An element with this name already exists
1168 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1169 ((This
->openFlags
& STGM_TRANSACTED
) ||
1170 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1172 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1178 WARN("file already exists\n");
1179 return STG_E_FILEALREADYEXISTS
;
1182 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1183 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1185 WARN("read-only storage\n");
1186 return STG_E_ACCESSDENIED
;
1189 memset(&newEntry
, 0, sizeof(DirEntry
));
1191 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1193 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1195 FIXME("name too long\n");
1196 return STG_E_INVALIDNAME
;
1199 strcpyW(newEntry
.name
, pwcsName
);
1201 newEntry
.stgType
= STGTY_STORAGE
;
1202 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1203 newEntry
.size
.u
.LowPart
= 0;
1204 newEntry
.size
.u
.HighPart
= 0;
1206 newEntry
.leftChild
= DIRENTRY_NULL
;
1207 newEntry
.rightChild
= DIRENTRY_NULL
;
1208 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1210 /* call CoFileTime to get the current time
1215 /* newEntry.clsid */
1218 * Create a new directory entry for the storage
1220 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1225 * Insert the new directory entry into the parent storage's tree
1227 hr
= insertIntoTree(
1229 This
->storageDirEntry
,
1233 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1238 * Open it to get a pointer to return.
1240 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1242 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1248 hr
= StorageBaseImpl_Flush(This
);
1254 /***************************************************************************
1258 * Reserve a directory entry in the file and initialize it.
1260 static HRESULT
StorageImpl_CreateDirEntry(
1261 StorageBaseImpl
*base
,
1262 const DirEntry
*newData
,
1265 StorageImpl
*storage
= (StorageImpl
*)base
;
1266 ULONG currentEntryIndex
= 0;
1267 ULONG newEntryIndex
= DIRENTRY_NULL
;
1269 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1270 WORD sizeOfNameString
;
1274 hr
= StorageImpl_ReadRawDirEntry(storage
,
1280 StorageUtl_ReadWord(
1282 OFFSET_PS_NAMELENGTH
,
1285 if (sizeOfNameString
== 0)
1288 * The entry exists and is available, we found it.
1290 newEntryIndex
= currentEntryIndex
;
1296 * We exhausted the directory entries, we will create more space below
1298 newEntryIndex
= currentEntryIndex
;
1300 currentEntryIndex
++;
1302 } while (newEntryIndex
== DIRENTRY_NULL
);
1305 * grow the directory stream
1309 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1310 ULARGE_INTEGER newSize
;
1312 ULONG lastEntry
= 0;
1313 ULONG blockCount
= 0;
1316 * obtain the new count of blocks in the directory stream
1318 blockCount
= BlockChainStream_GetCount(
1319 storage
->rootBlockChain
)+1;
1322 * initialize the size used by the directory stream
1324 newSize
.u
.HighPart
= 0;
1325 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1328 * add a block to the directory stream
1330 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1333 * memset the empty entry in order to initialize the unused newly
1336 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1341 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1344 entryIndex
= newEntryIndex
+ 1;
1345 entryIndex
< lastEntry
;
1348 StorageImpl_WriteRawDirEntry(
1354 StorageImpl_SaveFileHeader(storage
);
1357 UpdateRawDirEntry(currentData
, newData
);
1359 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1362 *index
= newEntryIndex
;
1367 /***************************************************************************
1371 * Mark a directory entry in the file as free.
1373 static HRESULT
StorageImpl_DestroyDirEntry(
1374 StorageBaseImpl
*base
,
1377 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1378 StorageImpl
*storage
= (StorageImpl
*)base
;
1380 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1382 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1386 /****************************************************************************
1390 * Case insensitive comparison of DirEntry.name by first considering
1393 * Returns <0 when name1 < name2
1394 * >0 when name1 > name2
1395 * 0 when name1 == name2
1397 static LONG
entryNameCmp(
1398 const OLECHAR
*name1
,
1399 const OLECHAR
*name2
)
1401 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1403 while (diff
== 0 && *name1
!= 0)
1406 * We compare the string themselves only when they are of the same length
1408 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1414 /****************************************************************************
1418 * Add a directory entry to a storage
1420 static HRESULT
insertIntoTree(
1421 StorageBaseImpl
*This
,
1422 DirRef parentStorageIndex
,
1423 DirRef newEntryIndex
)
1425 DirEntry currentEntry
;
1429 * Read the inserted entry
1431 StorageBaseImpl_ReadDirEntry(This
,
1436 * Read the storage entry
1438 StorageBaseImpl_ReadDirEntry(This
,
1442 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1445 * The root storage contains some element, therefore, start the research
1446 * for the appropriate location.
1449 DirRef current
, next
, previous
, currentEntryId
;
1452 * Keep a reference to the root of the storage's element tree
1454 currentEntryId
= currentEntry
.dirRootEntry
;
1459 StorageBaseImpl_ReadDirEntry(This
,
1460 currentEntry
.dirRootEntry
,
1463 previous
= currentEntry
.leftChild
;
1464 next
= currentEntry
.rightChild
;
1465 current
= currentEntryId
;
1469 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1473 if (previous
!= DIRENTRY_NULL
)
1475 StorageBaseImpl_ReadDirEntry(This
,
1482 currentEntry
.leftChild
= newEntryIndex
;
1483 StorageBaseImpl_WriteDirEntry(This
,
1491 if (next
!= DIRENTRY_NULL
)
1493 StorageBaseImpl_ReadDirEntry(This
,
1500 currentEntry
.rightChild
= newEntryIndex
;
1501 StorageBaseImpl_WriteDirEntry(This
,
1510 * Trying to insert an item with the same name in the
1511 * subtree structure.
1513 return STG_E_FILEALREADYEXISTS
;
1516 previous
= currentEntry
.leftChild
;
1517 next
= currentEntry
.rightChild
;
1523 * The storage is empty, make the new entry the root of its element tree
1525 currentEntry
.dirRootEntry
= newEntryIndex
;
1526 StorageBaseImpl_WriteDirEntry(This
,
1534 /****************************************************************************
1538 * Find and read the element of a storage with the given name.
1540 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1541 const OLECHAR
*name
, DirEntry
*data
)
1543 DirRef currentEntry
;
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1548 currentEntry
= data
->dirRootEntry
;
1550 while (currentEntry
!= DIRENTRY_NULL
)
1554 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1556 cmp
= entryNameCmp(name
, data
->name
);
1563 currentEntry
= data
->leftChild
;
1566 currentEntry
= data
->rightChild
;
1569 return currentEntry
;
1572 /****************************************************************************
1576 * Find and read the binary tree parent of the element with the given name.
1578 * If there is no such element, find a place where it could be inserted and
1579 * return STG_E_FILENOTFOUND.
1581 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1582 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1588 /* Read the storage entry to find the root of the tree. */
1589 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1591 *parentEntry
= storageEntry
;
1592 *relation
= DIRENTRY_RELATION_DIR
;
1594 childEntry
= parentData
->dirRootEntry
;
1596 while (childEntry
!= DIRENTRY_NULL
)
1600 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1602 cmp
= entryNameCmp(childName
, childData
.name
);
1610 *parentData
= childData
;
1611 *parentEntry
= childEntry
;
1612 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1614 childEntry
= parentData
->leftChild
;
1619 *parentData
= childData
;
1620 *parentEntry
= childEntry
;
1621 *relation
= DIRENTRY_RELATION_NEXT
;
1623 childEntry
= parentData
->rightChild
;
1627 if (childEntry
== DIRENTRY_NULL
)
1628 return STG_E_FILENOTFOUND
;
1634 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1635 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1636 SNB snbExclude
, IStorage
*pstgDest
);
1638 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1639 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1640 SNB snbExclude
, IStorage
*pstgDest
)
1646 IStream
*pstrChild
, *pstrTmp
;
1649 if (srcEntry
== DIRENTRY_NULL
)
1652 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1659 WCHAR
**snb
= snbExclude
;
1661 while ( *snb
!= NULL
&& !skip
)
1663 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1671 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1674 * create a new storage in destination storage
1676 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1677 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1682 * if it already exist, don't create a new one use this one
1684 if (hr
== STG_E_FILEALREADYEXISTS
)
1686 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1687 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1688 NULL
, 0, &pstgTmp
);
1693 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1694 skip_stream
, NULL
, pstgTmp
);
1696 IStorage_Release(pstgTmp
);
1699 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1702 * create a new stream in destination storage. If the stream already
1703 * exist, it will be deleted and a new one will be created.
1705 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1706 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1710 * open child stream storage. This operation must succeed even if the
1711 * stream is already open, so we use internal functions to do it.
1715 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1719 pstrChild
= &streamimpl
->IStream_iface
;
1721 IStream_AddRef(pstrChild
);
1733 * Get the size of the source stream
1735 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1738 * Set the size of the destination stream.
1740 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1745 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1748 IStream_Release( pstrChild
);
1751 IStream_Release( pstrTmp
);
1757 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1758 skip_stream
, snbExclude
, pstgDest
);
1761 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1762 skip_stream
, snbExclude
, pstgDest
);
1767 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1768 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1769 SNB snbExclude
, IStorage
*pstgDest
)
1774 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1777 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1780 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1781 skip_stream
, snbExclude
, pstgDest
);
1786 /*************************************************************************
1789 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1791 DWORD ciidExclude
, /* [in] */
1792 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1793 SNB snbExclude
, /* [unique][in] */
1794 IStorage
* pstgDest
) /* [unique][in] */
1796 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1798 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1801 TRACE("(%p, %d, %p, %p, %p)\n",
1802 iface
, ciidExclude
, rgiidExclude
,
1803 snbExclude
, pstgDest
);
1805 if ( pstgDest
== 0 )
1806 return STG_E_INVALIDPOINTER
;
1808 for(i
= 0; i
< ciidExclude
; ++i
)
1810 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1811 skip_storage
= TRUE
;
1812 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1815 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1820 /* Give up early if it looks like this would be infinitely recursive.
1821 * Oddly enough, this includes some cases that aren't really recursive, like
1822 * copying to a transacted child. */
1823 IStorage
*pstgDestAncestor
= pstgDest
;
1824 IStorage
*pstgDestAncestorChild
= NULL
;
1826 /* Go up the chain from the destination until we find the source storage. */
1827 while (pstgDestAncestor
!= iface
) {
1828 pstgDestAncestorChild
= pstgDest
;
1830 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1832 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1834 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1836 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1838 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1840 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1846 if (pstgDestAncestor
== iface
)
1850 if (pstgDestAncestorChild
&& snbExclude
)
1852 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1854 WCHAR
**snb
= snbExclude
;
1856 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1858 while ( *snb
!= NULL
&& fail
)
1860 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1867 return STG_E_ACCESSDENIED
;
1871 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1872 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1875 /*************************************************************************
1876 * MoveElementTo (IStorage)
1878 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1880 const OLECHAR
*pwcsName
, /* [string][in] */
1881 IStorage
*pstgDest
, /* [unique][in] */
1882 const OLECHAR
*pwcsNewName
,/* [string][in] */
1883 DWORD grfFlags
) /* [in] */
1885 FIXME("(%p %s %p %s %u): stub\n", iface
,
1886 debugstr_w(pwcsName
), pstgDest
,
1887 debugstr_w(pwcsNewName
), grfFlags
);
1891 /*************************************************************************
1894 * Ensures that any changes made to a storage object open in transacted mode
1895 * are reflected in the parent storage
1897 * In a non-transacted mode, this ensures all cached writes are completed.
1899 static HRESULT WINAPI
StorageImpl_Commit(
1901 DWORD grfCommitFlags
)/* [in] */
1903 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1904 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1905 return StorageBaseImpl_Flush(This
);
1908 /*************************************************************************
1911 * Discard all changes that have been made since the last commit operation
1913 static HRESULT WINAPI
StorageImpl_Revert(
1916 TRACE("(%p)\n", iface
);
1920 /*************************************************************************
1921 * DestroyElement (IStorage)
1923 * Strategy: This implementation is built this way for simplicity not for speed.
1924 * I always delete the topmost element of the enumeration and adjust
1925 * the deleted element pointer all the time. This takes longer to
1926 * do but allow to reinvoke DestroyElement whenever we encounter a
1927 * storage object. The optimisation resides in the usage of another
1928 * enumeration strategy that would give all the leaves of a storage
1929 * first. (postfix order)
1931 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1933 const OLECHAR
*pwcsName
)/* [string][in] */
1935 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1938 DirEntry entryToDelete
;
1939 DirRef entryToDeleteRef
;
1942 iface
, debugstr_w(pwcsName
));
1945 return STG_E_INVALIDPOINTER
;
1948 return STG_E_REVERTED
;
1950 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1951 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1952 return STG_E_ACCESSDENIED
;
1954 entryToDeleteRef
= findElement(
1956 This
->storageDirEntry
,
1960 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1962 return STG_E_FILENOTFOUND
;
1965 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1967 hr
= deleteStorageContents(
1972 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1974 hr
= deleteStreamContents(
1984 * Remove the entry from its parent storage
1986 hr
= removeFromTree(
1988 This
->storageDirEntry
,
1992 * Invalidate the entry
1995 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1998 hr
= StorageBaseImpl_Flush(This
);
2004 /******************************************************************************
2005 * Internal stream list handlers
2008 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2010 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2011 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2014 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2016 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2017 list_remove(&(strm
->StrmListEntry
));
2020 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
2022 StgStreamImpl
*strm
;
2024 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
2026 if (strm
->dirEntry
== streamEntry
)
2035 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2037 StorageInternalImpl
*childstg
;
2039 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2041 if (childstg
->base
.storageDirEntry
== storageEntry
)
2050 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2052 struct list
*cur
, *cur2
;
2053 StgStreamImpl
*strm
=NULL
;
2054 StorageInternalImpl
*childstg
=NULL
;
2056 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2057 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2058 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2059 strm
->parentStorage
= NULL
;
2063 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2064 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2065 StorageBaseImpl_Invalidate( &childstg
->base
);
2068 if (stg
->transactedChild
)
2070 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2072 stg
->transactedChild
= NULL
;
2077 /*********************************************************************
2081 * Delete the contents of a storage entry.
2084 static HRESULT
deleteStorageContents(
2085 StorageBaseImpl
*parentStorage
,
2086 DirRef indexToDelete
,
2087 DirEntry entryDataToDelete
)
2089 IEnumSTATSTG
*elements
= 0;
2090 IStorage
*childStorage
= 0;
2091 STATSTG currentElement
;
2093 HRESULT destroyHr
= S_OK
;
2094 StorageInternalImpl
*stg
, *stg2
;
2096 /* Invalidate any open storage objects. */
2097 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2099 if (stg
->base
.storageDirEntry
== indexToDelete
)
2101 StorageBaseImpl_Invalidate(&stg
->base
);
2106 * Open the storage and enumerate it
2108 hr
= IStorage_OpenStorage(
2109 &parentStorage
->IStorage_iface
,
2110 entryDataToDelete
.name
,
2112 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2123 * Enumerate the elements
2125 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2130 * Obtain the next element
2132 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2135 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2137 CoTaskMemFree(currentElement
.pwcsName
);
2141 * We need to Reset the enumeration every time because we delete elements
2142 * and the enumeration could be invalid
2144 IEnumSTATSTG_Reset(elements
);
2146 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2148 IStorage_Release(childStorage
);
2149 IEnumSTATSTG_Release(elements
);
2154 /*********************************************************************
2158 * Perform the deletion of a stream's data
2161 static HRESULT
deleteStreamContents(
2162 StorageBaseImpl
*parentStorage
,
2163 DirRef indexToDelete
,
2164 DirEntry entryDataToDelete
)
2168 ULARGE_INTEGER size
;
2169 StgStreamImpl
*strm
, *strm2
;
2171 /* Invalidate any open stream objects. */
2172 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2174 if (strm
->dirEntry
== indexToDelete
)
2176 TRACE("Stream deleted %p\n", strm
);
2177 strm
->parentStorage
= NULL
;
2178 list_remove(&strm
->StrmListEntry
);
2182 size
.u
.HighPart
= 0;
2185 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2186 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2196 hr
= IStream_SetSize(pis
, size
);
2204 * Release the stream object.
2206 IStream_Release(pis
);
2211 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2215 case DIRENTRY_RELATION_PREVIOUS
:
2216 entry
->leftChild
= new_target
;
2218 case DIRENTRY_RELATION_NEXT
:
2219 entry
->rightChild
= new_target
;
2221 case DIRENTRY_RELATION_DIR
:
2222 entry
->dirRootEntry
= new_target
;
2229 /*************************************************************************
2233 * This method removes a directory entry from its parent storage tree without
2234 * freeing any resources attached to it.
2236 static HRESULT
removeFromTree(
2237 StorageBaseImpl
*This
,
2238 DirRef parentStorageIndex
,
2239 DirRef deletedIndex
)
2241 DirEntry entryToDelete
;
2242 DirEntry parentEntry
;
2243 DirRef parentEntryRef
;
2244 ULONG typeOfRelation
;
2247 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2253 * Find the element that links to the one we want to delete.
2255 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2256 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2261 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2264 * Replace the deleted entry with its left child
2266 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2268 hr
= StorageBaseImpl_WriteDirEntry(
2277 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2280 * We need to reinsert the right child somewhere. We already know it and
2281 * its children are greater than everything in the left tree, so we
2282 * insert it at the rightmost point in the left tree.
2284 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2285 DirEntry newRightChildParentEntry
;
2289 hr
= StorageBaseImpl_ReadDirEntry(
2291 newRightChildParent
,
2292 &newRightChildParentEntry
);
2298 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2299 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2300 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2302 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2304 hr
= StorageBaseImpl_WriteDirEntry(
2306 newRightChildParent
,
2307 &newRightChildParentEntry
);
2317 * Replace the deleted entry with its right child
2319 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2321 hr
= StorageBaseImpl_WriteDirEntry(
2335 /******************************************************************************
2336 * SetElementTimes (IStorage)
2338 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2340 const OLECHAR
*pwcsName
,/* [string][in] */
2341 const FILETIME
*pctime
, /* [in] */
2342 const FILETIME
*patime
, /* [in] */
2343 const FILETIME
*pmtime
) /* [in] */
2345 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2349 /******************************************************************************
2350 * SetStateBits (IStorage)
2352 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2354 DWORD grfStateBits
,/* [in] */
2355 DWORD grfMask
) /* [in] */
2357 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2360 return STG_E_REVERTED
;
2362 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2366 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2367 DirRef index
, const DirEntry
*data
)
2369 StorageImpl
*This
= (StorageImpl
*)base
;
2370 return StorageImpl_WriteDirEntry(This
, index
, data
);
2373 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2374 DirRef index
, DirEntry
*data
)
2376 StorageImpl
*This
= (StorageImpl
*)base
;
2377 return StorageImpl_ReadDirEntry(This
, index
, data
);
2380 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2384 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2386 if (!This
->blockChainCache
[i
])
2388 return &This
->blockChainCache
[i
];
2392 i
= This
->blockChainToEvict
;
2394 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2395 This
->blockChainCache
[i
] = NULL
;
2397 This
->blockChainToEvict
++;
2398 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2399 This
->blockChainToEvict
= 0;
2401 return &This
->blockChainCache
[i
];
2404 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2407 int i
, free_index
=-1;
2409 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2411 if (!This
->blockChainCache
[i
])
2413 if (free_index
== -1) free_index
= i
;
2415 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2417 return &This
->blockChainCache
[i
];
2421 if (free_index
== -1)
2423 free_index
= This
->blockChainToEvict
;
2425 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2426 This
->blockChainCache
[free_index
] = NULL
;
2428 This
->blockChainToEvict
++;
2429 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2430 This
->blockChainToEvict
= 0;
2433 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2434 return &This
->blockChainCache
[free_index
];
2437 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2441 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2443 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2445 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2446 This
->blockChainCache
[i
] = NULL
;
2452 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2453 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2455 StorageImpl
*This
= (StorageImpl
*)base
;
2460 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2461 if (FAILED(hr
)) return hr
;
2463 if (data
.size
.QuadPart
== 0)
2469 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2471 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2478 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2480 SmallBlockChainStream
*stream
;
2482 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2483 if (!stream
) return E_OUTOFMEMORY
;
2485 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2487 SmallBlockChainStream_Destroy(stream
);
2493 BlockChainStream
*stream
= NULL
;
2495 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2496 if (!stream
) return E_OUTOFMEMORY
;
2498 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2504 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2505 ULARGE_INTEGER newsize
)
2507 StorageImpl
*This
= (StorageImpl
*)base
;
2510 SmallBlockChainStream
*smallblock
=NULL
;
2511 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2513 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2514 if (FAILED(hr
)) return hr
;
2516 /* In simple mode keep the stream size above the small block limit */
2517 if (This
->base
.openFlags
& STGM_SIMPLE
)
2518 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2520 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2523 /* Create a block chain object of the appropriate type */
2524 if (data
.size
.QuadPart
== 0)
2526 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2528 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2529 if (!smallblock
) return E_OUTOFMEMORY
;
2533 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2534 bigblock
= *pbigblock
;
2535 if (!bigblock
) return E_OUTOFMEMORY
;
2538 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2540 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2541 if (!smallblock
) return E_OUTOFMEMORY
;
2545 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2546 bigblock
= *pbigblock
;
2547 if (!bigblock
) return E_OUTOFMEMORY
;
2550 /* Change the block chain type if necessary. */
2551 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2553 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2556 SmallBlockChainStream_Destroy(smallblock
);
2560 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2561 *pbigblock
= bigblock
;
2563 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2565 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2570 /* Set the size of the block chain. */
2573 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2574 SmallBlockChainStream_Destroy(smallblock
);
2578 BlockChainStream_SetSize(bigblock
, newsize
);
2581 /* Set the size in the directory entry. */
2582 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2585 data
.size
= newsize
;
2587 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2592 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2593 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2595 StorageImpl
*This
= (StorageImpl
*)base
;
2598 ULARGE_INTEGER newSize
;
2600 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2601 if (FAILED(hr
)) return hr
;
2603 /* Grow the stream if necessary */
2604 newSize
.QuadPart
= 0;
2605 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2607 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2609 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2613 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2614 if (FAILED(hr
)) return hr
;
2617 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2619 SmallBlockChainStream
*stream
;
2621 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2622 if (!stream
) return E_OUTOFMEMORY
;
2624 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2626 SmallBlockChainStream_Destroy(stream
);
2632 BlockChainStream
*stream
;
2634 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2635 if (!stream
) return E_OUTOFMEMORY
;
2637 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2641 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2644 StorageImpl
*This
= (StorageImpl
*)base
;
2645 DirEntry dst_data
, src_data
;
2648 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2651 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2655 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2656 dst_data
.startingBlock
= src_data
.startingBlock
;
2657 dst_data
.size
= src_data
.size
;
2659 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2665 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
2666 ULONG
* result
, BOOL refresh
)
2668 StorageImpl
*This
= (StorageImpl
*)base
;
2673 ULARGE_INTEGER offset
;
2677 offset
.u
.HighPart
= 0;
2678 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
2679 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
2683 /* FIXME: Throw out everything and reload the file if this changed. */
2684 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
2688 *result
= This
->transactionSig
;
2693 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
2696 StorageImpl
*This
= (StorageImpl
*)base
;
2698 This
->transactionSig
= value
;
2699 StorageImpl_SaveFileHeader(This
);
2704 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
2706 StorageImpl
*This
= (StorageImpl
*)base
;
2708 ULARGE_INTEGER offset
, cb
;
2712 /* Synchronous grab of second priority range, the commit lock, and the
2713 * lock-checking lock. */
2714 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2715 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2719 offset
.QuadPart
= RANGELOCK_COMMIT
;
2723 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2725 if (hr
== STG_E_INVALIDFUNCTION
)
2731 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
2733 StorageImpl
*This
= (StorageImpl
*)base
;
2735 ULARGE_INTEGER offset
, cb
;
2739 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2740 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2744 offset
.QuadPart
= RANGELOCK_COMMIT
;
2748 hr
= ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2750 if (hr
== STG_E_INVALIDFUNCTION
)
2756 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2758 StorageImpl
*This
= (StorageImpl
*) iface
;
2762 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2764 *result
= statstg
.pwcsName
;
2769 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
2771 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2772 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
2775 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
2777 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2778 return IStorage_AddRef(&This
->IStorage_iface
);
2781 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
2783 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2784 return IStorage_Release(&This
->IStorage_iface
);
2787 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
2789 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2790 FIXME("(%p)->(%d): stub\n", This
, timeout
);
2794 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
2796 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2797 FIXME("(%p): stub\n", This
);
2801 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
2803 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2804 FIXME("(%p): stub\n", This
);
2808 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
2810 directwriterlock_QueryInterface
,
2811 directwriterlock_AddRef
,
2812 directwriterlock_Release
,
2813 directwriterlock_WaitForWriteAccess
,
2814 directwriterlock_ReleaseWriteAccess
,
2815 directwriterlock_HaveWriteAccess
2819 * Virtual function table for the IStorage32Impl class.
2821 static const IStorageVtbl Storage32Impl_Vtbl
=
2823 StorageBaseImpl_QueryInterface
,
2824 StorageBaseImpl_AddRef
,
2825 StorageBaseImpl_Release
,
2826 StorageBaseImpl_CreateStream
,
2827 StorageBaseImpl_OpenStream
,
2828 StorageBaseImpl_CreateStorage
,
2829 StorageBaseImpl_OpenStorage
,
2830 StorageBaseImpl_CopyTo
,
2831 StorageBaseImpl_MoveElementTo
,
2834 StorageBaseImpl_EnumElements
,
2835 StorageBaseImpl_DestroyElement
,
2836 StorageBaseImpl_RenameElement
,
2837 StorageBaseImpl_SetElementTimes
,
2838 StorageBaseImpl_SetClass
,
2839 StorageBaseImpl_SetStateBits
,
2840 StorageBaseImpl_Stat
2843 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2845 StorageImpl_Destroy
,
2846 StorageImpl_Invalidate
,
2848 StorageImpl_GetFilename
,
2849 StorageImpl_CreateDirEntry
,
2850 StorageImpl_BaseWriteDirEntry
,
2851 StorageImpl_BaseReadDirEntry
,
2852 StorageImpl_DestroyDirEntry
,
2853 StorageImpl_StreamReadAt
,
2854 StorageImpl_StreamWriteAt
,
2855 StorageImpl_StreamSetSize
,
2856 StorageImpl_StreamLink
,
2857 StorageImpl_GetTransactionSig
,
2858 StorageImpl_SetTransactionSig
,
2859 StorageImpl_LockTransaction
,
2860 StorageImpl_UnlockTransaction
2863 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
2864 ULARGE_INTEGER cb
, DWORD dwLockType
)
2868 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2869 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This
->lockBytes
, offset
, cb
)))
2872 /* otherwise we have to fake it based on an async lock */
2877 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
2879 if (hr
== STG_E_ACCESSDENIED
)
2882 if (delay
< 150) delay
++;
2884 } while (hr
== STG_E_ACCESSDENIED
);
2889 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
2890 ULONG end
, HRESULT fail_hr
)
2893 ULARGE_INTEGER offset
, cb
;
2895 offset
.QuadPart
= start
;
2896 cb
.QuadPart
= 1 + end
- start
;
2898 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2899 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2901 if (hr
== STG_E_ACCESSDENIED
)
2907 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
2911 ULARGE_INTEGER offset
, cb
;
2915 for (i
=start
; i
<=end
; i
++)
2917 offset
.QuadPart
= i
;
2918 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2919 if (hr
!= STG_E_ACCESSDENIED
)
2925 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
2927 if (This
->locked_bytes
[j
] == 0)
2929 This
->locked_bytes
[j
] = i
;
2938 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
2941 ULARGE_INTEGER offset
;
2943 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
2945 if (openFlags
& STGM_NOSNAPSHOT
)
2947 /* STGM_NOSNAPSHOT implies deny write */
2948 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
2949 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
2952 /* Wrap all other locking inside a single lock so we can check ranges safely */
2953 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
2955 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2957 /* If the ILockBytes doesn't support locking that's ok. */
2958 if (FAILED(hr
)) return S_OK
;
2962 /* First check for any conflicting locks. */
2963 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2964 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
2966 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2967 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
2969 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2970 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
2972 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2973 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
2975 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2976 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
2978 /* Then grab our locks. */
2979 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2981 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
2983 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
2986 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2987 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
2989 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2990 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
2992 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2993 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
2995 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2996 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
2998 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
2999 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
3001 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
3003 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3008 static HRESULT
StorageImpl_Construct(
3016 StorageImpl
** result
)
3020 DirEntry currentEntry
;
3021 DirRef currentEntryRef
;
3023 if ( FAILED( validateSTGM(openFlags
) ))
3024 return STG_E_INVALIDFLAG
;
3026 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
3028 return E_OUTOFMEMORY
;
3030 memset(This
, 0, sizeof(StorageImpl
));
3032 list_init(&This
->base
.strmHead
);
3034 list_init(&This
->base
.storageHead
);
3036 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
3037 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
3038 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
3039 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
3040 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
3042 This
->base
.create
= create
;
3044 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
3045 This
->base
.lockingrole
= SWMR_Writer
;
3046 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
3047 This
->base
.lockingrole
= SWMR_Reader
;
3049 This
->base
.lockingrole
= SWMR_None
;
3051 This
->base
.reverted
= FALSE
;
3054 * Initialize the big block cache.
3056 This
->bigBlockSize
= sector_size
;
3057 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
3059 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
3062 This
->lockBytes
= pLkbyt
;
3063 ILockBytes_AddRef(pLkbyt
);
3069 hr
= StorageImpl_GrabLocks(This
, openFlags
);
3076 ULARGE_INTEGER size
;
3077 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
3079 /* Discard any existing data. */
3081 ILockBytes_SetSize(This
->lockBytes
, size
);
3084 * Initialize all header variables:
3085 * - The big block depot consists of one block and it is at block 0
3086 * - The directory table starts at block 1
3087 * - There is no small block depot
3089 memset( This
->bigBlockDepotStart
,
3091 sizeof(This
->bigBlockDepotStart
));
3093 This
->bigBlockDepotCount
= 1;
3094 This
->bigBlockDepotStart
[0] = 0;
3095 This
->rootStartBlock
= 1;
3096 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
3097 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3098 if (sector_size
== 4096)
3099 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
3101 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
3102 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
3103 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3104 This
->extBigBlockDepotCount
= 0;
3106 StorageImpl_SaveFileHeader(This
);
3109 * Add one block for the big block depot and one block for the directory table
3111 size
.u
.HighPart
= 0;
3112 size
.u
.LowPart
= This
->bigBlockSize
* 3;
3113 ILockBytes_SetSize(This
->lockBytes
, size
);
3116 * Initialize the big block depot
3118 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3119 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
3120 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
3121 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
3126 * Load the header for the file.
3128 hr
= StorageImpl_LoadFileHeader(This
);
3137 * There is no block depot cached yet.
3139 This
->indexBlockDepotCached
= 0xFFFFFFFF;
3140 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
3143 * Start searching for free blocks with block 0.
3145 This
->prevFreeBlock
= 0;
3147 This
->firstFreeSmallBlock
= 0;
3149 /* Read the extended big block depot locations. */
3150 if (This
->extBigBlockDepotCount
!= 0)
3152 ULONG current_block
= This
->extBigBlockDepotStart
;
3153 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
3156 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
3157 if (!This
->extBigBlockDepotLocations
)
3163 This
->extBigBlockDepotLocationsSize
= cache_size
;
3165 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
3167 if (current_block
== BLOCK_END_OF_CHAIN
)
3169 WARN("File has too few extended big block depot blocks.\n");
3170 hr
= STG_E_DOCFILECORRUPT
;
3173 This
->extBigBlockDepotLocations
[i
] = current_block
;
3174 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
3179 This
->extBigBlockDepotLocations
= NULL
;
3180 This
->extBigBlockDepotLocationsSize
= 0;
3184 * Create the block chain abstractions.
3186 if(!(This
->rootBlockChain
=
3187 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
3189 hr
= STG_E_READFAULT
;
3193 if(!(This
->smallBlockDepotChain
=
3194 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
3197 hr
= STG_E_READFAULT
;
3202 * Write the root storage entry (memory only)
3206 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3209 * Initialize the directory table
3211 memset(&rootEntry
, 0, sizeof(rootEntry
));
3212 strcpyW(rootEntry
.name
, rootentryW
);
3213 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
3214 rootEntry
.stgType
= STGTY_ROOT
;
3215 rootEntry
.leftChild
= DIRENTRY_NULL
;
3216 rootEntry
.rightChild
= DIRENTRY_NULL
;
3217 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
3218 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
3219 rootEntry
.size
.u
.HighPart
= 0;
3220 rootEntry
.size
.u
.LowPart
= 0;
3222 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
3226 * Find the ID of the root storage.
3228 currentEntryRef
= 0;
3232 hr
= StorageImpl_ReadDirEntry(
3239 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
3240 (currentEntry
.stgType
== STGTY_ROOT
) )
3242 This
->base
.storageDirEntry
= currentEntryRef
;
3248 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
3252 hr
= STG_E_READFAULT
;
3257 * Create the block chain abstraction for the small block root chain.
3259 if(!(This
->smallBlockRootChain
=
3260 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
3262 hr
= STG_E_READFAULT
;
3268 IStorage_Release(&This
->base
.IStorage_iface
);
3273 StorageImpl_Flush(&This
->base
);
3280 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
3282 StorageImpl
*This
= (StorageImpl
*) iface
;
3284 StorageBaseImpl_DeleteAll(&This
->base
);
3286 This
->base
.reverted
= TRUE
;
3289 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3291 StorageImpl
*This
= (StorageImpl
*) iface
;
3293 TRACE("(%p)\n", This
);
3295 StorageImpl_Flush(iface
);
3297 StorageImpl_Invalidate(iface
);
3299 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3301 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3302 BlockChainStream_Destroy(This
->rootBlockChain
);
3303 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3305 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3306 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3308 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
3310 ULARGE_INTEGER offset
, cb
;
3312 if (This
->locked_bytes
[i
] != 0)
3314 offset
.QuadPart
= This
->locked_bytes
[i
];
3315 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3319 if (This
->lockBytes
)
3320 ILockBytes_Release(This
->lockBytes
);
3321 HeapFree(GetProcessHeap(), 0, This
);
3324 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3326 StorageImpl
*This
= (StorageImpl
*)storage
;
3329 TRACE("(%p)\n", This
);
3331 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3334 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3337 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3339 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3340 if (This
->blockChainCache
[i
])
3341 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3344 hr
= ILockBytes_Flush(This
->lockBytes
);
3349 /******************************************************************************
3350 * Storage32Impl_GetNextFreeBigBlock
3352 * Returns the index of the next free big block.
3353 * If the big block depot is filled, this method will enlarge it.
3356 static ULONG
StorageImpl_GetNextFreeBigBlock(
3359 ULONG depotBlockIndexPos
;
3360 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3361 ULONG depotBlockOffset
;
3362 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3363 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3365 ULONG freeBlock
= BLOCK_UNUSED
;
3367 ULARGE_INTEGER neededSize
;
3370 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3371 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3374 * Scan the entire big block depot until we find a block marked free
3376 while (nextBlockIndex
!= BLOCK_UNUSED
)
3378 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3380 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3383 * Grow the primary depot.
3385 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3387 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3390 * Add a block depot.
3392 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3393 This
->bigBlockDepotCount
++;
3394 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3397 * Flag it as a block depot.
3399 StorageImpl_SetNextBlockInChain(This
,
3403 /* Save new header information.
3405 StorageImpl_SaveFileHeader(This
);
3410 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3412 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3415 * Grow the extended depot.
3417 ULONG extIndex
= BLOCK_UNUSED
;
3418 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3419 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3421 if (extBlockOffset
== 0)
3423 /* We need an extended block.
3425 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3426 This
->extBigBlockDepotCount
++;
3427 depotBlockIndexPos
= extIndex
+ 1;
3430 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3433 * Add a block depot and mark it in the extended block.
3435 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3436 This
->bigBlockDepotCount
++;
3437 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3439 /* Flag the block depot.
3441 StorageImpl_SetNextBlockInChain(This
,
3445 /* If necessary, flag the extended depot block.
3447 if (extIndex
!= BLOCK_UNUSED
)
3448 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3450 /* Save header information.
3452 StorageImpl_SaveFileHeader(This
);
3456 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3460 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3461 ( nextBlockIndex
!= BLOCK_UNUSED
))
3463 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3465 if (nextBlockIndex
== BLOCK_UNUSED
)
3467 freeBlock
= (depotIndex
* blocksPerDepot
) +
3468 (depotBlockOffset
/sizeof(ULONG
));
3471 depotBlockOffset
+= sizeof(ULONG
);
3476 depotBlockOffset
= 0;
3480 * make sure that the block physically exists before using it
3482 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3484 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3486 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3487 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3489 This
->prevFreeBlock
= freeBlock
;
3494 /******************************************************************************
3495 * Storage32Impl_AddBlockDepot
3497 * This will create a depot block, essentially it is a block initialized
3500 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3502 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3503 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3504 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3505 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3508 * Initialize blocks as free
3510 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3512 /* Reserve the range lock sector */
3513 if (depotIndex
== rangeLockDepot
)
3515 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3518 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3521 /******************************************************************************
3522 * Storage32Impl_GetExtDepotBlock
3524 * Returns the index of the block that corresponds to the specified depot
3525 * index. This method is only for depot indexes equal or greater than
3526 * COUNT_BBDEPOTINHEADER.
3528 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3530 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3531 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3532 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3533 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3534 ULONG blockIndex
= BLOCK_UNUSED
;
3535 ULONG extBlockIndex
;
3536 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3537 int index
, num_blocks
;
3539 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3541 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3542 return BLOCK_UNUSED
;
3544 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3546 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3548 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3550 num_blocks
= This
->bigBlockSize
/ 4;
3552 for (index
= 0; index
< num_blocks
; index
++)
3554 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3555 This
->extBlockDepotCached
[index
] = blockIndex
;
3558 This
->indexExtBlockDepotCached
= extBlockCount
;
3561 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3566 /******************************************************************************
3567 * Storage32Impl_SetExtDepotBlock
3569 * Associates the specified block index to the specified depot index.
3570 * This method is only for depot indexes equal or greater than
3571 * COUNT_BBDEPOTINHEADER.
3573 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3575 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3576 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3577 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3578 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3579 ULONG extBlockIndex
;
3581 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3583 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3585 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3587 if (extBlockIndex
!= BLOCK_UNUSED
)
3589 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3590 extBlockOffset
* sizeof(ULONG
),
3594 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3596 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3600 /******************************************************************************
3601 * Storage32Impl_AddExtBlockDepot
3603 * Creates an extended depot block.
3605 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3607 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3608 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3609 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3610 ULONG index
= BLOCK_UNUSED
;
3611 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3612 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3613 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3615 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3616 blocksPerDepotBlock
;
3618 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3621 * The first extended block.
3623 This
->extBigBlockDepotStart
= index
;
3628 * Find the last existing extended block.
3630 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3633 * Add the new extended block to the chain.
3635 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3640 * Initialize this block.
3642 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3643 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3645 /* Add the block to our cache. */
3646 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3648 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3649 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3651 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3652 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3654 This
->extBigBlockDepotLocations
= new_cache
;
3655 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3657 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3662 /******************************************************************************
3663 * Storage32Impl_FreeBigBlock
3665 * This method will flag the specified block as free in the big block depot.
3667 static void StorageImpl_FreeBigBlock(
3671 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3673 if (blockIndex
< This
->prevFreeBlock
)
3674 This
->prevFreeBlock
= blockIndex
;
3677 /************************************************************************
3678 * Storage32Impl_GetNextBlockInChain
3680 * This method will retrieve the block index of the next big block in
3683 * Params: This - Pointer to the Storage object.
3684 * blockIndex - Index of the block to retrieve the chain
3686 * nextBlockIndex - receives the return value.
3688 * Returns: This method returns the index of the next block in the chain.
3689 * It will return the constants:
3690 * BLOCK_SPECIAL - If the block given was not part of a
3692 * BLOCK_END_OF_CHAIN - If the block given was the last in
3694 * BLOCK_UNUSED - If the block given was not past of a chain
3696 * BLOCK_EXTBBDEPOT - This block is part of the extended
3699 * See Windows documentation for more details on IStorage methods.
3701 static HRESULT
StorageImpl_GetNextBlockInChain(
3704 ULONG
* nextBlockIndex
)
3706 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3707 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3708 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3709 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3711 ULONG depotBlockIndexPos
;
3712 int index
, num_blocks
;
3714 *nextBlockIndex
= BLOCK_SPECIAL
;
3716 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3718 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3719 This
->bigBlockDepotCount
);
3720 return STG_E_READFAULT
;
3724 * Cache the currently accessed depot block.
3726 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3728 This
->indexBlockDepotCached
= depotBlockCount
;
3730 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3732 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3737 * We have to look in the extended depot.
3739 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3742 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3745 return STG_E_READFAULT
;
3747 num_blocks
= This
->bigBlockSize
/ 4;
3749 for (index
= 0; index
< num_blocks
; index
++)
3751 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3752 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3756 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3761 /******************************************************************************
3762 * Storage32Impl_GetNextExtendedBlock
3764 * Given an extended block this method will return the next extended block.
3767 * The last ULONG of an extended block is the block index of the next
3768 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3772 * - The index of the next extended block
3773 * - BLOCK_UNUSED: there is no next extended block.
3774 * - Any other return values denotes failure.
3776 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3778 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3779 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3781 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3784 return nextBlockIndex
;
3787 /******************************************************************************
3788 * Storage32Impl_SetNextBlockInChain
3790 * This method will write the index of the specified block's next block
3791 * in the big block depot.
3793 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3796 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3797 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3798 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3801 static void StorageImpl_SetNextBlockInChain(
3806 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3807 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3808 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3809 ULONG depotBlockIndexPos
;
3811 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3812 assert(blockIndex
!= nextBlock
);
3814 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
3815 /* This should never happen (storage file format spec forbids it), but
3816 * older versions of Wine may have generated broken files. We don't want to
3817 * assert and potentially lose data, but we do want to know if this ever
3818 * happens in a newly-created file. */
3819 ERR("Using range lock page\n");
3821 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3823 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3828 * We have to look in the extended depot.
3830 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3833 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3836 * Update the cached block depot, if necessary.
3838 if (depotBlockCount
== This
->indexBlockDepotCached
)
3840 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3844 /******************************************************************************
3845 * Storage32Impl_LoadFileHeader
3847 * This method will read in the file header
3849 static HRESULT
StorageImpl_LoadFileHeader(
3853 BYTE headerBigBlock
[HEADER_SIZE
];
3855 ULARGE_INTEGER offset
;
3860 * Get a pointer to the big block of data containing the header.
3862 offset
.u
.HighPart
= 0;
3863 offset
.u
.LowPart
= 0;
3864 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3865 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3866 hr
= STG_E_FILENOTFOUND
;
3869 * Extract the information from the header.
3874 * Check for the "magic number" signature and return an error if it is not
3877 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3879 return STG_E_OLDFORMAT
;
3882 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3884 return STG_E_INVALIDHEADER
;
3887 StorageUtl_ReadWord(
3889 OFFSET_BIGBLOCKSIZEBITS
,
3890 &This
->bigBlockSizeBits
);
3892 StorageUtl_ReadWord(
3894 OFFSET_SMALLBLOCKSIZEBITS
,
3895 &This
->smallBlockSizeBits
);
3897 StorageUtl_ReadDWord(
3899 OFFSET_BBDEPOTCOUNT
,
3900 &This
->bigBlockDepotCount
);
3902 StorageUtl_ReadDWord(
3904 OFFSET_ROOTSTARTBLOCK
,
3905 &This
->rootStartBlock
);
3907 StorageUtl_ReadDWord(
3909 OFFSET_TRANSACTIONSIG
,
3910 &This
->transactionSig
);
3912 StorageUtl_ReadDWord(
3914 OFFSET_SMALLBLOCKLIMIT
,
3915 &This
->smallBlockLimit
);
3917 StorageUtl_ReadDWord(
3919 OFFSET_SBDEPOTSTART
,
3920 &This
->smallBlockDepotStart
);
3922 StorageUtl_ReadDWord(
3924 OFFSET_EXTBBDEPOTSTART
,
3925 &This
->extBigBlockDepotStart
);
3927 StorageUtl_ReadDWord(
3929 OFFSET_EXTBBDEPOTCOUNT
,
3930 &This
->extBigBlockDepotCount
);
3932 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3934 StorageUtl_ReadDWord(
3936 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3937 &(This
->bigBlockDepotStart
[index
]));
3941 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3943 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3944 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3947 * Right now, the code is making some assumptions about the size of the
3948 * blocks, just make sure they are what we're expecting.
3950 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3951 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3952 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3954 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3955 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3956 hr
= STG_E_INVALIDHEADER
;
3965 /******************************************************************************
3966 * Storage32Impl_SaveFileHeader
3968 * This method will save to the file the header
3970 static void StorageImpl_SaveFileHeader(
3973 BYTE headerBigBlock
[HEADER_SIZE
];
3976 ULARGE_INTEGER offset
;
3977 DWORD bytes_read
, bytes_written
;
3978 DWORD major_version
, dirsectorcount
;
3981 * Get a pointer to the big block of data containing the header.
3983 offset
.u
.HighPart
= 0;
3984 offset
.u
.LowPart
= 0;
3985 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3986 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3987 hr
= STG_E_FILENOTFOUND
;
3989 if (This
->bigBlockSizeBits
== 0x9)
3991 else if (This
->bigBlockSizeBits
== 0xc)
3995 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
4000 * If the block read failed, the file is probably new.
4005 * Initialize for all unknown fields.
4007 memset(headerBigBlock
, 0, HEADER_SIZE
);
4010 * Initialize the magic number.
4012 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
4016 * Write the information to the header.
4018 StorageUtl_WriteWord(
4020 OFFSET_MINORVERSION
,
4023 StorageUtl_WriteWord(
4025 OFFSET_MAJORVERSION
,
4028 StorageUtl_WriteWord(
4030 OFFSET_BYTEORDERMARKER
,
4033 StorageUtl_WriteWord(
4035 OFFSET_BIGBLOCKSIZEBITS
,
4036 This
->bigBlockSizeBits
);
4038 StorageUtl_WriteWord(
4040 OFFSET_SMALLBLOCKSIZEBITS
,
4041 This
->smallBlockSizeBits
);
4043 if (major_version
>= 4)
4045 if (This
->rootBlockChain
)
4046 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
4048 /* This file is being created, and it will start out with one block. */
4052 /* This field must be 0 in versions older than 4 */
4055 StorageUtl_WriteDWord(
4057 OFFSET_DIRSECTORCOUNT
,
4060 StorageUtl_WriteDWord(
4062 OFFSET_BBDEPOTCOUNT
,
4063 This
->bigBlockDepotCount
);
4065 StorageUtl_WriteDWord(
4067 OFFSET_ROOTSTARTBLOCK
,
4068 This
->rootStartBlock
);
4070 StorageUtl_WriteDWord(
4072 OFFSET_TRANSACTIONSIG
,
4073 This
->transactionSig
);
4075 StorageUtl_WriteDWord(
4077 OFFSET_SMALLBLOCKLIMIT
,
4078 This
->smallBlockLimit
);
4080 StorageUtl_WriteDWord(
4082 OFFSET_SBDEPOTSTART
,
4083 This
->smallBlockDepotStart
);
4085 StorageUtl_WriteDWord(
4087 OFFSET_SBDEPOTCOUNT
,
4088 This
->smallBlockDepotChain
?
4089 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
4091 StorageUtl_WriteDWord(
4093 OFFSET_EXTBBDEPOTSTART
,
4094 This
->extBigBlockDepotStart
);
4096 StorageUtl_WriteDWord(
4098 OFFSET_EXTBBDEPOTCOUNT
,
4099 This
->extBigBlockDepotCount
);
4101 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
4103 StorageUtl_WriteDWord(
4105 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
4106 (This
->bigBlockDepotStart
[index
]));
4110 * Write the big block back to the file.
4112 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
4115 /******************************************************************************
4116 * StorageImpl_ReadRawDirEntry
4118 * This method will read the raw data from a directory entry in the file.
4120 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4122 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
4124 ULARGE_INTEGER offset
;
4128 offset
.u
.HighPart
= 0;
4129 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
4131 hr
= BlockChainStream_ReadAt(
4132 This
->rootBlockChain
,
4138 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
4139 return STG_E_READFAULT
;
4144 /******************************************************************************
4145 * StorageImpl_WriteRawDirEntry
4147 * This method will write the raw data from a directory entry in the file.
4149 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4151 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
4153 ULARGE_INTEGER offset
;
4156 offset
.u
.HighPart
= 0;
4157 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
4159 return BlockChainStream_WriteAt(
4160 This
->rootBlockChain
,
4167 /******************************************************************************
4170 * Update raw directory entry data from the fields in newData.
4172 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4174 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
4176 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
4179 buffer
+ OFFSET_PS_NAME
,
4181 DIRENTRY_NAME_BUFFER_LEN
);
4183 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
4185 StorageUtl_WriteWord(
4187 OFFSET_PS_NAMELENGTH
,
4188 newData
->sizeOfNameString
);
4190 StorageUtl_WriteDWord(
4192 OFFSET_PS_LEFTCHILD
,
4193 newData
->leftChild
);
4195 StorageUtl_WriteDWord(
4197 OFFSET_PS_RIGHTCHILD
,
4198 newData
->rightChild
);
4200 StorageUtl_WriteDWord(
4203 newData
->dirRootEntry
);
4205 StorageUtl_WriteGUID(
4210 StorageUtl_WriteDWord(
4213 newData
->ctime
.dwLowDateTime
);
4215 StorageUtl_WriteDWord(
4217 OFFSET_PS_CTIMEHIGH
,
4218 newData
->ctime
.dwHighDateTime
);
4220 StorageUtl_WriteDWord(
4223 newData
->mtime
.dwLowDateTime
);
4225 StorageUtl_WriteDWord(
4227 OFFSET_PS_MTIMEHIGH
,
4228 newData
->ctime
.dwHighDateTime
);
4230 StorageUtl_WriteDWord(
4232 OFFSET_PS_STARTBLOCK
,
4233 newData
->startingBlock
);
4235 StorageUtl_WriteDWord(
4238 newData
->size
.u
.LowPart
);
4241 /******************************************************************************
4242 * Storage32Impl_ReadDirEntry
4244 * This method will read the specified directory entry.
4246 HRESULT
StorageImpl_ReadDirEntry(
4251 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4254 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
4256 if (SUCCEEDED(readRes
))
4258 memset(buffer
->name
, 0, sizeof(buffer
->name
));
4261 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
4262 DIRENTRY_NAME_BUFFER_LEN
);
4263 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
4265 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
4267 StorageUtl_ReadWord(
4269 OFFSET_PS_NAMELENGTH
,
4270 &buffer
->sizeOfNameString
);
4272 StorageUtl_ReadDWord(
4274 OFFSET_PS_LEFTCHILD
,
4275 &buffer
->leftChild
);
4277 StorageUtl_ReadDWord(
4279 OFFSET_PS_RIGHTCHILD
,
4280 &buffer
->rightChild
);
4282 StorageUtl_ReadDWord(
4285 &buffer
->dirRootEntry
);
4287 StorageUtl_ReadGUID(
4292 StorageUtl_ReadDWord(
4295 &buffer
->ctime
.dwLowDateTime
);
4297 StorageUtl_ReadDWord(
4299 OFFSET_PS_CTIMEHIGH
,
4300 &buffer
->ctime
.dwHighDateTime
);
4302 StorageUtl_ReadDWord(
4305 &buffer
->mtime
.dwLowDateTime
);
4307 StorageUtl_ReadDWord(
4309 OFFSET_PS_MTIMEHIGH
,
4310 &buffer
->mtime
.dwHighDateTime
);
4312 StorageUtl_ReadDWord(
4314 OFFSET_PS_STARTBLOCK
,
4315 &buffer
->startingBlock
);
4317 StorageUtl_ReadDWord(
4320 &buffer
->size
.u
.LowPart
);
4322 buffer
->size
.u
.HighPart
= 0;
4328 /*********************************************************************
4329 * Write the specified directory entry to the file
4331 HRESULT
StorageImpl_WriteDirEntry(
4334 const DirEntry
* buffer
)
4336 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4338 UpdateRawDirEntry(currentEntry
, buffer
);
4340 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4343 static HRESULT
StorageImpl_ReadBigBlock(
4349 ULARGE_INTEGER ulOffset
;
4353 ulOffset
.u
.HighPart
= 0;
4354 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4356 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4358 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4360 /* File ends during this block; fill the rest with 0's. */
4361 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4364 if (out_read
) *out_read
= read
;
4369 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4375 ULARGE_INTEGER ulOffset
;
4379 ulOffset
.u
.HighPart
= 0;
4380 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4381 ulOffset
.u
.LowPart
+= offset
;
4383 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4384 *value
= lendian32toh(tmp
);
4385 return (read
== sizeof(DWORD
));
4388 static BOOL
StorageImpl_WriteBigBlock(
4393 ULARGE_INTEGER ulOffset
;
4396 ulOffset
.u
.HighPart
= 0;
4397 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4399 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4400 return (wrote
== This
->bigBlockSize
);
4403 static BOOL
StorageImpl_WriteDWordToBigBlock(
4409 ULARGE_INTEGER ulOffset
;
4412 ulOffset
.u
.HighPart
= 0;
4413 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4414 ulOffset
.u
.LowPart
+= offset
;
4416 value
= htole32(value
);
4417 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4418 return (wrote
== sizeof(DWORD
));
4421 /******************************************************************************
4422 * Storage32Impl_SmallBlocksToBigBlocks
4424 * This method will convert a small block chain to a big block chain.
4425 * The small block chain will be destroyed.
4427 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4429 SmallBlockChainStream
** ppsbChain
)
4431 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4432 ULARGE_INTEGER size
, offset
;
4433 ULONG cbRead
, cbWritten
;
4434 ULARGE_INTEGER cbTotalRead
;
4435 DirRef streamEntryRef
;
4436 HRESULT resWrite
= S_OK
;
4438 DirEntry streamEntry
;
4440 BlockChainStream
*bbTempChain
= NULL
;
4441 BlockChainStream
*bigBlockChain
= NULL
;
4444 * Create a temporary big block chain that doesn't have
4445 * an associated directory entry. This temporary chain will be
4446 * used to copy data from small blocks to big blocks.
4448 bbTempChain
= BlockChainStream_Construct(This
,
4451 if(!bbTempChain
) return NULL
;
4453 * Grow the big block chain.
4455 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4456 BlockChainStream_SetSize(bbTempChain
, size
);
4459 * Copy the contents of the small block chain to the big block chain
4460 * by small block size increments.
4462 offset
.u
.LowPart
= 0;
4463 offset
.u
.HighPart
= 0;
4464 cbTotalRead
.QuadPart
= 0;
4466 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4469 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4471 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4474 if (FAILED(resRead
))
4479 cbTotalRead
.QuadPart
+= cbRead
;
4481 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4487 if (FAILED(resWrite
))
4490 offset
.u
.LowPart
+= cbRead
;
4494 resRead
= STG_E_READFAULT
;
4497 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4498 HeapFree(GetProcessHeap(),0,buffer
);
4500 size
.u
.HighPart
= 0;
4503 if (FAILED(resRead
) || FAILED(resWrite
))
4505 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4506 BlockChainStream_SetSize(bbTempChain
, size
);
4507 BlockChainStream_Destroy(bbTempChain
);
4512 * Destroy the small block chain.
4514 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4515 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4516 SmallBlockChainStream_Destroy(*ppsbChain
);
4520 * Change the directory entry. This chain is now a big block chain
4521 * and it doesn't reside in the small blocks chain anymore.
4523 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4525 streamEntry
.startingBlock
= bbHeadOfChain
;
4527 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4530 * Destroy the temporary entryless big block chain.
4531 * Create a new big block chain associated with this entry.
4533 BlockChainStream_Destroy(bbTempChain
);
4534 bigBlockChain
= BlockChainStream_Construct(This
,
4538 return bigBlockChain
;
4541 /******************************************************************************
4542 * Storage32Impl_BigBlocksToSmallBlocks
4544 * This method will convert a big block chain to a small block chain.
4545 * The big block chain will be destroyed on success.
4547 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4549 BlockChainStream
** ppbbChain
,
4550 ULARGE_INTEGER newSize
)
4552 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4553 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4554 DirRef streamEntryRef
;
4555 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4556 DirEntry streamEntry
;
4558 SmallBlockChainStream
* sbTempChain
;
4560 TRACE("%p %p\n", This
, ppbbChain
);
4562 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4568 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4569 size
= BlockChainStream_GetSize(*ppbbChain
);
4570 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4572 offset
.u
.HighPart
= 0;
4573 offset
.u
.LowPart
= 0;
4574 cbTotalRead
.QuadPart
= 0;
4575 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4576 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4578 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4579 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4587 cbTotalRead
.QuadPart
+= cbRead
;
4589 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4590 cbRead
, buffer
, &cbWritten
);
4592 if(FAILED(resWrite
))
4595 offset
.u
.LowPart
+= cbRead
;
4599 resRead
= STG_E_READFAULT
;
4603 HeapFree(GetProcessHeap(), 0, buffer
);
4605 size
.u
.HighPart
= 0;
4608 if(FAILED(resRead
) || FAILED(resWrite
))
4610 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4611 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4612 SmallBlockChainStream_Destroy(sbTempChain
);
4616 /* destroy the original big block chain */
4617 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4618 BlockChainStream_SetSize(*ppbbChain
, size
);
4619 BlockChainStream_Destroy(*ppbbChain
);
4622 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4623 streamEntry
.startingBlock
= sbHeadOfChain
;
4624 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4626 SmallBlockChainStream_Destroy(sbTempChain
);
4627 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4630 static HRESULT
StorageBaseImpl_CopyStream(
4631 StorageBaseImpl
*dst
, DirRef dst_entry
,
4632 StorageBaseImpl
*src
, DirRef src_entry
)
4637 ULARGE_INTEGER bytes_copied
;
4638 ULONG bytestocopy
, bytesread
, byteswritten
;
4640 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4644 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4646 bytes_copied
.QuadPart
= 0;
4647 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4649 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4651 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4653 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4656 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4657 data
, &byteswritten
);
4660 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4661 bytes_copied
.QuadPart
+= byteswritten
;
4669 static HRESULT
StorageBaseImpl_DupStorageTree(
4670 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
4671 StorageBaseImpl
*src
, DirRef src_entry
)
4675 BOOL has_stream
=FALSE
;
4677 if (src_entry
== DIRENTRY_NULL
)
4679 *dst_entry
= DIRENTRY_NULL
;
4683 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
4686 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
4687 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
4688 data
.size
.QuadPart
= 0;
4690 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
4694 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
4697 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
4700 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
4702 if (SUCCEEDED(hr
) && has_stream
)
4703 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
4708 static HRESULT
StorageBaseImpl_CopyStorageTree(
4709 StorageBaseImpl
*dst
, DirRef dst_entry
,
4710 StorageBaseImpl
*src
, DirRef src_entry
)
4713 DirEntry src_data
, dst_data
;
4714 DirRef new_root_entry
;
4716 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
4720 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
4725 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
4726 dst_data
.clsid
= src_data
.clsid
;
4727 dst_data
.ctime
= src_data
.ctime
;
4728 dst_data
.mtime
= src_data
.mtime
;
4729 dst_data
.dirRootEntry
= new_root_entry
;
4733 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
4738 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
4742 ULARGE_INTEGER zero
;
4744 if (entry
== DIRENTRY_NULL
)
4749 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
4751 if (SUCCEEDED(hr
) && include_siblings
)
4752 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
4754 if (SUCCEEDED(hr
) && include_siblings
)
4755 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
4758 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
4760 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
4761 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
4764 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
4769 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4771 DirRef result
=This
->firstFreeEntry
;
4773 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4776 if (result
== This
->entries_size
)
4778 ULONG new_size
= This
->entries_size
* 2;
4779 TransactedDirEntry
*new_entries
;
4781 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4782 if (!new_entries
) return DIRENTRY_NULL
;
4784 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4785 HeapFree(GetProcessHeap(), 0, This
->entries
);
4787 This
->entries
= new_entries
;
4788 This
->entries_size
= new_size
;
4791 This
->entries
[result
].inuse
= TRUE
;
4793 This
->firstFreeEntry
= result
+1;
4798 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4799 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4801 DirRef stubEntryRef
;
4802 TransactedDirEntry
*entry
;
4804 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4806 if (stubEntryRef
!= DIRENTRY_NULL
)
4808 entry
= &This
->entries
[stubEntryRef
];
4810 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4812 entry
->read
= FALSE
;
4815 return stubEntryRef
;
4818 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4819 TransactedSnapshotImpl
*This
, DirRef entry
)
4824 if (!This
->entries
[entry
].read
)
4826 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4827 This
->entries
[entry
].transactedParentEntry
,
4830 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4832 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4834 if (data
.leftChild
== DIRENTRY_NULL
)
4838 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4840 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4842 if (data
.rightChild
== DIRENTRY_NULL
)
4846 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4848 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4850 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4856 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4857 This
->entries
[entry
].read
= TRUE
;
4864 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4865 TransactedSnapshotImpl
*This
, DirRef entry
)
4869 if (!This
->entries
[entry
].stream_dirty
)
4871 DirEntry new_entrydata
;
4873 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4874 new_entrydata
.name
[0] = 'S';
4875 new_entrydata
.sizeOfNameString
= 1;
4876 new_entrydata
.stgType
= STGTY_STREAM
;
4877 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4878 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4879 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4880 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4882 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4883 &This
->entries
[entry
].stream_entry
);
4885 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4887 hr
= StorageBaseImpl_CopyStream(
4888 This
->scratch
, This
->entries
[entry
].stream_entry
,
4889 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4892 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4896 This
->entries
[entry
].stream_dirty
= TRUE
;
4898 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4900 /* Since this entry is modified, and we aren't using its stream data, we
4901 * no longer care about the original entry. */
4903 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4905 if (delete_ref
!= DIRENTRY_NULL
)
4906 This
->entries
[delete_ref
].deleted
= TRUE
;
4908 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4915 /* Find the first entry in a depth-first traversal. */
4916 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4917 TransactedSnapshotImpl
* This
, DirRef parent
)
4919 DirRef cursor
, prev
;
4920 TransactedDirEntry
*entry
;
4923 entry
= &This
->entries
[cursor
];
4926 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4929 cursor
= entry
->data
.leftChild
;
4930 entry
= &This
->entries
[cursor
];
4931 entry
->parent
= prev
;
4933 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4936 cursor
= entry
->data
.rightChild
;
4937 entry
= &This
->entries
[cursor
];
4938 entry
->parent
= prev
;
4940 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4943 cursor
= entry
->data
.dirRootEntry
;
4944 entry
= &This
->entries
[cursor
];
4945 entry
->parent
= prev
;
4954 /* Find the next entry in a depth-first traversal. */
4955 static DirRef
TransactedSnapshotImpl_FindNextChild(
4956 TransactedSnapshotImpl
* This
, DirRef current
)
4959 TransactedDirEntry
*parent_entry
;
4961 parent
= This
->entries
[current
].parent
;
4962 parent_entry
= &This
->entries
[parent
];
4964 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4966 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4968 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4969 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4972 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4974 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4975 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4982 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4983 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4984 TransactedSnapshotImpl
* This
, DirRef entry
)
4986 return entry
!= DIRENTRY_NULL
&&
4987 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4990 /* Destroy the entries created by CopyTree. */
4991 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4992 TransactedSnapshotImpl
* This
, DirRef stop
)
4995 TransactedDirEntry
*entry
;
4996 ULARGE_INTEGER zero
;
5000 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5003 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5005 if (cursor
== DIRENTRY_NULL
)
5008 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5010 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5012 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5014 entry
= &This
->entries
[cursor
];
5016 if (entry
->stream_dirty
)
5017 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5018 entry
->newTransactedParentEntry
, zero
);
5020 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5021 entry
->newTransactedParentEntry
);
5023 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5026 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5030 /* Make a copy of our edited tree that we can use in the parent. */
5031 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5034 TransactedDirEntry
*entry
;
5037 cursor
= This
->base
.storageDirEntry
;
5038 entry
= &This
->entries
[cursor
];
5039 entry
->parent
= DIRENTRY_NULL
;
5040 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5042 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5045 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5047 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5048 entry
= &This
->entries
[cursor
];
5050 while (cursor
!= DIRENTRY_NULL
)
5052 /* Make a copy of this entry in the transacted parent. */
5054 (!entry
->dirty
&& !entry
->stream_dirty
&&
5055 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5056 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5057 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5058 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5063 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5065 newData
.size
.QuadPart
= 0;
5066 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5068 if (newData
.leftChild
!= DIRENTRY_NULL
)
5069 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5071 if (newData
.rightChild
!= DIRENTRY_NULL
)
5072 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5074 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5075 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5077 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5078 &entry
->newTransactedParentEntry
);
5081 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5085 if (entry
->stream_dirty
)
5087 hr
= StorageBaseImpl_CopyStream(
5088 This
->transactedParent
, entry
->newTransactedParentEntry
,
5089 This
->scratch
, entry
->stream_entry
);
5091 else if (entry
->data
.size
.QuadPart
)
5093 hr
= StorageBaseImpl_StreamLink(
5094 This
->transactedParent
, entry
->newTransactedParentEntry
,
5095 entry
->transactedParentEntry
);
5100 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5101 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5106 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5107 entry
= &This
->entries
[cursor
];
5113 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5115 DWORD grfCommitFlags
) /* [in] */
5117 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5118 TransactedDirEntry
*root_entry
;
5119 DirRef i
, dir_root_ref
;
5121 ULARGE_INTEGER zero
;
5123 ULONG transactionSig
;
5127 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5129 /* Cannot commit a read-only transacted storage */
5130 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5131 return STG_E_ACCESSDENIED
;
5133 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5134 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5137 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5140 if (transactionSig
!= This
->lastTransactionSig
)
5142 ERR("file was externally modified\n");
5143 hr
= STG_E_NOTCURRENT
;
5148 This
->lastTransactionSig
= transactionSig
+1;
5149 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5152 else if (hr
== E_NOTIMPL
)
5155 if (FAILED(hr
)) goto end
;
5157 /* To prevent data loss, we create the new structure in the file before we
5158 * delete the old one, so that in case of errors the old data is intact. We
5159 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5160 * needed in the rare situation where we have just enough free disk space to
5161 * overwrite the existing data. */
5163 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5165 if (!root_entry
->read
)
5168 hr
= TransactedSnapshotImpl_CopyTree(This
);
5169 if (FAILED(hr
)) goto end
;
5171 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5172 dir_root_ref
= DIRENTRY_NULL
;
5174 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5176 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5178 /* Update the storage to use the new data in one step. */
5180 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5181 root_entry
->transactedParentEntry
, &data
);
5185 data
.dirRootEntry
= dir_root_ref
;
5186 data
.clsid
= root_entry
->data
.clsid
;
5187 data
.ctime
= root_entry
->data
.ctime
;
5188 data
.mtime
= root_entry
->data
.mtime
;
5190 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5191 root_entry
->transactedParentEntry
, &data
);
5194 /* Try to flush after updating the root storage, but if the flush fails, keep
5195 * going, on the theory that it'll either succeed later or the subsequent
5196 * writes will fail. */
5197 StorageBaseImpl_Flush(This
->transactedParent
);
5201 /* Destroy the old now-orphaned data. */
5202 for (i
=0; i
<This
->entries_size
; i
++)
5204 TransactedDirEntry
*entry
= &This
->entries
[i
];
5209 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5210 entry
->transactedParentEntry
, zero
);
5211 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5212 entry
->transactedParentEntry
);
5213 memset(entry
, 0, sizeof(TransactedDirEntry
));
5214 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5216 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5218 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5219 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5220 entry
->transactedParentEntry
);
5221 if (entry
->stream_dirty
)
5223 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5224 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5225 entry
->stream_dirty
= FALSE
;
5227 entry
->dirty
= FALSE
;
5228 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5235 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5239 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5241 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5247 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
5250 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5251 ULARGE_INTEGER zero
;
5254 TRACE("(%p)\n", iface
);
5256 /* Destroy the open objects. */
5257 StorageBaseImpl_DeleteAll(&This
->base
);
5259 /* Clear out the scratch file. */
5261 for (i
=0; i
<This
->entries_size
; i
++)
5263 if (This
->entries
[i
].stream_dirty
)
5265 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
5268 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
5272 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
5274 This
->firstFreeEntry
= 0;
5275 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
5280 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
5282 if (!This
->reverted
)
5284 TRACE("Storage invalidated (stg=%p)\n", This
);
5286 This
->reverted
= TRUE
;
5288 StorageBaseImpl_DeleteAll(This
);
5292 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
5294 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5296 IStorage_Revert(&This
->base
.IStorage_iface
);
5297 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5298 IStorage_Release(&This
->scratch
->IStorage_iface
);
5299 HeapFree(GetProcessHeap(), 0, This
->entries
);
5300 HeapFree(GetProcessHeap(), 0, This
);
5303 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
5305 /* We only need to flush when committing. */
5309 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5311 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5313 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5316 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
5317 const DirEntry
*newData
, DirRef
*index
)
5319 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5321 TransactedDirEntry
*new_entry
;
5323 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
5324 if (new_ref
== DIRENTRY_NULL
)
5325 return E_OUTOFMEMORY
;
5327 new_entry
= &This
->entries
[new_ref
];
5329 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
5330 new_entry
->read
= TRUE
;
5331 new_entry
->dirty
= TRUE
;
5332 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
5336 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
5341 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
5342 DirRef index
, const DirEntry
*data
)
5344 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5347 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5349 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5350 if (FAILED(hr
)) return hr
;
5352 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
5354 if (index
!= This
->base
.storageDirEntry
)
5356 This
->entries
[index
].dirty
= TRUE
;
5358 if (data
->size
.QuadPart
== 0 &&
5359 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5361 /* Since this entry is modified, and we aren't using its stream data, we
5362 * no longer care about the original entry. */
5364 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5366 if (delete_ref
!= DIRENTRY_NULL
)
5367 This
->entries
[delete_ref
].deleted
= TRUE
;
5369 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5376 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
5377 DirRef index
, DirEntry
*data
)
5379 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5382 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5383 if (FAILED(hr
)) return hr
;
5385 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
5387 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5392 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5395 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5397 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
5398 This
->entries
[index
].data
.size
.QuadPart
!= 0)
5400 /* If we deleted this entry while it has stream data. We must have left the
5401 * data because some other entry is using it, and we need to leave the
5402 * original entry alone. */
5403 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
5404 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
5408 This
->entries
[index
].deleted
= TRUE
;
5414 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
5415 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5417 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5419 if (This
->entries
[index
].stream_dirty
)
5421 return StorageBaseImpl_StreamReadAt(This
->scratch
,
5422 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
5424 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
5426 /* This stream doesn't live in the parent, and we haven't allocated storage
5433 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
5434 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
5438 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
5439 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5441 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5444 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5445 if (FAILED(hr
)) return hr
;
5447 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5448 if (FAILED(hr
)) return hr
;
5450 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
5451 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5453 if (SUCCEEDED(hr
) && size
!= 0)
5454 This
->entries
[index
].data
.size
.QuadPart
= max(
5455 This
->entries
[index
].data
.size
.QuadPart
,
5456 offset
.QuadPart
+ size
);
5461 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5462 DirRef index
, ULARGE_INTEGER newsize
)
5464 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5467 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5468 if (FAILED(hr
)) return hr
;
5470 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5473 if (newsize
.QuadPart
== 0)
5475 /* Destroy any parent references or entries in the scratch file. */
5476 if (This
->entries
[index
].stream_dirty
)
5478 ULARGE_INTEGER zero
;
5480 StorageBaseImpl_StreamSetSize(This
->scratch
,
5481 This
->entries
[index
].stream_entry
, zero
);
5482 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5483 This
->entries
[index
].stream_entry
);
5484 This
->entries
[index
].stream_dirty
= FALSE
;
5486 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5489 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5491 if (delete_ref
!= DIRENTRY_NULL
)
5492 This
->entries
[delete_ref
].deleted
= TRUE
;
5494 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5499 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5500 if (FAILED(hr
)) return hr
;
5502 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5503 This
->entries
[index
].stream_entry
, newsize
);
5507 This
->entries
[index
].data
.size
= newsize
;
5512 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5513 DirRef dst
, DirRef src
)
5515 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5517 TransactedDirEntry
*dst_entry
, *src_entry
;
5519 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5520 if (FAILED(hr
)) return hr
;
5522 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5523 if (FAILED(hr
)) return hr
;
5525 dst_entry
= &This
->entries
[dst
];
5526 src_entry
= &This
->entries
[src
];
5528 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5529 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5530 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5531 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5532 dst_entry
->data
.size
= src_entry
->data
.size
;
5537 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
5538 ULONG
* result
, BOOL refresh
)
5543 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
5549 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5554 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5559 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5561 StorageBaseImpl_QueryInterface
,
5562 StorageBaseImpl_AddRef
,
5563 StorageBaseImpl_Release
,
5564 StorageBaseImpl_CreateStream
,
5565 StorageBaseImpl_OpenStream
,
5566 StorageBaseImpl_CreateStorage
,
5567 StorageBaseImpl_OpenStorage
,
5568 StorageBaseImpl_CopyTo
,
5569 StorageBaseImpl_MoveElementTo
,
5570 TransactedSnapshotImpl_Commit
,
5571 TransactedSnapshotImpl_Revert
,
5572 StorageBaseImpl_EnumElements
,
5573 StorageBaseImpl_DestroyElement
,
5574 StorageBaseImpl_RenameElement
,
5575 StorageBaseImpl_SetElementTimes
,
5576 StorageBaseImpl_SetClass
,
5577 StorageBaseImpl_SetStateBits
,
5578 StorageBaseImpl_Stat
5581 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5583 TransactedSnapshotImpl_Destroy
,
5584 TransactedSnapshotImpl_Invalidate
,
5585 TransactedSnapshotImpl_Flush
,
5586 TransactedSnapshotImpl_GetFilename
,
5587 TransactedSnapshotImpl_CreateDirEntry
,
5588 TransactedSnapshotImpl_WriteDirEntry
,
5589 TransactedSnapshotImpl_ReadDirEntry
,
5590 TransactedSnapshotImpl_DestroyDirEntry
,
5591 TransactedSnapshotImpl_StreamReadAt
,
5592 TransactedSnapshotImpl_StreamWriteAt
,
5593 TransactedSnapshotImpl_StreamSetSize
,
5594 TransactedSnapshotImpl_StreamLink
,
5595 TransactedSnapshotImpl_GetTransactionSig
,
5596 TransactedSnapshotImpl_SetTransactionSig
,
5597 TransactedSnapshotImpl_LockTransaction
,
5598 TransactedSnapshotImpl_UnlockTransaction
5601 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5602 TransactedSnapshotImpl
** result
)
5606 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5611 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5613 /* This is OK because the property set storage functions use the IStorage functions. */
5614 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5615 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5617 list_init(&(*result
)->base
.strmHead
);
5619 list_init(&(*result
)->base
.storageHead
);
5621 (*result
)->base
.ref
= 1;
5623 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5625 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5626 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5628 /* Create a new temporary storage to act as the scratch file. */
5629 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5631 (*result
)->scratch
= impl_from_IStorage(scratch
);
5635 ULONG num_entries
= 20;
5637 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5638 (*result
)->entries_size
= num_entries
;
5639 (*result
)->firstFreeEntry
= 0;
5641 if ((*result
)->entries
)
5643 /* parentStorage already has 1 reference, which we take over here. */
5644 (*result
)->transactedParent
= parentStorage
;
5646 parentStorage
->transactedChild
= &(*result
)->base
;
5648 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5652 IStorage_Release(scratch
);
5658 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5663 return E_OUTOFMEMORY
;
5666 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
5668 if (!This
->reverted
)
5670 TRACE("Storage invalidated (stg=%p)\n", This
);
5672 This
->reverted
= TRUE
;
5674 StorageBaseImpl_DeleteAll(This
);
5678 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
5680 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5682 TransactedSharedImpl_Invalidate(&This
->base
);
5683 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5684 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
5685 HeapFree(GetProcessHeap(), 0, This
);
5688 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
5690 /* We only need to flush when committing. */
5694 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5696 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5698 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5701 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
5702 const DirEntry
*newData
, DirRef
*index
)
5704 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5706 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
5710 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
5711 DirRef index
, const DirEntry
*data
)
5713 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5715 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
5719 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
5720 DirRef index
, DirEntry
*data
)
5722 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5724 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
5728 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5731 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5733 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
5737 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
5738 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5740 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5742 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
5743 index
, offset
, size
, buffer
, bytesRead
);
5746 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
5747 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5749 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5751 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
5752 index
, offset
, size
, buffer
, bytesWritten
);
5755 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
5756 DirRef index
, ULARGE_INTEGER newsize
)
5758 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5760 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
5764 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
5765 DirRef dst
, DirRef src
)
5767 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5769 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
5773 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
5774 ULONG
* result
, BOOL refresh
)
5779 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
5785 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5790 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5795 static HRESULT WINAPI
TransactedSharedImpl_Commit(
5797 DWORD grfCommitFlags
) /* [in] */
5799 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5800 DirRef new_storage_ref
, prev_storage_ref
;
5801 DirEntry src_data
, dst_data
;
5803 ULONG transactionSig
;
5805 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5807 /* Cannot commit a read-only transacted storage */
5808 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5809 return STG_E_ACCESSDENIED
;
5811 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5812 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5815 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5818 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
5819 hr
= STG_E_NOTCURRENT
;
5822 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
5824 else if (hr
== E_NOTIMPL
)
5828 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
5830 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5832 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
5835 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5838 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5842 prev_storage_ref
= dst_data
.dirRootEntry
;
5843 dst_data
.dirRootEntry
= new_storage_ref
;
5844 dst_data
.clsid
= src_data
.clsid
;
5845 dst_data
.ctime
= src_data
.ctime
;
5846 dst_data
.mtime
= src_data
.mtime
;
5847 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5852 /* Try to flush after updating the root storage, but if the flush fails, keep
5853 * going, on the theory that it'll either succeed later or the subsequent
5854 * writes will fail. */
5855 StorageBaseImpl_Flush(This
->transactedParent
);
5857 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
5861 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5863 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5866 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
5870 This
->lastTransactionSig
= transactionSig
+1;
5877 static HRESULT WINAPI
TransactedSharedImpl_Revert(
5880 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5882 TRACE("(%p)\n", iface
);
5884 /* Destroy the open objects. */
5885 StorageBaseImpl_DeleteAll(&This
->base
);
5887 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
5890 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
5892 StorageBaseImpl_QueryInterface
,
5893 StorageBaseImpl_AddRef
,
5894 StorageBaseImpl_Release
,
5895 StorageBaseImpl_CreateStream
,
5896 StorageBaseImpl_OpenStream
,
5897 StorageBaseImpl_CreateStorage
,
5898 StorageBaseImpl_OpenStorage
,
5899 StorageBaseImpl_CopyTo
,
5900 StorageBaseImpl_MoveElementTo
,
5901 TransactedSharedImpl_Commit
,
5902 TransactedSharedImpl_Revert
,
5903 StorageBaseImpl_EnumElements
,
5904 StorageBaseImpl_DestroyElement
,
5905 StorageBaseImpl_RenameElement
,
5906 StorageBaseImpl_SetElementTimes
,
5907 StorageBaseImpl_SetClass
,
5908 StorageBaseImpl_SetStateBits
,
5909 StorageBaseImpl_Stat
5912 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
5914 TransactedSharedImpl_Destroy
,
5915 TransactedSharedImpl_Invalidate
,
5916 TransactedSharedImpl_Flush
,
5917 TransactedSharedImpl_GetFilename
,
5918 TransactedSharedImpl_CreateDirEntry
,
5919 TransactedSharedImpl_WriteDirEntry
,
5920 TransactedSharedImpl_ReadDirEntry
,
5921 TransactedSharedImpl_DestroyDirEntry
,
5922 TransactedSharedImpl_StreamReadAt
,
5923 TransactedSharedImpl_StreamWriteAt
,
5924 TransactedSharedImpl_StreamSetSize
,
5925 TransactedSharedImpl_StreamLink
,
5926 TransactedSharedImpl_GetTransactionSig
,
5927 TransactedSharedImpl_SetTransactionSig
,
5928 TransactedSharedImpl_LockTransaction
,
5929 TransactedSharedImpl_UnlockTransaction
5932 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
5933 TransactedSharedImpl
** result
)
5937 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
5942 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
5944 /* This is OK because the property set storage functions use the IStorage functions. */
5945 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5946 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
5948 list_init(&(*result
)->base
.strmHead
);
5950 list_init(&(*result
)->base
.storageHead
);
5952 (*result
)->base
.ref
= 1;
5954 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5956 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
5962 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5963 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5967 stgo
.ulSectorSize
= 4096;
5968 stgo
.pwcsTemplateFile
= NULL
;
5970 /* Create a new temporary storage to act as the scratch file. */
5971 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
5972 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
5973 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
5977 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
5978 parentStorage
, parentStorage
->storageDirEntry
);
5982 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
5984 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
5985 (*result
)->transactedParent
= parentStorage
;
5989 IStorage_Release(scratch
);
5992 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
5995 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6000 return E_OUTOFMEMORY
;
6003 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6004 BOOL toplevel
, StorageBaseImpl
** result
)
6006 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6008 if (parentStorage
->openFlags
& fixme_flags
)
6010 fixme_flags
&= ~parentStorage
->openFlags
;
6011 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6014 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6015 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6016 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6018 /* Need to create a temp file for the snapshot */
6019 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6022 return TransactedSnapshotImpl_Construct(parentStorage
,
6023 (TransactedSnapshotImpl
**)result
);
6026 static HRESULT
Storage_Construct(
6034 StorageBaseImpl
** result
)
6036 StorageImpl
*newStorage
;
6037 StorageBaseImpl
*newTransactedStorage
;
6040 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6041 if (FAILED(hr
)) goto end
;
6043 if (openFlags
& STGM_TRANSACTED
)
6045 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6047 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6049 *result
= newTransactedStorage
;
6052 *result
= &newStorage
->base
;
6058 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
6060 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6062 if (!This
->base
.reverted
)
6064 TRACE("Storage invalidated (stg=%p)\n", This
);
6066 This
->base
.reverted
= TRUE
;
6068 This
->parentStorage
= NULL
;
6070 StorageBaseImpl_DeleteAll(&This
->base
);
6072 list_remove(&This
->ParentListEntry
);
6076 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
6078 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6080 StorageInternalImpl_Invalidate(&This
->base
);
6082 HeapFree(GetProcessHeap(), 0, This
);
6085 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
6087 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6089 return StorageBaseImpl_Flush(This
->parentStorage
);
6092 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6094 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6096 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
6099 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
6100 const DirEntry
*newData
, DirRef
*index
)
6102 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6104 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
6108 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
6109 DirRef index
, const DirEntry
*data
)
6111 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6113 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
6117 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
6118 DirRef index
, DirEntry
*data
)
6120 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6122 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6126 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6129 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6131 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
6135 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
6136 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6138 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6140 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
6141 index
, offset
, size
, buffer
, bytesRead
);
6144 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
6145 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6147 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6149 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
6150 index
, offset
, size
, buffer
, bytesWritten
);
6153 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
6154 DirRef index
, ULARGE_INTEGER newsize
)
6156 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6158 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
6162 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
6163 DirRef dst
, DirRef src
)
6165 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6167 return StorageBaseImpl_StreamLink(This
->parentStorage
,
6171 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
6172 ULONG
* result
, BOOL refresh
)
6177 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
6183 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6188 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6193 /******************************************************************************
6195 ** Storage32InternalImpl_Commit
6198 static HRESULT WINAPI
StorageInternalImpl_Commit(
6200 DWORD grfCommitFlags
) /* [in] */
6202 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
6203 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6204 return StorageBaseImpl_Flush(This
);
6207 /******************************************************************************
6209 ** Storage32InternalImpl_Revert
6212 static HRESULT WINAPI
StorageInternalImpl_Revert(
6215 FIXME("(%p): stub\n", iface
);
6219 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
6221 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
6222 HeapFree(GetProcessHeap(), 0, This
);
6225 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
6226 IEnumSTATSTG
* iface
,
6230 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6233 return E_INVALIDARG
;
6237 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
6238 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
6241 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
6245 return E_NOINTERFACE
;
6248 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
6249 IEnumSTATSTG
* iface
)
6251 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6252 return InterlockedIncrement(&This
->ref
);
6255 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
6256 IEnumSTATSTG
* iface
)
6258 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6262 newRef
= InterlockedDecrement(&This
->ref
);
6266 IEnumSTATSTGImpl_Destroy(This
);
6272 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
6273 IEnumSTATSTGImpl
* This
,
6276 DirRef result
= DIRENTRY_NULL
;
6280 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
6282 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6283 This
->parentStorage
->storageDirEntry
, &entry
);
6284 searchNode
= entry
.dirRootEntry
;
6286 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
6288 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
6292 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
6296 searchNode
= entry
.rightChild
;
6300 result
= searchNode
;
6301 memcpy(result_name
, entry
.name
, sizeof(result_name
));
6302 searchNode
= entry
.leftChild
;
6310 if (result
!= DIRENTRY_NULL
)
6311 memcpy(This
->name
, result_name
, sizeof(result_name
));
6317 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
6318 IEnumSTATSTG
* iface
,
6321 ULONG
* pceltFetched
)
6323 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6325 DirEntry currentEntry
;
6326 STATSTG
* currentReturnStruct
= rgelt
;
6327 ULONG objectFetched
= 0;
6328 DirRef currentSearchNode
;
6331 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
6332 return E_INVALIDARG
;
6334 if (This
->parentStorage
->reverted
)
6335 return STG_E_REVERTED
;
6338 * To avoid the special case, get another pointer to a ULONG value if
6339 * the caller didn't supply one.
6341 if (pceltFetched
==0)
6342 pceltFetched
= &objectFetched
;
6345 * Start the iteration, we will iterate until we hit the end of the
6346 * linked list or until we hit the number of items to iterate through
6350 while ( *pceltFetched
< celt
)
6352 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6354 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6358 * Read the entry from the storage.
6360 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6365 * Copy the information to the return buffer.
6367 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
6368 currentReturnStruct
,
6373 * Step to the next item in the iteration
6376 currentReturnStruct
++;
6379 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
6386 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
6387 IEnumSTATSTG
* iface
,
6390 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6392 ULONG objectFetched
= 0;
6393 DirRef currentSearchNode
;
6396 if (This
->parentStorage
->reverted
)
6397 return STG_E_REVERTED
;
6399 while ( (objectFetched
< celt
) )
6401 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6403 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6409 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
6415 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
6416 IEnumSTATSTG
* iface
)
6418 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6420 if (This
->parentStorage
->reverted
)
6421 return STG_E_REVERTED
;
6428 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
6429 IEnumSTATSTG
* iface
,
6430 IEnumSTATSTG
** ppenum
)
6432 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6433 IEnumSTATSTGImpl
* newClone
;
6435 if (This
->parentStorage
->reverted
)
6436 return STG_E_REVERTED
;
6439 return E_INVALIDARG
;
6441 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
6442 This
->storageDirEntry
);
6446 return E_OUTOFMEMORY
;
6450 * The new clone enumeration must point to the same current node as
6453 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
6455 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
6461 * Virtual function table for the IEnumSTATSTGImpl class.
6463 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
6465 IEnumSTATSTGImpl_QueryInterface
,
6466 IEnumSTATSTGImpl_AddRef
,
6467 IEnumSTATSTGImpl_Release
,
6468 IEnumSTATSTGImpl_Next
,
6469 IEnumSTATSTGImpl_Skip
,
6470 IEnumSTATSTGImpl_Reset
,
6471 IEnumSTATSTGImpl_Clone
6474 /******************************************************************************
6475 ** IEnumSTATSTGImpl implementation
6478 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
6479 StorageBaseImpl
* parentStorage
,
6480 DirRef storageDirEntry
)
6482 IEnumSTATSTGImpl
* newEnumeration
;
6484 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
6488 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
6489 newEnumeration
->ref
= 1;
6490 newEnumeration
->name
[0] = 0;
6493 * We want to nail-down the reference to the storage in case the
6494 * enumeration out-lives the storage in the client application.
6496 newEnumeration
->parentStorage
= parentStorage
;
6497 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
6499 newEnumeration
->storageDirEntry
= storageDirEntry
;
6502 return newEnumeration
;
6506 * Virtual function table for the Storage32InternalImpl class.
6508 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
6510 StorageBaseImpl_QueryInterface
,
6511 StorageBaseImpl_AddRef
,
6512 StorageBaseImpl_Release
,
6513 StorageBaseImpl_CreateStream
,
6514 StorageBaseImpl_OpenStream
,
6515 StorageBaseImpl_CreateStorage
,
6516 StorageBaseImpl_OpenStorage
,
6517 StorageBaseImpl_CopyTo
,
6518 StorageBaseImpl_MoveElementTo
,
6519 StorageInternalImpl_Commit
,
6520 StorageInternalImpl_Revert
,
6521 StorageBaseImpl_EnumElements
,
6522 StorageBaseImpl_DestroyElement
,
6523 StorageBaseImpl_RenameElement
,
6524 StorageBaseImpl_SetElementTimes
,
6525 StorageBaseImpl_SetClass
,
6526 StorageBaseImpl_SetStateBits
,
6527 StorageBaseImpl_Stat
6530 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
6532 StorageInternalImpl_Destroy
,
6533 StorageInternalImpl_Invalidate
,
6534 StorageInternalImpl_Flush
,
6535 StorageInternalImpl_GetFilename
,
6536 StorageInternalImpl_CreateDirEntry
,
6537 StorageInternalImpl_WriteDirEntry
,
6538 StorageInternalImpl_ReadDirEntry
,
6539 StorageInternalImpl_DestroyDirEntry
,
6540 StorageInternalImpl_StreamReadAt
,
6541 StorageInternalImpl_StreamWriteAt
,
6542 StorageInternalImpl_StreamSetSize
,
6543 StorageInternalImpl_StreamLink
,
6544 StorageInternalImpl_GetTransactionSig
,
6545 StorageInternalImpl_SetTransactionSig
,
6546 StorageInternalImpl_LockTransaction
,
6547 StorageInternalImpl_UnlockTransaction
6550 /******************************************************************************
6551 ** Storage32InternalImpl implementation
6554 static StorageInternalImpl
* StorageInternalImpl_Construct(
6555 StorageBaseImpl
* parentStorage
,
6557 DirRef storageDirEntry
)
6559 StorageInternalImpl
* newStorage
;
6561 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
6565 list_init(&newStorage
->base
.strmHead
);
6567 list_init(&newStorage
->base
.storageHead
);
6570 * Initialize the virtual function table.
6572 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
6573 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
6574 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
6575 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
6577 newStorage
->base
.reverted
= FALSE
;
6579 newStorage
->base
.ref
= 1;
6581 newStorage
->parentStorage
= parentStorage
;
6584 * Keep a reference to the directory entry of this storage
6586 newStorage
->base
.storageDirEntry
= storageDirEntry
;
6588 newStorage
->base
.create
= FALSE
;
6596 /******************************************************************************
6597 ** StorageUtl implementation
6600 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6604 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6605 *value
= lendian16toh(tmp
);
6608 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6610 value
= htole16(value
);
6611 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6614 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6618 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6619 *value
= lendian32toh(tmp
);
6622 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6624 value
= htole32(value
);
6625 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6628 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6629 ULARGE_INTEGER
* value
)
6631 #ifdef WORDS_BIGENDIAN
6634 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6635 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6636 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6638 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6642 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6643 const ULARGE_INTEGER
*value
)
6645 #ifdef WORDS_BIGENDIAN
6648 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6649 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6650 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6652 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6656 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6658 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6659 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6660 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6662 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6665 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6667 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6668 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6669 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6671 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6674 void StorageUtl_CopyDirEntryToSTATSTG(
6675 StorageBaseImpl
* storage
,
6676 STATSTG
* destination
,
6677 const DirEntry
* source
,
6681 * The copy of the string occurs only when the flag is not set
6683 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6685 /* Use the filename for the root storage. */
6686 destination
->pwcsName
= 0;
6687 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6689 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6690 (source
->name
[0] == 0) )
6692 destination
->pwcsName
= 0;
6696 destination
->pwcsName
=
6697 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6699 strcpyW(destination
->pwcsName
, source
->name
);
6702 switch (source
->stgType
)
6706 destination
->type
= STGTY_STORAGE
;
6709 destination
->type
= STGTY_STREAM
;
6712 destination
->type
= STGTY_STREAM
;
6716 destination
->cbSize
= source
->size
;
6718 currentReturnStruct->mtime = {0}; TODO
6719 currentReturnStruct->ctime = {0};
6720 currentReturnStruct->atime = {0};
6722 destination
->grfMode
= 0;
6723 destination
->grfLocksSupported
= 0;
6724 destination
->clsid
= source
->clsid
;
6725 destination
->grfStateBits
= 0;
6726 destination
->reserved
= 0;
6729 /******************************************************************************
6730 ** BlockChainStream implementation
6733 /* Read and save the index of all blocks in this stream. */
6734 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6736 ULONG next_sector
, next_offset
;
6738 struct BlockChainRun
*last_run
;
6740 if (This
->indexCacheLen
== 0)
6744 next_sector
= BlockChainStream_GetHeadOfChain(This
);
6748 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6749 next_offset
= last_run
->lastOffset
+1;
6750 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
6751 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
6753 if (FAILED(hr
)) return hr
;
6756 while (next_sector
!= BLOCK_END_OF_CHAIN
)
6758 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
6760 /* Add the current block to the cache. */
6761 if (This
->indexCacheSize
== 0)
6763 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
6764 if (!This
->indexCache
) return E_OUTOFMEMORY
;
6765 This
->indexCacheSize
= 16;
6767 else if (This
->indexCacheSize
== This
->indexCacheLen
)
6769 struct BlockChainRun
*new_cache
;
6772 new_size
= This
->indexCacheSize
* 2;
6773 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
6774 if (!new_cache
) return E_OUTOFMEMORY
;
6775 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
6777 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6778 This
->indexCache
= new_cache
;
6779 This
->indexCacheSize
= new_size
;
6782 This
->indexCacheLen
++;
6783 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6784 last_run
->firstSector
= next_sector
;
6785 last_run
->firstOffset
= next_offset
;
6788 last_run
->lastOffset
= next_offset
;
6790 /* Find the next block. */
6792 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
6793 if (FAILED(hr
)) return hr
;
6796 if (This
->indexCacheLen
)
6798 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
6799 This
->numBlocks
= last_run
->lastOffset
+1;
6803 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6804 This
->numBlocks
= 0;
6810 /* Locate the nth block in this stream. */
6811 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
6813 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
6814 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
6816 if (offset
>= This
->numBlocks
)
6817 return BLOCK_END_OF_CHAIN
;
6819 while (min_run
< max_run
)
6821 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
6822 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
6824 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
6825 max_run
= run_to_check
-1;
6827 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
6829 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
6830 min_run
= run_to_check
+1;
6833 /* Block is in this run. */
6834 min_run
= max_run
= run_to_check
;
6837 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
6840 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
6841 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
6843 BlockChainBlock
*result
=NULL
;
6847 if (This
->cachedBlocks
[i
].index
== index
)
6849 *sector
= This
->cachedBlocks
[i
].sector
;
6850 *block
= &This
->cachedBlocks
[i
];
6854 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6855 if (*sector
== BLOCK_END_OF_CHAIN
)
6856 return STG_E_DOCFILECORRUPT
;
6860 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6861 result
= &This
->cachedBlocks
[0];
6862 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6863 result
= &This
->cachedBlocks
[1];
6866 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6867 if (This
->blockToEvict
== 2)
6868 This
->blockToEvict
= 0;
6873 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6874 return STG_E_WRITEFAULT
;
6875 result
->dirty
= FALSE
;
6878 result
->read
= FALSE
;
6879 result
->index
= index
;
6880 result
->sector
= *sector
;
6887 BlockChainStream
* BlockChainStream_Construct(
6888 StorageImpl
* parentStorage
,
6889 ULONG
* headOfStreamPlaceHolder
,
6892 BlockChainStream
* newStream
;
6894 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6896 newStream
->parentStorage
= parentStorage
;
6897 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6898 newStream
->ownerDirEntry
= dirEntry
;
6899 newStream
->indexCache
= NULL
;
6900 newStream
->indexCacheLen
= 0;
6901 newStream
->indexCacheSize
= 0;
6902 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6903 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6904 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6905 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6906 newStream
->blockToEvict
= 0;
6908 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6910 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6911 HeapFree(GetProcessHeap(), 0, newStream
);
6918 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6921 if (!This
) return S_OK
;
6924 if (This
->cachedBlocks
[i
].dirty
)
6926 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6927 This
->cachedBlocks
[i
].dirty
= FALSE
;
6929 return STG_E_WRITEFAULT
;
6935 void BlockChainStream_Destroy(BlockChainStream
* This
)
6939 BlockChainStream_Flush(This
);
6940 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6942 HeapFree(GetProcessHeap(), 0, This
);
6945 /******************************************************************************
6946 * BlockChainStream_GetHeadOfChain
6948 * Returns the head of this stream chain.
6949 * Some special chains don't have directory entries, their heads are kept in
6950 * This->headOfStreamPlaceHolder.
6953 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6955 DirEntry chainEntry
;
6958 if (This
->headOfStreamPlaceHolder
!= 0)
6959 return *(This
->headOfStreamPlaceHolder
);
6961 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6963 hr
= StorageImpl_ReadDirEntry(
6964 This
->parentStorage
,
6965 This
->ownerDirEntry
,
6970 return chainEntry
.startingBlock
;
6974 return BLOCK_END_OF_CHAIN
;
6977 /******************************************************************************
6978 * BlockChainStream_GetCount
6980 * Returns the number of blocks that comprises this chain.
6981 * This is not the size of the stream as the last block may not be full!
6983 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6985 return This
->numBlocks
;
6988 /******************************************************************************
6989 * BlockChainStream_ReadAt
6991 * Reads a specified number of bytes from this chain at the specified offset.
6992 * bytesRead may be NULL.
6993 * Failure will be returned if the specified number of bytes has not been read.
6995 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6996 ULARGE_INTEGER offset
,
7001 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7002 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
7003 ULONG bytesToReadInBuffer
;
7006 ULARGE_INTEGER stream_size
;
7008 BlockChainBlock
*cachedBlock
;
7010 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7013 * Find the first block in the stream that contains part of the buffer.
7015 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7019 stream_size
= BlockChainStream_GetSize(This
);
7020 if (stream_size
.QuadPart
> offset
.QuadPart
)
7021 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7026 * Start reading the buffer.
7028 bufferWalker
= buffer
;
7032 ULARGE_INTEGER ulOffset
;
7036 * Calculate how many bytes we can copy from this big block.
7038 bytesToReadInBuffer
=
7039 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7041 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7048 /* Not in cache, and we're going to read past the end of the block. */
7049 ulOffset
.u
.HighPart
= 0;
7050 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7053 StorageImpl_ReadAt(This
->parentStorage
,
7056 bytesToReadInBuffer
,
7061 if (!cachedBlock
->read
)
7064 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7065 return STG_E_READFAULT
;
7067 cachedBlock
->read
= TRUE
;
7070 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7071 bytesReadAt
= bytesToReadInBuffer
;
7074 blockNoInSequence
++;
7075 bufferWalker
+= bytesReadAt
;
7076 size
-= bytesReadAt
;
7077 *bytesRead
+= bytesReadAt
;
7078 offsetInBlock
= 0; /* There is no offset on the next block */
7080 if (bytesToReadInBuffer
!= bytesReadAt
)
7087 /******************************************************************************
7088 * BlockChainStream_WriteAt
7090 * Writes the specified number of bytes to this chain at the specified offset.
7091 * Will fail if not all specified number of bytes have been written.
7093 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7094 ULARGE_INTEGER offset
,
7097 ULONG
* bytesWritten
)
7099 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7100 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
7103 const BYTE
* bufferWalker
;
7105 BlockChainBlock
*cachedBlock
;
7108 bufferWalker
= buffer
;
7112 ULARGE_INTEGER ulOffset
;
7113 DWORD bytesWrittenAt
;
7116 * Calculate how many bytes we can copy to this big block.
7119 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7121 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7123 /* BlockChainStream_SetSize should have already been called to ensure we have
7124 * enough blocks in the chain to write into */
7127 ERR("not enough blocks in chain to write data\n");
7133 /* Not in cache, and we're going to write past the end of the block. */
7134 ulOffset
.u
.HighPart
= 0;
7135 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7138 StorageImpl_WriteAt(This
->parentStorage
,
7146 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7149 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7150 return STG_E_READFAULT
;
7153 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7154 bytesWrittenAt
= bytesToWrite
;
7155 cachedBlock
->read
= TRUE
;
7156 cachedBlock
->dirty
= TRUE
;
7159 blockNoInSequence
++;
7160 bufferWalker
+= bytesWrittenAt
;
7161 size
-= bytesWrittenAt
;
7162 *bytesWritten
+= bytesWrittenAt
;
7163 offsetInBlock
= 0; /* There is no offset on the next block */
7165 if (bytesWrittenAt
!= bytesToWrite
)
7169 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7172 /******************************************************************************
7173 * BlockChainStream_Shrink
7175 * Shrinks this chain in the big block depot.
7177 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7178 ULARGE_INTEGER newSize
)
7185 * Figure out how many blocks are needed to contain the new size
7187 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7189 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
7195 * Go to the new end of chain
7197 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7199 /* Mark the new end of chain */
7200 StorageImpl_SetNextBlockInChain(
7201 This
->parentStorage
,
7203 BLOCK_END_OF_CHAIN
);
7205 This
->tailIndex
= blockIndex
;
7209 if (This
->headOfStreamPlaceHolder
!= 0)
7211 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7215 DirEntry chainEntry
;
7216 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7218 StorageImpl_ReadDirEntry(
7219 This
->parentStorage
,
7220 This
->ownerDirEntry
,
7223 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7225 StorageImpl_WriteDirEntry(
7226 This
->parentStorage
,
7227 This
->ownerDirEntry
,
7231 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7234 This
->numBlocks
= numBlocks
;
7237 * Mark the extra blocks as free
7239 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7241 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7242 StorageImpl_FreeBigBlock(This
->parentStorage
,
7243 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7244 if (last_run
->lastOffset
== last_run
->firstOffset
)
7245 This
->indexCacheLen
--;
7247 last_run
->lastOffset
--;
7251 * Reset the last accessed block cache.
7255 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7257 This
->cachedBlocks
[i
].index
= 0xffffffff;
7258 This
->cachedBlocks
[i
].dirty
= FALSE
;
7265 /******************************************************************************
7266 * BlockChainStream_Enlarge
7268 * Grows this chain in the big block depot.
7270 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7271 ULARGE_INTEGER newSize
)
7273 ULONG blockIndex
, currentBlock
;
7275 ULONG oldNumBlocks
= 0;
7277 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7280 * Empty chain. Create the head.
7282 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7284 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7285 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7287 BLOCK_END_OF_CHAIN
);
7289 if (This
->headOfStreamPlaceHolder
!= 0)
7291 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7295 DirEntry chainEntry
;
7296 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7298 StorageImpl_ReadDirEntry(
7299 This
->parentStorage
,
7300 This
->ownerDirEntry
,
7303 chainEntry
.startingBlock
= blockIndex
;
7305 StorageImpl_WriteDirEntry(
7306 This
->parentStorage
,
7307 This
->ownerDirEntry
,
7311 This
->tailIndex
= blockIndex
;
7312 This
->numBlocks
= 1;
7316 * Figure out how many blocks are needed to contain this stream
7318 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
7320 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
7324 * Go to the current end of chain
7326 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7328 currentBlock
= blockIndex
;
7330 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7333 currentBlock
= blockIndex
;
7335 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7340 This
->tailIndex
= currentBlock
;
7343 currentBlock
= This
->tailIndex
;
7344 oldNumBlocks
= This
->numBlocks
;
7347 * Add new blocks to the chain
7349 if (oldNumBlocks
< newNumBlocks
)
7351 while (oldNumBlocks
< newNumBlocks
)
7353 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7355 StorageImpl_SetNextBlockInChain(
7356 This
->parentStorage
,
7360 StorageImpl_SetNextBlockInChain(
7361 This
->parentStorage
,
7363 BLOCK_END_OF_CHAIN
);
7365 currentBlock
= blockIndex
;
7369 This
->tailIndex
= blockIndex
;
7370 This
->numBlocks
= newNumBlocks
;
7373 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7379 /******************************************************************************
7380 * BlockChainStream_SetSize
7382 * Sets the size of this stream. The big block depot will be updated.
7383 * The file will grow if we grow the chain.
7385 * TODO: Free the actual blocks in the file when we shrink the chain.
7386 * Currently, the blocks are still in the file. So the file size
7387 * doesn't shrink even if we shrink streams.
7389 BOOL
BlockChainStream_SetSize(
7390 BlockChainStream
* This
,
7391 ULARGE_INTEGER newSize
)
7393 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7395 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7398 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7400 BlockChainStream_Shrink(This
, newSize
);
7404 BlockChainStream_Enlarge(This
, newSize
);
7410 /******************************************************************************
7411 * BlockChainStream_GetSize
7413 * Returns the size of this chain.
7414 * Will return the block count if this chain doesn't have a directory entry.
7416 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7418 DirEntry chainEntry
;
7420 if(This
->headOfStreamPlaceHolder
== NULL
)
7423 * This chain has a directory entry so use the size value from there.
7425 StorageImpl_ReadDirEntry(
7426 This
->parentStorage
,
7427 This
->ownerDirEntry
,
7430 return chainEntry
.size
;
7435 * this chain is a chain that does not have a directory entry, figure out the
7436 * size by making the product number of used blocks times the
7439 ULARGE_INTEGER result
;
7440 result
.u
.HighPart
= 0;
7443 BlockChainStream_GetCount(This
) *
7444 This
->parentStorage
->bigBlockSize
;
7450 /******************************************************************************
7451 ** SmallBlockChainStream implementation
7454 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7455 StorageImpl
* parentStorage
,
7456 ULONG
* headOfStreamPlaceHolder
,
7459 SmallBlockChainStream
* newStream
;
7461 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7463 newStream
->parentStorage
= parentStorage
;
7464 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7465 newStream
->ownerDirEntry
= dirEntry
;
7470 void SmallBlockChainStream_Destroy(
7471 SmallBlockChainStream
* This
)
7473 HeapFree(GetProcessHeap(), 0, This
);
7476 /******************************************************************************
7477 * SmallBlockChainStream_GetHeadOfChain
7479 * Returns the head of this chain of small blocks.
7481 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7482 SmallBlockChainStream
* This
)
7484 DirEntry chainEntry
;
7487 if (This
->headOfStreamPlaceHolder
!= NULL
)
7488 return *(This
->headOfStreamPlaceHolder
);
7490 if (This
->ownerDirEntry
)
7492 hr
= StorageImpl_ReadDirEntry(
7493 This
->parentStorage
,
7494 This
->ownerDirEntry
,
7499 return chainEntry
.startingBlock
;
7504 return BLOCK_END_OF_CHAIN
;
7507 /******************************************************************************
7508 * SmallBlockChainStream_GetNextBlockInChain
7510 * Returns the index of the next small block in this chain.
7513 * - BLOCK_END_OF_CHAIN: end of this chain
7514 * - BLOCK_UNUSED: small block 'blockIndex' is free
7516 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7517 SmallBlockChainStream
* This
,
7519 ULONG
* nextBlockInChain
)
7521 ULARGE_INTEGER offsetOfBlockInDepot
;
7526 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7528 offsetOfBlockInDepot
.u
.HighPart
= 0;
7529 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7532 * Read those bytes in the buffer from the small block file.
7534 res
= BlockChainStream_ReadAt(
7535 This
->parentStorage
->smallBlockDepotChain
,
7536 offsetOfBlockInDepot
,
7541 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7542 res
= STG_E_READFAULT
;
7546 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7553 /******************************************************************************
7554 * SmallBlockChainStream_SetNextBlockInChain
7556 * Writes the index of the next block of the specified block in the small
7558 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7559 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7561 static void SmallBlockChainStream_SetNextBlockInChain(
7562 SmallBlockChainStream
* This
,
7566 ULARGE_INTEGER offsetOfBlockInDepot
;
7570 offsetOfBlockInDepot
.u
.HighPart
= 0;
7571 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7573 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7576 * Read those bytes in the buffer from the small block file.
7578 BlockChainStream_WriteAt(
7579 This
->parentStorage
->smallBlockDepotChain
,
7580 offsetOfBlockInDepot
,
7586 /******************************************************************************
7587 * SmallBlockChainStream_FreeBlock
7589 * Flag small block 'blockIndex' as free in the small block depot.
7591 static void SmallBlockChainStream_FreeBlock(
7592 SmallBlockChainStream
* This
,
7595 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7598 /******************************************************************************
7599 * SmallBlockChainStream_GetNextFreeBlock
7601 * Returns the index of a free small block. The small block depot will be
7602 * enlarged if necessary. The small block chain will also be enlarged if
7605 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7606 SmallBlockChainStream
* This
)
7608 ULARGE_INTEGER offsetOfBlockInDepot
;
7611 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7612 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7614 ULONG smallBlocksPerBigBlock
;
7616 ULONG blocksRequired
;
7617 ULARGE_INTEGER old_size
, size_required
;
7619 offsetOfBlockInDepot
.u
.HighPart
= 0;
7622 * Scan the small block depot for a free block
7624 while (nextBlockIndex
!= BLOCK_UNUSED
)
7626 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
7628 res
= BlockChainStream_ReadAt(
7629 This
->parentStorage
->smallBlockDepotChain
,
7630 offsetOfBlockInDepot
,
7636 * If we run out of space for the small block depot, enlarge it
7638 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7640 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7642 if (nextBlockIndex
!= BLOCK_UNUSED
)
7648 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7650 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7651 ULARGE_INTEGER newSize
, offset
;
7654 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
7655 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7658 * Initialize all the small blocks to free
7660 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7661 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
7662 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7663 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7665 StorageImpl_SaveFileHeader(This
->parentStorage
);
7669 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7671 smallBlocksPerBigBlock
=
7672 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7675 * Verify if we have to allocate big blocks to contain small blocks
7677 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7679 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
7681 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7683 if (size_required
.QuadPart
> old_size
.QuadPart
)
7685 BlockChainStream_SetSize(
7686 This
->parentStorage
->smallBlockRootChain
,
7689 StorageImpl_ReadDirEntry(
7690 This
->parentStorage
,
7691 This
->parentStorage
->base
.storageDirEntry
,
7694 rootEntry
.size
= size_required
;
7696 StorageImpl_WriteDirEntry(
7697 This
->parentStorage
,
7698 This
->parentStorage
->base
.storageDirEntry
,
7705 /******************************************************************************
7706 * SmallBlockChainStream_ReadAt
7708 * Reads a specified number of bytes from this chain at the specified offset.
7709 * bytesRead may be NULL.
7710 * Failure will be returned if the specified number of bytes has not been read.
7712 HRESULT
SmallBlockChainStream_ReadAt(
7713 SmallBlockChainStream
* This
,
7714 ULARGE_INTEGER offset
,
7720 ULARGE_INTEGER offsetInBigBlockFile
;
7721 ULONG blockNoInSequence
=
7722 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7724 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7725 ULONG bytesToReadInBuffer
;
7727 ULONG bytesReadFromBigBlockFile
;
7729 ULARGE_INTEGER stream_size
;
7732 * This should never happen on a small block file.
7734 assert(offset
.u
.HighPart
==0);
7738 stream_size
= SmallBlockChainStream_GetSize(This
);
7739 if (stream_size
.QuadPart
> offset
.QuadPart
)
7740 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7745 * Find the first block in the stream that contains part of the buffer.
7747 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7749 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7751 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7754 blockNoInSequence
--;
7758 * Start reading the buffer.
7760 bufferWalker
= buffer
;
7762 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7765 * Calculate how many bytes we can copy from this small block.
7767 bytesToReadInBuffer
=
7768 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7771 * Calculate the offset of the small block in the small block file.
7773 offsetInBigBlockFile
.u
.HighPart
= 0;
7774 offsetInBigBlockFile
.u
.LowPart
=
7775 blockIndex
* This
->parentStorage
->smallBlockSize
;
7777 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
7780 * Read those bytes in the buffer from the small block file.
7781 * The small block has already been identified so it shouldn't fail
7782 * unless the file is corrupt.
7784 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7785 offsetInBigBlockFile
,
7786 bytesToReadInBuffer
,
7788 &bytesReadFromBigBlockFile
);
7793 if (!bytesReadFromBigBlockFile
)
7794 return STG_E_DOCFILECORRUPT
;
7797 * Step to the next big block.
7799 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7801 return STG_E_DOCFILECORRUPT
;
7803 bufferWalker
+= bytesReadFromBigBlockFile
;
7804 size
-= bytesReadFromBigBlockFile
;
7805 *bytesRead
+= bytesReadFromBigBlockFile
;
7806 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7812 /******************************************************************************
7813 * SmallBlockChainStream_WriteAt
7815 * Writes the specified number of bytes to this chain at the specified offset.
7816 * Will fail if not all specified number of bytes have been written.
7818 HRESULT
SmallBlockChainStream_WriteAt(
7819 SmallBlockChainStream
* This
,
7820 ULARGE_INTEGER offset
,
7823 ULONG
* bytesWritten
)
7825 ULARGE_INTEGER offsetInBigBlockFile
;
7826 ULONG blockNoInSequence
=
7827 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7829 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7830 ULONG bytesToWriteInBuffer
;
7832 ULONG bytesWrittenToBigBlockFile
;
7833 const BYTE
* bufferWalker
;
7837 * This should never happen on a small block file.
7839 assert(offset
.u
.HighPart
==0);
7842 * Find the first block in the stream that contains part of the buffer.
7844 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7846 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7848 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7849 return STG_E_DOCFILECORRUPT
;
7850 blockNoInSequence
--;
7854 * Start writing the buffer.
7857 bufferWalker
= buffer
;
7858 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7861 * Calculate how many bytes we can copy to this small block.
7863 bytesToWriteInBuffer
=
7864 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7867 * Calculate the offset of the small block in the small block file.
7869 offsetInBigBlockFile
.u
.HighPart
= 0;
7870 offsetInBigBlockFile
.u
.LowPart
=
7871 blockIndex
* This
->parentStorage
->smallBlockSize
;
7873 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
7876 * Write those bytes in the buffer to the small block file.
7878 res
= BlockChainStream_WriteAt(
7879 This
->parentStorage
->smallBlockRootChain
,
7880 offsetInBigBlockFile
,
7881 bytesToWriteInBuffer
,
7883 &bytesWrittenToBigBlockFile
);
7888 * Step to the next big block.
7890 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7893 bufferWalker
+= bytesWrittenToBigBlockFile
;
7894 size
-= bytesWrittenToBigBlockFile
;
7895 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7896 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7899 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7902 /******************************************************************************
7903 * SmallBlockChainStream_Shrink
7905 * Shrinks this chain in the small block depot.
7907 static BOOL
SmallBlockChainStream_Shrink(
7908 SmallBlockChainStream
* This
,
7909 ULARGE_INTEGER newSize
)
7911 ULONG blockIndex
, extraBlock
;
7915 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7917 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7920 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7923 * Go to the new end of chain
7925 while (count
< numBlocks
)
7927 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7934 * If the count is 0, we have a special case, the head of the chain was
7939 DirEntry chainEntry
;
7941 StorageImpl_ReadDirEntry(This
->parentStorage
,
7942 This
->ownerDirEntry
,
7945 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7947 StorageImpl_WriteDirEntry(This
->parentStorage
,
7948 This
->ownerDirEntry
,
7952 * We start freeing the chain at the head block.
7954 extraBlock
= blockIndex
;
7958 /* Get the next block before marking the new end */
7959 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7963 /* Mark the new end of chain */
7964 SmallBlockChainStream_SetNextBlockInChain(
7967 BLOCK_END_OF_CHAIN
);
7971 * Mark the extra blocks as free
7973 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7975 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7978 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7979 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7980 extraBlock
= blockIndex
;
7986 /******************************************************************************
7987 * SmallBlockChainStream_Enlarge
7989 * Grows this chain in the small block depot.
7991 static BOOL
SmallBlockChainStream_Enlarge(
7992 SmallBlockChainStream
* This
,
7993 ULARGE_INTEGER newSize
)
7995 ULONG blockIndex
, currentBlock
;
7997 ULONG oldNumBlocks
= 0;
7999 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8002 * Empty chain. Create the head.
8004 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8006 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8007 SmallBlockChainStream_SetNextBlockInChain(
8010 BLOCK_END_OF_CHAIN
);
8012 if (This
->headOfStreamPlaceHolder
!= NULL
)
8014 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8018 DirEntry chainEntry
;
8020 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8023 chainEntry
.startingBlock
= blockIndex
;
8025 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8030 currentBlock
= blockIndex
;
8033 * Figure out how many blocks are needed to contain this stream
8035 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8037 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8041 * Go to the current end of chain
8043 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8046 currentBlock
= blockIndex
;
8047 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8052 * Add new blocks to the chain
8054 while (oldNumBlocks
< newNumBlocks
)
8056 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8057 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8059 SmallBlockChainStream_SetNextBlockInChain(
8062 BLOCK_END_OF_CHAIN
);
8064 currentBlock
= blockIndex
;
8071 /******************************************************************************
8072 * SmallBlockChainStream_SetSize
8074 * Sets the size of this stream.
8075 * The file will grow if we grow the chain.
8077 * TODO: Free the actual blocks in the file when we shrink the chain.
8078 * Currently, the blocks are still in the file. So the file size
8079 * doesn't shrink even if we shrink streams.
8081 BOOL
SmallBlockChainStream_SetSize(
8082 SmallBlockChainStream
* This
,
8083 ULARGE_INTEGER newSize
)
8085 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8087 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8090 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8092 SmallBlockChainStream_Shrink(This
, newSize
);
8096 SmallBlockChainStream_Enlarge(This
, newSize
);
8102 /******************************************************************************
8103 * SmallBlockChainStream_GetCount
8105 * Returns the number of small blocks that comprises this chain.
8106 * This is not the size of the stream as the last block may not be full!
8109 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8114 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8116 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8121 blockIndex
, &blockIndex
)))
8128 /******************************************************************************
8129 * SmallBlockChainStream_GetSize
8131 * Returns the size of this chain.
8133 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8135 DirEntry chainEntry
;
8137 if(This
->headOfStreamPlaceHolder
!= NULL
)
8139 ULARGE_INTEGER result
;
8140 result
.u
.HighPart
= 0;
8142 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8143 This
->parentStorage
->smallBlockSize
;
8148 StorageImpl_ReadDirEntry(
8149 This
->parentStorage
,
8150 This
->ownerDirEntry
,
8153 return chainEntry
.size
;
8156 static HRESULT
create_storagefile(
8160 STGOPTIONS
* pStgOptions
,
8164 StorageBaseImpl
* newStorage
= 0;
8165 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8166 HRESULT hr
= STG_E_INVALIDFLAG
;
8170 DWORD fileAttributes
;
8171 WCHAR tempFileName
[MAX_PATH
];
8174 return STG_E_INVALIDPOINTER
;
8176 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8177 return STG_E_INVALIDPARAMETER
;
8179 /* if no share mode given then DENY_NONE is the default */
8180 if (STGM_SHARE_MODE(grfMode
) == 0)
8181 grfMode
|= STGM_SHARE_DENY_NONE
;
8183 if ( FAILED( validateSTGM(grfMode
) ))
8186 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8187 switch(STGM_ACCESS_MODE(grfMode
))
8190 case STGM_READWRITE
:
8196 /* in direct mode, can only use SHARE_EXCLUSIVE */
8197 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8200 /* but in transacted mode, any share mode is valid */
8203 * Generate a unique name.
8207 WCHAR tempPath
[MAX_PATH
];
8208 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8210 memset(tempPath
, 0, sizeof(tempPath
));
8211 memset(tempFileName
, 0, sizeof(tempFileName
));
8213 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8216 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8217 pwcsName
= tempFileName
;
8220 hr
= STG_E_INSUFFICIENTMEMORY
;
8224 creationMode
= TRUNCATE_EXISTING
;
8228 creationMode
= GetCreationModeFromSTGM(grfMode
);
8232 * Interpret the STGM value grfMode
8234 shareMode
= GetShareModeFromSTGM(grfMode
);
8235 accessMode
= GetAccessModeFromSTGM(grfMode
);
8237 if (grfMode
& STGM_DELETEONRELEASE
)
8238 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8240 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8244 hFile
= CreateFileW(pwcsName
,
8252 if (hFile
== INVALID_HANDLE_VALUE
)
8254 if(GetLastError() == ERROR_FILE_EXISTS
)
8255 hr
= STG_E_FILEALREADYEXISTS
;
8262 * Allocate and initialize the new IStorage32object.
8264 hr
= Storage_Construct(
8271 pStgOptions
->ulSectorSize
,
8279 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8280 IStorage_Release(&newStorage
->IStorage_iface
);
8283 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8288 /******************************************************************************
8289 * StgCreateDocfile [OLE32.@]
8290 * Creates a new compound file storage object
8293 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8294 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8295 * reserved [ ?] unused?, usually 0
8296 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8299 * S_OK if the file was successfully created
8300 * some STG_E_ value if error
8302 * if pwcsName is NULL, create file with new unique name
8303 * the function can returns
8304 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8307 HRESULT WINAPI
StgCreateDocfile(
8311 IStorage
**ppstgOpen
)
8313 STGOPTIONS stgoptions
= {1, 0, 512};
8315 TRACE("(%s, %x, %d, %p)\n",
8316 debugstr_w(pwcsName
), grfMode
,
8317 reserved
, ppstgOpen
);
8320 return STG_E_INVALIDPOINTER
;
8322 return STG_E_INVALIDPARAMETER
;
8324 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8327 /******************************************************************************
8328 * StgCreateStorageEx [OLE32.@]
8330 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8332 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8333 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8335 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8337 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8338 return STG_E_INVALIDPARAMETER
;
8341 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8343 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8344 return STG_E_INVALIDPARAMETER
;
8347 if (stgfmt
== STGFMT_FILE
)
8349 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8350 return STG_E_INVALIDPARAMETER
;
8353 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8355 STGOPTIONS defaultOptions
= {1, 0, 512};
8357 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8358 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8362 ERR("Invalid stgfmt argument\n");
8363 return STG_E_INVALIDPARAMETER
;
8366 /******************************************************************************
8367 * StgCreatePropSetStg [OLE32.@]
8369 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8370 IPropertySetStorage
**propset
)
8372 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8374 return STG_E_INVALIDPARAMETER
;
8376 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8379 /******************************************************************************
8380 * StgOpenStorageEx [OLE32.@]
8382 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8384 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8385 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8387 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8389 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8390 return STG_E_INVALIDPARAMETER
;
8396 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8397 return STG_E_INVALIDPARAMETER
;
8399 case STGFMT_STORAGE
:
8402 case STGFMT_DOCFILE
:
8403 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8405 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8406 return STG_E_INVALIDPARAMETER
;
8408 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8412 WARN("STGFMT_ANY assuming storage\n");
8416 return STG_E_INVALIDPARAMETER
;
8419 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8423 /******************************************************************************
8424 * StgOpenStorage [OLE32.@]
8426 HRESULT WINAPI
StgOpenStorage(
8427 const OLECHAR
*pwcsName
,
8428 IStorage
*pstgPriority
,
8432 IStorage
**ppstgOpen
)
8434 StorageBaseImpl
* newStorage
= 0;
8439 LPWSTR temp_name
= NULL
;
8441 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8442 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8443 snbExclude
, reserved
, ppstgOpen
);
8447 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8448 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8449 if (FAILED(hr
)) goto end
;
8450 pwcsName
= temp_name
;
8451 TRACE("using filename %s\n", debugstr_w(temp_name
));
8456 hr
= STG_E_INVALIDNAME
;
8462 hr
= STG_E_INVALIDPOINTER
;
8468 hr
= STG_E_INVALIDPARAMETER
;
8472 if (grfMode
& STGM_PRIORITY
)
8474 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8475 return STG_E_INVALIDFLAG
;
8476 if (grfMode
& STGM_DELETEONRELEASE
)
8477 return STG_E_INVALIDFUNCTION
;
8478 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8479 return STG_E_INVALIDFLAG
;
8480 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8481 grfMode
|= STGM_SHARE_DENY_NONE
;
8485 * Validate the sharing mode
8487 if (grfMode
& STGM_DIRECT_SWMR
)
8489 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8490 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8492 hr
= STG_E_INVALIDFLAG
;
8496 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8497 switch(STGM_SHARE_MODE(grfMode
))
8499 case STGM_SHARE_EXCLUSIVE
:
8500 case STGM_SHARE_DENY_WRITE
:
8503 hr
= STG_E_INVALIDFLAG
;
8507 if ( FAILED( validateSTGM(grfMode
) ) ||
8508 (grfMode
&STGM_CREATE
))
8510 hr
= STG_E_INVALIDFLAG
;
8514 /* shared reading requires transacted or single writer mode */
8515 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8516 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8517 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8519 hr
= STG_E_INVALIDFLAG
;
8524 * Interpret the STGM value grfMode
8526 shareMode
= GetShareModeFromSTGM(grfMode
);
8527 accessMode
= GetAccessModeFromSTGM(grfMode
);
8531 hFile
= CreateFileW( pwcsName
,
8536 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8539 if (hFile
==INVALID_HANDLE_VALUE
)
8541 DWORD last_error
= GetLastError();
8547 case ERROR_FILE_NOT_FOUND
:
8548 hr
= STG_E_FILENOTFOUND
;
8551 case ERROR_PATH_NOT_FOUND
:
8552 hr
= STG_E_PATHNOTFOUND
;
8555 case ERROR_ACCESS_DENIED
:
8556 case ERROR_WRITE_PROTECT
:
8557 hr
= STG_E_ACCESSDENIED
;
8560 case ERROR_SHARING_VIOLATION
:
8561 hr
= STG_E_SHAREVIOLATION
;
8572 * Refuse to open the file if it's too small to be a structured storage file
8573 * FIXME: verify the file when reading instead of here
8575 if (GetFileSize(hFile
, NULL
) < 0x100)
8578 hr
= STG_E_FILEALREADYEXISTS
;
8583 * Allocate and initialize the new IStorage32object.
8585 hr
= Storage_Construct(
8598 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8600 if(hr
== STG_E_INVALIDHEADER
)
8601 hr
= STG_E_FILEALREADYEXISTS
;
8605 *ppstgOpen
= &newStorage
->IStorage_iface
;
8608 CoTaskMemFree(temp_name
);
8609 if (pstgPriority
) IStorage_Release(pstgPriority
);
8610 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8614 /******************************************************************************
8615 * StgCreateDocfileOnILockBytes [OLE32.@]
8617 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8621 IStorage
** ppstgOpen
)
8623 StorageBaseImpl
* newStorage
= 0;
8626 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8627 return STG_E_INVALIDPOINTER
;
8630 * Allocate and initialize the new IStorage object.
8632 hr
= Storage_Construct(
8647 *ppstgOpen
= &newStorage
->IStorage_iface
;
8652 /******************************************************************************
8653 * StgOpenStorageOnILockBytes [OLE32.@]
8655 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8657 IStorage
*pstgPriority
,
8661 IStorage
**ppstgOpen
)
8663 StorageBaseImpl
* newStorage
= 0;
8666 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8667 return STG_E_INVALIDPOINTER
;
8669 if ( FAILED( validateSTGM(grfMode
) ))
8670 return STG_E_INVALIDFLAG
;
8675 * Allocate and initialize the new IStorage object.
8677 hr
= Storage_Construct(
8692 *ppstgOpen
= &newStorage
->IStorage_iface
;
8697 /******************************************************************************
8698 * StgSetTimes [ole32.@]
8699 * StgSetTimes [OLE32.@]
8703 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8704 FILETIME
const *patime
, FILETIME
const *pmtime
)
8706 IStorage
*stg
= NULL
;
8709 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8711 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8715 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8716 IStorage_Release(stg
);
8722 /******************************************************************************
8723 * StgIsStorageILockBytes [OLE32.@]
8725 * Determines if the ILockBytes contains a storage object.
8727 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8729 BYTE sig
[sizeof(STORAGE_magic
)];
8730 ULARGE_INTEGER offset
;
8733 offset
.u
.HighPart
= 0;
8734 offset
.u
.LowPart
= 0;
8736 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8738 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8744 /******************************************************************************
8745 * WriteClassStg [OLE32.@]
8747 * This method will store the specified CLSID in the specified storage object
8749 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8752 return E_INVALIDARG
;
8755 return STG_E_INVALIDPOINTER
;
8757 return IStorage_SetClass(pStg
, rclsid
);
8760 /***********************************************************************
8761 * ReadClassStg (OLE32.@)
8763 * This method reads the CLSID previously written to a storage object with
8764 * the WriteClassStg.
8767 * pstg [I] IStorage pointer
8768 * pclsid [O] Pointer to where the CLSID is written
8772 * Failure: HRESULT code.
8774 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8779 TRACE("(%p, %p)\n", pstg
, pclsid
);
8781 if(!pstg
|| !pclsid
)
8782 return E_INVALIDARG
;
8785 * read a STATSTG structure (contains the clsid) from the storage
8787 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8790 *pclsid
=pstatstg
.clsid
;
8795 /***********************************************************************
8796 * OleLoadFromStream (OLE32.@)
8798 * This function loads an object from stream
8800 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8804 LPPERSISTSTREAM xstm
;
8806 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8808 res
=ReadClassStm(pStm
,&clsid
);
8811 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8814 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8816 IUnknown_Release((IUnknown
*)*ppvObj
);
8819 res
=IPersistStream_Load(xstm
,pStm
);
8820 IPersistStream_Release(xstm
);
8821 /* FIXME: all refcounts ok at this point? I think they should be:
8824 * xstm : 0 (released)
8829 /***********************************************************************
8830 * OleSaveToStream (OLE32.@)
8832 * This function saves an object with the IPersistStream interface on it
8833 * to the specified stream.
8835 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8841 TRACE("(%p,%p)\n",pPStm
,pStm
);
8843 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8845 if (SUCCEEDED(res
)){
8847 res
=WriteClassStm(pStm
,&clsid
);
8851 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8854 TRACE("Finished Save\n");
8858 /****************************************************************************
8859 * This method validate a STGM parameter that can contain the values below
8861 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8862 * The stgm values contained in 0xffff0000 are bitmasks.
8864 * STGM_DIRECT 0x00000000
8865 * STGM_TRANSACTED 0x00010000
8866 * STGM_SIMPLE 0x08000000
8868 * STGM_READ 0x00000000
8869 * STGM_WRITE 0x00000001
8870 * STGM_READWRITE 0x00000002
8872 * STGM_SHARE_DENY_NONE 0x00000040
8873 * STGM_SHARE_DENY_READ 0x00000030
8874 * STGM_SHARE_DENY_WRITE 0x00000020
8875 * STGM_SHARE_EXCLUSIVE 0x00000010
8877 * STGM_PRIORITY 0x00040000
8878 * STGM_DELETEONRELEASE 0x04000000
8880 * STGM_CREATE 0x00001000
8881 * STGM_CONVERT 0x00020000
8882 * STGM_FAILIFTHERE 0x00000000
8884 * STGM_NOSCRATCH 0x00100000
8885 * STGM_NOSNAPSHOT 0x00200000
8887 static HRESULT
validateSTGM(DWORD stgm
)
8889 DWORD access
= STGM_ACCESS_MODE(stgm
);
8890 DWORD share
= STGM_SHARE_MODE(stgm
);
8891 DWORD create
= STGM_CREATE_MODE(stgm
);
8893 if (stgm
&~STGM_KNOWN_FLAGS
)
8895 ERR("unknown flags %08x\n", stgm
);
8903 case STGM_READWRITE
:
8911 case STGM_SHARE_DENY_NONE
:
8912 case STGM_SHARE_DENY_READ
:
8913 case STGM_SHARE_DENY_WRITE
:
8914 case STGM_SHARE_EXCLUSIVE
:
8917 if (!(stgm
& STGM_TRANSACTED
))
8927 case STGM_FAILIFTHERE
:
8934 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8936 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8940 * STGM_CREATE | STGM_CONVERT
8941 * if both are false, STGM_FAILIFTHERE is set to TRUE
8943 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8947 * STGM_NOSCRATCH requires STGM_TRANSACTED
8949 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8953 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8954 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8956 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8957 (!(stgm
& STGM_TRANSACTED
) ||
8958 share
== STGM_SHARE_EXCLUSIVE
||
8959 share
== STGM_SHARE_DENY_WRITE
) )
8965 /****************************************************************************
8966 * GetShareModeFromSTGM
8968 * This method will return a share mode flag from a STGM value.
8969 * The STGM value is assumed valid.
8971 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8973 switch (STGM_SHARE_MODE(stgm
))
8976 assert(stgm
& STGM_TRANSACTED
);
8978 case STGM_SHARE_DENY_NONE
:
8979 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8980 case STGM_SHARE_DENY_READ
:
8981 return FILE_SHARE_WRITE
;
8982 case STGM_SHARE_DENY_WRITE
:
8983 case STGM_SHARE_EXCLUSIVE
:
8984 return FILE_SHARE_READ
;
8986 ERR("Invalid share mode!\n");
8991 /****************************************************************************
8992 * GetAccessModeFromSTGM
8994 * This method will return an access mode flag from a STGM value.
8995 * The STGM value is assumed valid.
8997 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8999 switch (STGM_ACCESS_MODE(stgm
))
9002 return GENERIC_READ
;
9004 case STGM_READWRITE
:
9005 return GENERIC_READ
| GENERIC_WRITE
;
9007 ERR("Invalid access mode!\n");
9012 /****************************************************************************
9013 * GetCreationModeFromSTGM
9015 * This method will return a creation mode flag from a STGM value.
9016 * The STGM value is assumed valid.
9018 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9020 switch(STGM_CREATE_MODE(stgm
))
9023 return CREATE_ALWAYS
;
9025 FIXME("STGM_CONVERT not implemented!\n");
9027 case STGM_FAILIFTHERE
:
9030 ERR("Invalid create mode!\n");
9036 /*************************************************************************
9037 * OLECONVERT_LoadOLE10 [Internal]
9039 * Loads the OLE10 STREAM to memory
9042 * pOleStream [I] The OLESTREAM
9043 * pData [I] Data Structure for the OLESTREAM Data
9047 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9048 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9051 * This function is used by OleConvertOLESTREAMToIStorage only.
9053 * Memory allocated for pData must be freed by the caller
9055 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9058 HRESULT hRes
= S_OK
;
9062 pData
->pData
= NULL
;
9063 pData
->pstrOleObjFileName
= NULL
;
9065 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9068 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9069 if(dwSize
!= sizeof(pData
->dwOleID
))
9071 hRes
= CONVERT10_E_OLESTREAM_GET
;
9073 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9075 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9086 /* Get the TypeID... more info needed for this field */
9087 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9088 if(dwSize
!= sizeof(pData
->dwTypeID
))
9090 hRes
= CONVERT10_E_OLESTREAM_GET
;
9095 if(pData
->dwTypeID
!= 0)
9097 /* Get the length of the OleTypeName */
9098 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9099 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9101 hRes
= CONVERT10_E_OLESTREAM_GET
;
9106 if(pData
->dwOleTypeNameLength
> 0)
9108 /* Get the OleTypeName */
9109 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9110 if(dwSize
!= pData
->dwOleTypeNameLength
)
9112 hRes
= CONVERT10_E_OLESTREAM_GET
;
9118 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9119 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9121 hRes
= CONVERT10_E_OLESTREAM_GET
;
9125 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9126 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9127 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9128 if(pData
->pstrOleObjFileName
)
9130 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9131 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9133 hRes
= CONVERT10_E_OLESTREAM_GET
;
9137 hRes
= CONVERT10_E_OLESTREAM_GET
;
9142 /* Get the Width of the Metafile */
9143 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9144 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9146 hRes
= CONVERT10_E_OLESTREAM_GET
;
9150 /* Get the Height of the Metafile */
9151 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9152 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9154 hRes
= CONVERT10_E_OLESTREAM_GET
;
9160 /* Get the Length of the Data */
9161 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9162 if(dwSize
!= sizeof(pData
->dwDataLength
))
9164 hRes
= CONVERT10_E_OLESTREAM_GET
;
9168 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9170 if(!bStrem1
) /* if it is a second OLE stream data */
9172 pData
->dwDataLength
-= 8;
9173 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9174 if(dwSize
!= sizeof(pData
->strUnknown
))
9176 hRes
= CONVERT10_E_OLESTREAM_GET
;
9182 if(pData
->dwDataLength
> 0)
9184 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9186 /* Get Data (ex. IStorage, Metafile, or BMP) */
9189 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9190 if(dwSize
!= pData
->dwDataLength
)
9192 hRes
= CONVERT10_E_OLESTREAM_GET
;
9197 hRes
= CONVERT10_E_OLESTREAM_GET
;
9206 /*************************************************************************
9207 * OLECONVERT_SaveOLE10 [Internal]
9209 * Saves the OLE10 STREAM From memory
9212 * pData [I] Data Structure for the OLESTREAM Data
9213 * pOleStream [I] The OLESTREAM to save
9217 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9220 * This function is used by OleConvertIStorageToOLESTREAM only.
9223 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9226 HRESULT hRes
= S_OK
;
9230 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9231 if(dwSize
!= sizeof(pData
->dwOleID
))
9233 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9238 /* Set the TypeID */
9239 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9240 if(dwSize
!= sizeof(pData
->dwTypeID
))
9242 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9246 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9248 /* Set the Length of the OleTypeName */
9249 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9250 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9252 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9257 if(pData
->dwOleTypeNameLength
> 0)
9259 /* Set the OleTypeName */
9260 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9261 if(dwSize
!= pData
->dwOleTypeNameLength
)
9263 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9270 /* Set the width of the Metafile */
9271 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9272 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9274 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9280 /* Set the height of the Metafile */
9281 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9282 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9284 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9290 /* Set the length of the Data */
9291 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9292 if(dwSize
!= sizeof(pData
->dwDataLength
))
9294 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9300 if(pData
->dwDataLength
> 0)
9302 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9303 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9304 if(dwSize
!= pData
->dwDataLength
)
9306 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9314 /*************************************************************************
9315 * OLECONVERT_GetOLE20FromOLE10[Internal]
9317 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9318 * opens it, and copies the content to the dest IStorage for
9319 * OleConvertOLESTREAMToIStorage
9323 * pDestStorage [I] The IStorage to copy the data to
9324 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9325 * nBufferLength [I] The size of the buffer
9334 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9338 IStorage
*pTempStorage
;
9339 DWORD dwNumOfBytesWritten
;
9340 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9341 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9343 /* Create a temp File */
9344 GetTempPathW(MAX_PATH
, wstrTempDir
);
9345 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9346 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9348 if(hFile
!= INVALID_HANDLE_VALUE
)
9350 /* Write IStorage Data to File */
9351 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9354 /* Open and copy temp storage to the Dest Storage */
9355 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9358 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9359 IStorage_Release(pTempStorage
);
9361 DeleteFileW(wstrTempFile
);
9366 /*************************************************************************
9367 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9369 * Saves the OLE10 STREAM From memory
9372 * pStorage [I] The Src IStorage to copy
9373 * pData [I] The Dest Memory to write to.
9376 * The size in bytes allocated for pData
9379 * Memory allocated for pData must be freed by the caller
9381 * Used by OleConvertIStorageToOLESTREAM only.
9384 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9388 DWORD nDataLength
= 0;
9389 IStorage
*pTempStorage
;
9390 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9391 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9395 /* Create temp Storage */
9396 GetTempPathW(MAX_PATH
, wstrTempDir
);
9397 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9398 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9402 /* Copy Src Storage to the Temp Storage */
9403 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9404 IStorage_Release(pTempStorage
);
9406 /* Open Temp Storage as a file and copy to memory */
9407 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9408 if(hFile
!= INVALID_HANDLE_VALUE
)
9410 nDataLength
= GetFileSize(hFile
, NULL
);
9411 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9412 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9415 DeleteFileW(wstrTempFile
);
9420 /*************************************************************************
9421 * STORAGE_CreateOleStream [Internal]
9423 * Creates the "\001OLE" stream in the IStorage if necessary.
9426 * storage [I] Dest storage to create the stream in
9427 * flags [I] flags to be set for newly created stream
9430 * HRESULT return value
9434 * This stream is still unknown, MS Word seems to have extra data
9435 * but since the data is stored in the OLESTREAM there should be
9436 * no need to recreate the stream. If the stream is manually
9437 * deleted it will create it with this default data.
9440 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9442 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9443 static const DWORD version_magic
= 0x02000001;
9447 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9450 struct empty_1ole_stream
{
9451 DWORD version_magic
;
9453 DWORD update_options
;
9455 DWORD mon_stream_size
;
9457 struct empty_1ole_stream stream_data
;
9459 stream_data
.version_magic
= version_magic
;
9460 stream_data
.flags
= flags
;
9461 stream_data
.update_options
= 0;
9462 stream_data
.reserved
= 0;
9463 stream_data
.mon_stream_size
= 0;
9465 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9466 IStream_Release(stream
);
9472 /* write a string to a stream, preceded by its length */
9473 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9480 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9481 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9486 str
= CoTaskMemAlloc( len
);
9487 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9488 r
= IStream_Write( stm
, str
, len
, NULL
);
9489 CoTaskMemFree( str
);
9493 /* read a string preceded by its length from a stream */
9494 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9497 DWORD len
, count
= 0;
9501 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9504 if( count
!= sizeof(len
) )
9505 return E_OUTOFMEMORY
;
9507 TRACE("%d bytes\n",len
);
9509 str
= CoTaskMemAlloc( len
);
9511 return E_OUTOFMEMORY
;
9513 r
= IStream_Read( stm
, str
, len
, &count
);
9518 CoTaskMemFree( str
);
9519 return E_OUTOFMEMORY
;
9522 TRACE("Read string %s\n",debugstr_an(str
,len
));
9524 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9525 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9528 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9531 CoTaskMemFree( str
);
9539 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9540 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9544 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9546 static const BYTE unknown1
[12] =
9547 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9548 0xFF, 0xFF, 0xFF, 0xFF};
9549 static const BYTE unknown2
[16] =
9550 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9553 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9554 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9555 debugstr_w(szProgIDName
));
9557 /* Create a CompObj stream */
9558 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9559 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9563 /* Write CompObj Structure to stream */
9564 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9566 if( SUCCEEDED( r
) )
9567 r
= WriteClassStm( pstm
, clsid
);
9569 if( SUCCEEDED( r
) )
9570 r
= STREAM_WriteString( pstm
, lpszUserType
);
9571 if( SUCCEEDED( r
) )
9572 r
= STREAM_WriteString( pstm
, szClipName
);
9573 if( SUCCEEDED( r
) )
9574 r
= STREAM_WriteString( pstm
, szProgIDName
);
9575 if( SUCCEEDED( r
) )
9576 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9578 IStream_Release( pstm
);
9583 /***********************************************************************
9584 * WriteFmtUserTypeStg (OLE32.@)
9586 HRESULT WINAPI
WriteFmtUserTypeStg(
9587 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9591 WCHAR szwClipName
[0x40];
9593 LPWSTR wstrProgID
= NULL
;
9596 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9598 /* get the clipboard format name */
9601 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9602 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9606 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9608 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9614 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9616 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9618 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9619 cf
? szwClipName
: NULL
, wstrProgID
);
9621 CoTaskMemFree(wstrProgID
);
9627 /******************************************************************************
9628 * ReadFmtUserTypeStg [OLE32.@]
9630 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9634 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9635 unsigned char unknown1
[12];
9636 unsigned char unknown2
[16];
9638 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9641 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9643 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9644 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9647 WARN("Failed to open stream r = %08x\n", r
);
9651 /* read the various parts of the structure */
9652 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9653 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9655 r
= ReadClassStm( stm
, &clsid
);
9659 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9663 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9667 r
= STREAM_ReadString( stm
, &szProgIDName
);
9671 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9672 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9675 /* ok, success... now we just need to store what we found */
9677 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9679 if( lplpszUserType
)
9681 *lplpszUserType
= szCLSIDName
;
9686 CoTaskMemFree( szCLSIDName
);
9687 CoTaskMemFree( szOleTypeName
);
9688 CoTaskMemFree( szProgIDName
);
9689 IStream_Release( stm
);
9695 /*************************************************************************
9696 * OLECONVERT_CreateCompObjStream [Internal]
9698 * Creates a "\001CompObj" is the destination IStorage if necessary.
9701 * pStorage [I] The dest IStorage to create the CompObj Stream
9703 * strOleTypeName [I] The ProgID
9707 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9710 * This function is used by OleConvertOLESTREAMToIStorage only.
9712 * The stream data is stored in the OLESTREAM and there should be
9713 * no need to recreate the stream. If the stream is manually
9714 * deleted it will attempt to create it by querying the registry.
9718 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9721 HRESULT hStorageRes
, hRes
= S_OK
;
9722 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9723 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9724 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9726 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9727 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9729 /* Initialize the CompObj structure */
9730 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9731 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9732 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9735 /* Create a CompObj stream if it doesn't exist */
9736 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9737 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9738 if(hStorageRes
== S_OK
)
9740 /* copy the OleTypeName to the compobj struct */
9741 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9742 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9744 /* copy the OleTypeName to the compobj struct */
9745 /* Note: in the test made, these were Identical */
9746 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9747 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9750 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9751 bufferW
, OLESTREAM_MAX_STR_LEN
);
9752 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9758 /* Get the CLSID Default Name from the Registry */
9759 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9760 if(hErr
== ERROR_SUCCESS
)
9762 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9763 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9764 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9765 if(hErr
== ERROR_SUCCESS
)
9767 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9773 /* Write CompObj Structure to stream */
9774 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9776 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9778 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9779 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9781 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9783 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9784 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9786 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9788 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9789 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9791 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9793 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9794 IStream_Release(pStream
);
9800 /*************************************************************************
9801 * OLECONVERT_CreateOlePresStream[Internal]
9803 * Creates the "\002OlePres000" Stream with the Metafile data
9806 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9807 * dwExtentX [I] Width of the Metafile
9808 * dwExtentY [I] Height of the Metafile
9809 * pData [I] Metafile data
9810 * dwDataLength [I] Size of the Metafile data
9814 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9817 * This function is used by OleConvertOLESTREAMToIStorage only.
9820 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9824 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9825 BYTE pOlePresStreamHeader
[] =
9827 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9828 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9829 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9830 0x00, 0x00, 0x00, 0x00
9833 BYTE pOlePresStreamHeaderEmpty
[] =
9835 0x00, 0x00, 0x00, 0x00,
9836 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9837 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9838 0x00, 0x00, 0x00, 0x00
9841 /* Create the OlePres000 Stream */
9842 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9843 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9848 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9850 memset(&OlePres
, 0, sizeof(OlePres
));
9851 /* Do we have any metafile data to save */
9852 if(dwDataLength
> 0)
9854 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9855 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9859 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9860 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9862 /* Set width and height of the metafile */
9863 OlePres
.dwExtentX
= dwExtentX
;
9864 OlePres
.dwExtentY
= -dwExtentY
;
9866 /* Set Data and Length */
9867 if(dwDataLength
> sizeof(METAFILEPICT16
))
9869 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9870 OlePres
.pData
= &(pData
[8]);
9872 /* Save OlePres000 Data to Stream */
9873 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9874 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9875 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9876 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9877 if(OlePres
.dwSize
> 0)
9879 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9881 IStream_Release(pStream
);
9885 /*************************************************************************
9886 * OLECONVERT_CreateOle10NativeStream [Internal]
9888 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9891 * pStorage [I] Dest storage to create the stream in
9892 * pData [I] Ole10 Native Data (ex. bmp)
9893 * dwDataLength [I] Size of the Ole10 Native Data
9899 * This function is used by OleConvertOLESTREAMToIStorage only.
9901 * Might need to verify the data and return appropriate error message
9904 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9908 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9910 /* Create the Ole10Native Stream */
9911 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9912 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9916 /* Write info to stream */
9917 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9918 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9919 IStream_Release(pStream
);
9924 /*************************************************************************
9925 * OLECONVERT_GetOLE10ProgID [Internal]
9927 * Finds the ProgID (or OleTypeID) from the IStorage
9930 * pStorage [I] The Src IStorage to get the ProgID
9931 * strProgID [I] the ProgID string to get
9932 * dwSize [I] the size of the string
9936 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9939 * This function is used by OleConvertIStorageToOLESTREAM only.
9943 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9947 LARGE_INTEGER iSeekPos
;
9948 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9949 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9951 /* Open the CompObj Stream */
9952 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9953 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9957 /*Get the OleType from the CompObj Stream */
9958 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9959 iSeekPos
.u
.HighPart
= 0;
9961 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9962 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9963 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9964 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9965 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9966 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9967 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9969 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9972 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9974 IStream_Release(pStream
);
9979 LPOLESTR wstrProgID
;
9981 /* Get the OleType from the registry */
9982 REFCLSID clsid
= &(stat
.clsid
);
9983 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9984 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9987 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9988 CoTaskMemFree(wstrProgID
);
9995 /*************************************************************************
9996 * OLECONVERT_GetOle10PresData [Internal]
9998 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10001 * pStorage [I] Src IStroage
10002 * pOleStream [I] Dest OleStream Mem Struct
10008 * This function is used by OleConvertIStorageToOLESTREAM only.
10010 * Memory allocated for pData must be freed by the caller
10014 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10019 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10021 /* Initialize Default data for OLESTREAM */
10022 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10023 pOleStreamData
[0].dwTypeID
= 2;
10024 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10025 pOleStreamData
[1].dwTypeID
= 0;
10026 pOleStreamData
[0].dwMetaFileWidth
= 0;
10027 pOleStreamData
[0].dwMetaFileHeight
= 0;
10028 pOleStreamData
[0].pData
= NULL
;
10029 pOleStreamData
[1].pData
= NULL
;
10031 /* Open Ole10Native Stream */
10032 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10033 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10037 /* Read Size and Data */
10038 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10039 if(pOleStreamData
->dwDataLength
> 0)
10041 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10042 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10044 IStream_Release(pStream
);
10050 /*************************************************************************
10051 * OLECONVERT_GetOle20PresData[Internal]
10053 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10056 * pStorage [I] Src IStroage
10057 * pOleStreamData [I] Dest OleStream Mem Struct
10063 * This function is used by OleConvertIStorageToOLESTREAM only.
10065 * Memory allocated for pData must be freed by the caller
10067 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10071 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10072 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10074 /* Initialize Default data for OLESTREAM */
10075 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10076 pOleStreamData
[0].dwTypeID
= 2;
10077 pOleStreamData
[0].dwMetaFileWidth
= 0;
10078 pOleStreamData
[0].dwMetaFileHeight
= 0;
10079 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10080 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10081 pOleStreamData
[1].dwTypeID
= 0;
10082 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10083 pOleStreamData
[1].strOleTypeName
[0] = 0;
10084 pOleStreamData
[1].dwMetaFileWidth
= 0;
10085 pOleStreamData
[1].dwMetaFileHeight
= 0;
10086 pOleStreamData
[1].pData
= NULL
;
10087 pOleStreamData
[1].dwDataLength
= 0;
10090 /* Open OlePress000 stream */
10091 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10092 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10095 LARGE_INTEGER iSeekPos
;
10096 METAFILEPICT16 MetaFilePict
;
10097 static const char strMetafilePictName
[] = "METAFILEPICT";
10099 /* Set the TypeID for a Metafile */
10100 pOleStreamData
[1].dwTypeID
= 5;
10102 /* Set the OleTypeName to Metafile */
10103 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10104 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10106 iSeekPos
.u
.HighPart
= 0;
10107 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10109 /* Get Presentation Data */
10110 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10111 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10112 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10113 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10115 /*Set width and Height */
10116 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10117 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10118 if(olePress
.dwSize
> 0)
10121 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10123 /* Set MetaFilePict struct */
10124 MetaFilePict
.mm
= 8;
10125 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10126 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10127 MetaFilePict
.hMF
= 0;
10129 /* Get Metafile Data */
10130 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10131 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10132 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10134 IStream_Release(pStream
);
10138 /*************************************************************************
10139 * OleConvertOLESTREAMToIStorage [OLE32.@]
10141 * Read info on MSDN
10144 * DVTARGETDEVICE parameter is not handled
10145 * Still unsure of some mem fields for OLE 10 Stream
10146 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10147 * and "\001OLE" streams
10150 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10151 LPOLESTREAM pOleStream
,
10153 const DVTARGETDEVICE
* ptd
)
10157 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10159 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10161 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10165 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10168 if(pstg
== NULL
|| pOleStream
== NULL
)
10170 hRes
= E_INVALIDARG
;
10175 /* Load the OLESTREAM to Memory */
10176 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10181 /* Load the OLESTREAM to Memory (part 2)*/
10182 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10188 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10190 /* Do we have the IStorage Data in the OLESTREAM */
10191 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10193 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10194 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10198 /* It must be an original OLE 1.0 source */
10199 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10204 /* It must be an original OLE 1.0 source */
10205 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10208 /* Create CompObj Stream if necessary */
10209 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10212 /*Create the Ole Stream if necessary */
10213 STORAGE_CreateOleStream(pstg
, 0);
10218 /* Free allocated memory */
10219 for(i
=0; i
< 2; i
++)
10221 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10222 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10223 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10228 /*************************************************************************
10229 * OleConvertIStorageToOLESTREAM [OLE32.@]
10231 * Read info on MSDN
10233 * Read info on MSDN
10236 * Still unsure of some mem fields for OLE 10 Stream
10237 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10238 * and "\001OLE" streams.
10241 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10243 LPOLESTREAM pOleStream
)
10246 HRESULT hRes
= S_OK
;
10248 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10249 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10251 TRACE("%p %p\n", pstg
, pOleStream
);
10253 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10255 if(pstg
== NULL
|| pOleStream
== NULL
)
10257 hRes
= E_INVALIDARG
;
10261 /* Get the ProgID */
10262 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10263 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10267 /* Was it originally Ole10 */
10268 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10271 IStream_Release(pStream
);
10272 /* Get Presentation Data for Ole10Native */
10273 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10277 /* Get Presentation Data (OLE20) */
10278 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10281 /* Save OLESTREAM */
10282 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10285 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10290 /* Free allocated memory */
10291 for(i
=0; i
< 2; i
++)
10293 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10299 enum stream_1ole_flags
{
10300 OleStream_LinkedObject
= 0x00000001,
10301 OleStream_Convert
= 0x00000004
10304 /***********************************************************************
10305 * GetConvertStg (OLE32.@)
10307 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10309 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10310 static const DWORD version_magic
= 0x02000001;
10315 TRACE("%p\n", stg
);
10317 if (!stg
) return E_INVALIDARG
;
10319 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10320 if (FAILED(hr
)) return hr
;
10322 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10323 IStream_Release(stream
);
10324 if (FAILED(hr
)) return hr
;
10326 if (header
[0] != version_magic
)
10328 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10332 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10335 /***********************************************************************
10336 * SetConvertStg (OLE32.@)
10338 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10340 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10341 DWORD flags
= convert
? OleStream_Convert
: 0;
10346 TRACE("(%p, %d)\n", storage
, convert
);
10348 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10351 if (hr
!= STG_E_FILENOTFOUND
)
10354 return STORAGE_CreateOleStream(storage
, flags
);
10357 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10360 IStream_Release(stream
);
10364 /* update flag if differs */
10365 if ((header
[1] ^ flags
) & OleStream_Convert
)
10367 LARGE_INTEGER pos
= {{0}};
10369 if (header
[1] & OleStream_Convert
)
10370 flags
= header
[1] & ~OleStream_Convert
;
10372 flags
= header
[1] | OleStream_Convert
;
10374 pos
.QuadPart
= sizeof(DWORD
);
10375 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10378 IStream_Release(stream
);
10382 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10385 IStream_Release(stream
);
10389 /******************************************************************************
10390 * StgIsStorageFile [OLE32.@]
10391 * Verify if the file contains a storage object
10397 * S_OK if file has magic bytes as a storage object
10398 * S_FALSE if file is not storage
10401 StgIsStorageFile(LPCOLESTR fn
)
10407 TRACE("%s\n", debugstr_w(fn
));
10408 hf
= CreateFileW(fn
, GENERIC_READ
,
10409 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10410 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10412 if (hf
== INVALID_HANDLE_VALUE
)
10413 return STG_E_FILENOTFOUND
;
10415 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10417 WARN(" unable to read file\n");
10424 if (bytes_read
!= 8) {
10425 TRACE(" too short\n");
10429 if (!memcmp(magic
,STORAGE_magic
,8)) {
10430 TRACE(" -> YES\n");
10434 TRACE(" -> Invalid header.\n");
10438 /***********************************************************************
10439 * WriteClassStm (OLE32.@)
10441 * Writes a CLSID to a stream.
10444 * pStm [I] Stream to write to.
10445 * rclsid [I] CLSID to write.
10449 * Failure: HRESULT code.
10451 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10453 TRACE("(%p,%p)\n",pStm
,rclsid
);
10455 if (!pStm
|| !rclsid
)
10456 return E_INVALIDARG
;
10458 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10461 /***********************************************************************
10462 * ReadClassStm (OLE32.@)
10464 * Reads a CLSID from a stream.
10467 * pStm [I] Stream to read from.
10468 * rclsid [O] CLSID to read.
10472 * Failure: HRESULT code.
10474 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10479 TRACE("(%p,%p)\n",pStm
,pclsid
);
10481 if (!pStm
|| !pclsid
)
10482 return E_INVALIDARG
;
10484 /* clear the output args */
10485 *pclsid
= CLSID_NULL
;
10487 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10492 if (nbByte
!= sizeof(CLSID
))
10493 return STG_E_READFAULT
;