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.
34 #include "storage32.h"
36 #include <wine/wingdi16.h>
38 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
42 * These are signatures to detect the type of Document file.
44 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
45 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
47 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
50 /****************************************************************************
51 * StorageInternalImpl definitions.
53 * Definition of the implementation structure for the IStorage interface.
54 * This one implements the IStorage interface for storage that are
55 * inside another storage.
57 typedef struct StorageInternalImpl
59 struct StorageBaseImpl base
;
62 * Entry in the parent's stream tracking list
64 struct list ParentListEntry
;
66 StorageBaseImpl
*parentStorage
;
67 } StorageInternalImpl
;
69 static const IStorageVtbl StorageInternalImpl_Vtbl
;
70 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
*,DWORD
,DirRef
);
72 typedef struct TransactedDirEntry
74 /* If applicable, a reference to the original DirEntry in the transacted
75 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
76 DirRef transactedParentEntry
;
78 /* True if this entry is being used. */
81 /* True if data is up to date. */
84 /* True if this entry has been modified. */
87 /* True if this entry's stream has been modified. */
90 /* True if this entry has been deleted in the transacted storage, but the
91 * delete has not yet been committed. */
94 /* If this entry's stream has been modified, a reference to where the stream
95 * is stored in the snapshot file. */
98 /* This directory entry's data, including any changes that have been made. */
101 /* A reference to the parent of this node. This is only valid while we are
102 * committing changes. */
105 /* A reference to a newly-created entry in the transacted parent. This is
106 * always equal to transactedParentEntry except when committing changes. */
107 DirRef newTransactedParentEntry
;
108 } TransactedDirEntry
;
111 /****************************************************************************
112 * Transacted storage object.
114 typedef struct TransactedSnapshotImpl
116 struct StorageBaseImpl base
;
119 * Modified streams are temporarily saved to the scratch file.
121 StorageBaseImpl
*scratch
;
123 /* The directory structure is kept here, so that we can track how these
124 * entries relate to those in the parent storage. */
125 TransactedDirEntry
*entries
;
127 ULONG firstFreeEntry
;
130 * Changes are committed to the transacted parent.
132 StorageBaseImpl
*transactedParent
;
134 /* The transaction signature from when we last committed */
135 ULONG lastTransactionSig
;
136 } TransactedSnapshotImpl
;
138 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
139 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*,BOOL
,StorageBaseImpl
**);
141 typedef struct TransactedSharedImpl
143 struct StorageBaseImpl base
;
146 * Snapshot and uncommitted changes go here.
148 TransactedSnapshotImpl
*scratch
;
151 * Changes are committed to the transacted parent.
153 StorageBaseImpl
*transactedParent
;
155 /* The transaction signature from when we last committed */
156 ULONG lastTransactionSig
;
157 } TransactedSharedImpl
;
160 /****************************************************************************
161 * BlockChainStream definitions.
163 * The BlockChainStream class is a utility class that is used to create an
164 * abstraction of the big block chains in the storage file.
169 /* This represents a range of blocks that happen reside in consecutive sectors. */
175 typedef struct BlockChainBlock
181 BYTE data
[MAX_BIG_BLOCK_SIZE
];
184 struct BlockChainStream
186 StorageImpl
* parentStorage
;
187 ULONG
* headOfStreamPlaceHolder
;
188 DirRef ownerDirEntry
;
189 struct BlockChainRun
* indexCache
;
191 ULONG indexCacheSize
;
192 BlockChainBlock cachedBlocks
[2];
198 /* Returns the number of blocks that comprises this chain.
199 * This is not the size of the stream as the last block may not be full!
201 static inline ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
203 return This
->numBlocks
;
206 static BlockChainStream
* BlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
207 static void BlockChainStream_Destroy(BlockChainStream
*);
208 static HRESULT
BlockChainStream_ReadAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
209 static HRESULT
BlockChainStream_WriteAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
210 static HRESULT
BlockChainStream_Flush(BlockChainStream
*);
211 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
*);
212 static BOOL
BlockChainStream_SetSize(BlockChainStream
*,ULARGE_INTEGER
);
215 /****************************************************************************
216 * SmallBlockChainStream definitions.
218 * The SmallBlockChainStream class is a utility class that is used to create an
219 * abstraction of the small block chains in the storage file.
222 struct SmallBlockChainStream
224 StorageImpl
* parentStorage
;
225 DirRef ownerDirEntry
;
226 ULONG
* headOfStreamPlaceHolder
;
229 static SmallBlockChainStream
* SmallBlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
230 static void SmallBlockChainStream_Destroy(SmallBlockChainStream
*);
231 static HRESULT
SmallBlockChainStream_ReadAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
232 static HRESULT
SmallBlockChainStream_WriteAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
233 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
*);
234 static BOOL
SmallBlockChainStream_SetSize(SmallBlockChainStream
*,ULARGE_INTEGER
);
237 /************************************************************************
239 ***********************************************************************/
241 /************************************************************************
242 * This method validates an STGM parameter that can contain the values below
244 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
245 * The stgm values contained in 0xffff0000 are bitmasks.
247 * STGM_DIRECT 0x00000000
248 * STGM_TRANSACTED 0x00010000
249 * STGM_SIMPLE 0x08000000
251 * STGM_READ 0x00000000
252 * STGM_WRITE 0x00000001
253 * STGM_READWRITE 0x00000002
255 * STGM_SHARE_DENY_NONE 0x00000040
256 * STGM_SHARE_DENY_READ 0x00000030
257 * STGM_SHARE_DENY_WRITE 0x00000020
258 * STGM_SHARE_EXCLUSIVE 0x00000010
260 * STGM_PRIORITY 0x00040000
261 * STGM_DELETEONRELEASE 0x04000000
263 * STGM_CREATE 0x00001000
264 * STGM_CONVERT 0x00020000
265 * STGM_FAILIFTHERE 0x00000000
267 * STGM_NOSCRATCH 0x00100000
268 * STGM_NOSNAPSHOT 0x00200000
270 static HRESULT
validateSTGM(DWORD stgm
)
272 DWORD access
= STGM_ACCESS_MODE(stgm
);
273 DWORD share
= STGM_SHARE_MODE(stgm
);
274 DWORD create
= STGM_CREATE_MODE(stgm
);
276 if (stgm
&~STGM_KNOWN_FLAGS
)
278 ERR("unknown flags %08x\n", stgm
);
294 case STGM_SHARE_DENY_NONE
:
295 case STGM_SHARE_DENY_READ
:
296 case STGM_SHARE_DENY_WRITE
:
297 case STGM_SHARE_EXCLUSIVE
:
300 if (!(stgm
& STGM_TRANSACTED
))
310 case STGM_FAILIFTHERE
:
317 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
319 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
323 * STGM_CREATE | STGM_CONVERT
324 * if both are false, STGM_FAILIFTHERE is set to TRUE
326 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
330 * STGM_NOSCRATCH requires STGM_TRANSACTED
332 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
336 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
337 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
339 if ( (stgm
& STGM_NOSNAPSHOT
) &&
340 (!(stgm
& STGM_TRANSACTED
) ||
341 share
== STGM_SHARE_EXCLUSIVE
||
342 share
== STGM_SHARE_DENY_WRITE
) )
348 /************************************************************************
349 * GetShareModeFromSTGM
351 * This method will return a share mode flag from a STGM value.
352 * The STGM value is assumed valid.
354 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
356 switch (STGM_SHARE_MODE(stgm
))
359 assert(stgm
& STGM_TRANSACTED
);
361 case STGM_SHARE_DENY_NONE
:
362 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
363 case STGM_SHARE_DENY_READ
:
364 return FILE_SHARE_WRITE
;
365 case STGM_SHARE_DENY_WRITE
:
366 case STGM_SHARE_EXCLUSIVE
:
367 return FILE_SHARE_READ
;
369 ERR("Invalid share mode!\n");
374 /************************************************************************
375 * GetAccessModeFromSTGM
377 * This method will return an access mode flag from a STGM value.
378 * The STGM value is assumed valid.
380 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
382 switch (STGM_ACCESS_MODE(stgm
))
388 return GENERIC_READ
| GENERIC_WRITE
;
390 ERR("Invalid access mode!\n");
395 /************************************************************************
396 * GetCreationModeFromSTGM
398 * This method will return a creation mode flag from a STGM value.
399 * The STGM value is assumed valid.
401 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
403 switch(STGM_CREATE_MODE(stgm
))
406 return CREATE_ALWAYS
;
408 FIXME("STGM_CONVERT not implemented!\n");
410 case STGM_FAILIFTHERE
:
413 ERR("Invalid create mode!\n");
419 /************************************************************************
420 * IDirectWriterLock implementation
421 ***********************************************************************/
423 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
425 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
428 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
430 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
431 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
434 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
436 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
437 return IStorage_AddRef(&This
->IStorage_iface
);
440 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
442 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
443 return IStorage_Release(&This
->IStorage_iface
);
446 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
448 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
449 FIXME("(%p)->(%d): stub\n", This
, timeout
);
453 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
455 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
456 FIXME("(%p): stub\n", This
);
460 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
462 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
463 FIXME("(%p): stub\n", This
);
467 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
469 directwriterlock_QueryInterface
,
470 directwriterlock_AddRef
,
471 directwriterlock_Release
,
472 directwriterlock_WaitForWriteAccess
,
473 directwriterlock_ReleaseWriteAccess
,
474 directwriterlock_HaveWriteAccess
478 /************************************************************************
479 * StorageBaseImpl implementation : Tree helper functions
480 ***********************************************************************/
482 /****************************************************************************
486 * Case insensitive comparison of DirEntry.name by first considering
489 * Returns <0 when name1 < name2
490 * >0 when name1 > name2
491 * 0 when name1 == name2
493 static LONG
entryNameCmp(
494 const OLECHAR
*name1
,
495 const OLECHAR
*name2
)
497 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
499 while (diff
== 0 && *name1
!= 0)
502 * We compare the string themselves only when they are of the same length
504 diff
= toupperW(*name1
++) - toupperW(*name2
++);
510 /****************************************************************************
514 * Find and read the element of a storage with the given name.
516 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
517 const OLECHAR
*name
, DirEntry
*data
)
521 /* Read the storage entry to find the root of the tree. */
522 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
524 currentEntry
= data
->dirRootEntry
;
526 while (currentEntry
!= DIRENTRY_NULL
)
530 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
532 cmp
= entryNameCmp(name
, data
->name
);
539 currentEntry
= data
->leftChild
;
542 currentEntry
= data
->rightChild
;
548 /****************************************************************************
552 * Find and read the binary tree parent of the element with the given name.
554 * If there is no such element, find a place where it could be inserted and
555 * return STG_E_FILENOTFOUND.
557 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
558 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
564 /* Read the storage entry to find the root of the tree. */
565 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
567 *parentEntry
= storageEntry
;
568 *relation
= DIRENTRY_RELATION_DIR
;
570 childEntry
= parentData
->dirRootEntry
;
572 while (childEntry
!= DIRENTRY_NULL
)
576 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
578 cmp
= entryNameCmp(childName
, childData
.name
);
586 *parentData
= childData
;
587 *parentEntry
= childEntry
;
588 *relation
= DIRENTRY_RELATION_PREVIOUS
;
590 childEntry
= parentData
->leftChild
;
595 *parentData
= childData
;
596 *parentEntry
= childEntry
;
597 *relation
= DIRENTRY_RELATION_NEXT
;
599 childEntry
= parentData
->rightChild
;
603 if (childEntry
== DIRENTRY_NULL
)
604 return STG_E_FILENOTFOUND
;
609 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
613 case DIRENTRY_RELATION_PREVIOUS
:
614 entry
->leftChild
= new_target
;
616 case DIRENTRY_RELATION_NEXT
:
617 entry
->rightChild
= new_target
;
619 case DIRENTRY_RELATION_DIR
:
620 entry
->dirRootEntry
= new_target
;
627 /****************************************************************************
631 * Add a directory entry to a storage
633 static HRESULT
insertIntoTree(
634 StorageBaseImpl
*This
,
635 DirRef parentStorageIndex
,
636 DirRef newEntryIndex
)
638 DirEntry currentEntry
;
642 * Read the inserted entry
644 StorageBaseImpl_ReadDirEntry(This
,
649 * Read the storage entry
651 StorageBaseImpl_ReadDirEntry(This
,
655 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
658 * The root storage contains some element, therefore, start the research
659 * for the appropriate location.
662 DirRef current
, next
, previous
, currentEntryId
;
665 * Keep a reference to the root of the storage's element tree
667 currentEntryId
= currentEntry
.dirRootEntry
;
672 StorageBaseImpl_ReadDirEntry(This
,
673 currentEntry
.dirRootEntry
,
676 previous
= currentEntry
.leftChild
;
677 next
= currentEntry
.rightChild
;
678 current
= currentEntryId
;
682 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
686 if (previous
!= DIRENTRY_NULL
)
688 StorageBaseImpl_ReadDirEntry(This
,
695 currentEntry
.leftChild
= newEntryIndex
;
696 StorageBaseImpl_WriteDirEntry(This
,
704 if (next
!= DIRENTRY_NULL
)
706 StorageBaseImpl_ReadDirEntry(This
,
713 currentEntry
.rightChild
= newEntryIndex
;
714 StorageBaseImpl_WriteDirEntry(This
,
723 * Trying to insert an item with the same name in the
726 return STG_E_FILEALREADYEXISTS
;
729 previous
= currentEntry
.leftChild
;
730 next
= currentEntry
.rightChild
;
736 * The storage is empty, make the new entry the root of its element tree
738 currentEntry
.dirRootEntry
= newEntryIndex
;
739 StorageBaseImpl_WriteDirEntry(This
,
747 /*************************************************************************
751 * This method removes a directory entry from its parent storage tree without
752 * freeing any resources attached to it.
754 static HRESULT
removeFromTree(
755 StorageBaseImpl
*This
,
756 DirRef parentStorageIndex
,
759 DirEntry entryToDelete
;
760 DirEntry parentEntry
;
761 DirRef parentEntryRef
;
762 ULONG typeOfRelation
;
765 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
771 * Find the element that links to the one we want to delete.
773 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
774 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
779 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
782 * Replace the deleted entry with its left child
784 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
786 hr
= StorageBaseImpl_WriteDirEntry(
795 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
798 * We need to reinsert the right child somewhere. We already know it and
799 * its children are greater than everything in the left tree, so we
800 * insert it at the rightmost point in the left tree.
802 DirRef newRightChildParent
= entryToDelete
.leftChild
;
803 DirEntry newRightChildParentEntry
;
807 hr
= StorageBaseImpl_ReadDirEntry(
810 &newRightChildParentEntry
);
816 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
817 newRightChildParent
= newRightChildParentEntry
.rightChild
;
818 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
820 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
822 hr
= StorageBaseImpl_WriteDirEntry(
825 &newRightChildParentEntry
);
835 * Replace the deleted entry with its right child
837 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
839 hr
= StorageBaseImpl_WriteDirEntry(
853 /************************************************************************
854 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
855 ***********************************************************************/
858 * IEnumSTATSTGImpl definitions.
860 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
861 * This class allows iterating through the content of a storage and finding
862 * specific items inside it.
864 struct IEnumSTATSTGImpl
866 IEnumSTATSTG IEnumSTATSTG_iface
;
868 LONG ref
; /* Reference count */
869 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
870 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
872 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
875 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
877 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
880 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
882 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
883 HeapFree(GetProcessHeap(), 0, This
);
886 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
891 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
893 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
900 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
901 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
903 *ppvObject
= &This
->IEnumSTATSTG_iface
;
904 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
905 TRACE("<-- %p\n", *ppvObject
);
909 TRACE("<-- E_NOINTERFACE\n");
910 return E_NOINTERFACE
;
913 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
916 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
917 return InterlockedIncrement(&This
->ref
);
920 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
923 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
927 newRef
= InterlockedDecrement(&This
->ref
);
931 IEnumSTATSTGImpl_Destroy(This
);
937 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
938 IEnumSTATSTGImpl
* This
,
941 DirRef result
= DIRENTRY_NULL
;
945 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
947 TRACE("%p,%p\n", This
, ref
);
949 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
950 This
->parentStorage
->storageDirEntry
, &entry
);
951 searchNode
= entry
.dirRootEntry
;
953 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
955 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
959 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
963 searchNode
= entry
.rightChild
;
968 memcpy(result_name
, entry
.name
, sizeof(result_name
));
969 searchNode
= entry
.leftChild
;
977 if (result
!= DIRENTRY_NULL
)
978 memcpy(This
->name
, result_name
, sizeof(result_name
));
981 TRACE("<-- %08x\n", hr
);
985 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
991 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
993 DirEntry currentEntry
;
994 STATSTG
* currentReturnStruct
= rgelt
;
995 ULONG objectFetched
= 0;
996 DirRef currentSearchNode
;
999 TRACE("%p,%u,%p,%p\n", iface
, celt
, rgelt
, pceltFetched
);
1001 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
1002 return E_INVALIDARG
;
1004 if (This
->parentStorage
->reverted
)
1006 TRACE("<-- STG_E_REVERTED\n");
1007 return STG_E_REVERTED
;
1011 * To avoid the special case, get another pointer to a ULONG value if
1012 * the caller didn't supply one.
1014 if (pceltFetched
==0)
1015 pceltFetched
= &objectFetched
;
1018 * Start the iteration, we will iterate until we hit the end of the
1019 * linked list or until we hit the number of items to iterate through
1023 while ( *pceltFetched
< celt
)
1025 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1027 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1029 memset(currentReturnStruct
, 0, sizeof(*currentReturnStruct
));
1034 * Read the entry from the storage.
1036 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
1039 if (FAILED(hr
)) break;
1042 * Copy the information to the return buffer.
1044 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
1045 currentReturnStruct
,
1050 * Step to the next item in the iteration
1053 currentReturnStruct
++;
1056 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
1059 TRACE("<-- %08x (asked %u, got %u)\n", hr
, celt
, *pceltFetched
);
1064 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
1065 IEnumSTATSTG
* iface
,
1068 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1070 ULONG objectFetched
= 0;
1071 DirRef currentSearchNode
;
1074 TRACE("%p,%u\n", iface
, celt
);
1076 if (This
->parentStorage
->reverted
)
1078 TRACE("<-- STG_E_REVERTED\n");
1079 return STG_E_REVERTED
;
1082 while ( (objectFetched
< celt
) )
1084 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1086 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1092 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
1095 TRACE("<-- %08x\n", hr
);
1099 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
1100 IEnumSTATSTG
* iface
)
1102 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1104 TRACE("%p\n", iface
);
1106 if (This
->parentStorage
->reverted
)
1108 TRACE("<-- STG_E_REVERTED\n");
1109 return STG_E_REVERTED
;
1117 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
*,DirRef
);
1119 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
1120 IEnumSTATSTG
* iface
,
1121 IEnumSTATSTG
** ppenum
)
1123 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1124 IEnumSTATSTGImpl
* newClone
;
1126 TRACE("%p,%p\n", iface
, ppenum
);
1128 if (This
->parentStorage
->reverted
)
1130 TRACE("<-- STG_E_REVERTED\n");
1131 return STG_E_REVERTED
;
1135 return E_INVALIDARG
;
1137 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
1138 This
->storageDirEntry
);
1142 return E_OUTOFMEMORY
;
1146 * The new clone enumeration must point to the same current node as
1149 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
1151 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
1157 * Virtual function table for the IEnumSTATSTGImpl class.
1159 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
1161 IEnumSTATSTGImpl_QueryInterface
,
1162 IEnumSTATSTGImpl_AddRef
,
1163 IEnumSTATSTGImpl_Release
,
1164 IEnumSTATSTGImpl_Next
,
1165 IEnumSTATSTGImpl_Skip
,
1166 IEnumSTATSTGImpl_Reset
,
1167 IEnumSTATSTGImpl_Clone
1170 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
1171 StorageBaseImpl
* parentStorage
,
1172 DirRef storageDirEntry
)
1174 IEnumSTATSTGImpl
* newEnumeration
;
1176 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
1180 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
1181 newEnumeration
->ref
= 1;
1182 newEnumeration
->name
[0] = 0;
1185 * We want to nail-down the reference to the storage in case the
1186 * enumeration out-lives the storage in the client application.
1188 newEnumeration
->parentStorage
= parentStorage
;
1189 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
1191 newEnumeration
->storageDirEntry
= storageDirEntry
;
1194 return newEnumeration
;
1198 /************************************************************************
1199 * StorageBaseImpl implementation
1200 ***********************************************************************/
1202 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
1204 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
1207 /************************************************************************
1208 * StorageBaseImpl_QueryInterface (IUnknown)
1210 * This method implements the common QueryInterface for all IStorage
1211 * implementations contained in this file.
1213 * See Windows documentation for more details on IUnknown methods.
1215 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
1220 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1222 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
1225 return E_INVALIDARG
;
1229 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
1230 IsEqualGUID(&IID_IStorage
, riid
))
1232 *ppvObject
= &This
->IStorage_iface
;
1234 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
1236 *ppvObject
= &This
->IPropertySetStorage_iface
;
1238 /* locking interface is reported for writer only */
1239 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
1241 *ppvObject
= &This
->IDirectWriterLock_iface
;
1245 TRACE("<-- E_NOINTERFACE\n");
1246 return E_NOINTERFACE
;
1249 IStorage_AddRef(iface
);
1250 TRACE("<-- %p\n", *ppvObject
);
1254 /************************************************************************
1255 * StorageBaseImpl_AddRef (IUnknown)
1257 * This method implements the common AddRef for all IStorage
1258 * implementations contained in this file.
1260 * See Windows documentation for more details on IUnknown methods.
1262 static ULONG WINAPI
StorageBaseImpl_AddRef(
1265 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1266 ULONG ref
= InterlockedIncrement(&This
->ref
);
1268 TRACE("(%p) AddRef to %d\n", This
, ref
);
1273 /************************************************************************
1274 * StorageBaseImpl_Release (IUnknown)
1276 * This method implements the common Release for all IStorage
1277 * implementations contained in this file.
1279 * See Windows documentation for more details on IUnknown methods.
1281 static ULONG WINAPI
StorageBaseImpl_Release(
1284 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1286 ULONG ref
= InterlockedDecrement(&This
->ref
);
1288 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
1293 * Since we are using a system of base-classes, we want to call the
1294 * destructor of the appropriate derived class. To do this, we are
1295 * using virtual functions to implement the destructor.
1297 StorageBaseImpl_Destroy(This
);
1303 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1304 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1305 SNB snbExclude
, IStorage
*pstgDest
);
1307 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1308 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1309 SNB snbExclude
, IStorage
*pstgDest
)
1315 IStream
*pstrChild
, *pstrTmp
;
1318 if (srcEntry
== DIRENTRY_NULL
)
1321 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1328 WCHAR
**snb
= snbExclude
;
1330 while ( *snb
!= NULL
&& !skip
)
1332 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1340 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1343 * create a new storage in destination storage
1345 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1346 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1351 * if it already exist, don't create a new one use this one
1353 if (hr
== STG_E_FILEALREADYEXISTS
)
1355 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1356 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1357 NULL
, 0, &pstgTmp
);
1362 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1363 skip_stream
, NULL
, pstgTmp
);
1365 IStorage_Release(pstgTmp
);
1368 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1371 * create a new stream in destination storage. If the stream already
1372 * exist, it will be deleted and a new one will be created.
1374 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1375 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1379 * open child stream storage. This operation must succeed even if the
1380 * stream is already open, so we use internal functions to do it.
1384 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1388 pstrChild
= &streamimpl
->IStream_iface
;
1390 IStream_AddRef(pstrChild
);
1402 * Get the size of the source stream
1404 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1407 * Set the size of the destination stream.
1409 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1414 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1417 IStream_Release( pstrChild
);
1420 IStream_Release( pstrTmp
);
1426 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1427 skip_stream
, snbExclude
, pstgDest
);
1430 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1431 skip_stream
, snbExclude
, pstgDest
);
1433 TRACE("<-- %08x\n", hr
);
1437 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1439 StgStreamImpl
*strm
;
1441 TRACE("%p,%d\n", stg
, streamEntry
);
1443 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1445 if (strm
->dirEntry
== streamEntry
)
1454 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1456 StorageInternalImpl
*childstg
;
1458 TRACE("%p,%d\n", stg
, storageEntry
);
1460 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1462 if (childstg
->base
.storageDirEntry
== storageEntry
)
1471 /************************************************************************
1472 * StorageBaseImpl_OpenStream (IStorage)
1474 * This method will open the specified stream object from the current storage.
1476 * See Windows documentation for more details on IStorage methods.
1478 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
1480 const OLECHAR
* pwcsName
, /* [string][in] */
1481 void* reserved1
, /* [unique][in] */
1482 DWORD grfMode
, /* [in] */
1483 DWORD reserved2
, /* [in] */
1484 IStream
** ppstm
) /* [out] */
1486 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1487 StgStreamImpl
* newStream
;
1488 DirEntry currentEntry
;
1489 DirRef streamEntryRef
;
1490 HRESULT res
= STG_E_UNKNOWN
;
1492 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1493 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1495 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1503 if ( FAILED( validateSTGM(grfMode
) ) ||
1504 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1506 res
= STG_E_INVALIDFLAG
;
1513 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1515 res
= STG_E_INVALIDFUNCTION
;
1521 res
= STG_E_REVERTED
;
1526 * Check that we're compatible with the parent's storage mode, but
1527 * only if we are not in transacted mode
1529 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1530 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1532 res
= STG_E_INVALIDFLAG
;
1538 * Search for the element with the given name
1540 streamEntryRef
= findElement(
1542 This
->storageDirEntry
,
1547 * If it was found, construct the stream object and return a pointer to it.
1549 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1550 (currentEntry
.stgType
==STGTY_STREAM
) )
1552 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1554 /* A single stream cannot be opened a second time. */
1555 res
= STG_E_ACCESSDENIED
;
1559 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1563 newStream
->grfMode
= grfMode
;
1564 *ppstm
= &newStream
->IStream_iface
;
1566 IStream_AddRef(*ppstm
);
1572 res
= E_OUTOFMEMORY
;
1576 res
= STG_E_FILENOTFOUND
;
1580 TRACE("<-- IStream %p\n", *ppstm
);
1581 TRACE("<-- %08x\n", res
);
1585 /************************************************************************
1586 * StorageBaseImpl_OpenStorage (IStorage)
1588 * This method will open a new storage object from the current storage.
1590 * See Windows documentation for more details on IStorage methods.
1592 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1594 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1595 IStorage
* pstgPriority
, /* [unique][in] */
1596 DWORD grfMode
, /* [in] */
1597 SNB snbExclude
, /* [unique][in] */
1598 DWORD reserved
, /* [in] */
1599 IStorage
** ppstg
) /* [out] */
1601 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1602 StorageInternalImpl
* newStorage
;
1603 StorageBaseImpl
* newTransactedStorage
;
1604 DirEntry currentEntry
;
1605 DirRef storageEntryRef
;
1606 HRESULT res
= STG_E_UNKNOWN
;
1608 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1609 iface
, debugstr_w(pwcsName
), pstgPriority
,
1610 grfMode
, snbExclude
, reserved
, ppstg
);
1612 if ((pwcsName
==NULL
) || (ppstg
==0) )
1618 if (This
->openFlags
& STGM_SIMPLE
)
1620 res
= STG_E_INVALIDFUNCTION
;
1625 if (snbExclude
!= NULL
)
1627 res
= STG_E_INVALIDPARAMETER
;
1631 if ( FAILED( validateSTGM(grfMode
) ))
1633 res
= STG_E_INVALIDFLAG
;
1640 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1641 (grfMode
& STGM_DELETEONRELEASE
) ||
1642 (grfMode
& STGM_PRIORITY
) )
1644 res
= STG_E_INVALIDFUNCTION
;
1649 return STG_E_REVERTED
;
1652 * Check that we're compatible with the parent's storage mode,
1653 * but only if we are not transacted
1655 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1656 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1658 res
= STG_E_ACCESSDENIED
;
1665 storageEntryRef
= findElement(
1667 This
->storageDirEntry
,
1671 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1672 (currentEntry
.stgType
==STGTY_STORAGE
) )
1674 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1676 /* A single storage cannot be opened a second time. */
1677 res
= STG_E_ACCESSDENIED
;
1681 newStorage
= StorageInternalImpl_Construct(
1686 if (newStorage
!= 0)
1688 if (grfMode
& STGM_TRANSACTED
)
1690 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1694 HeapFree(GetProcessHeap(), 0, newStorage
);
1698 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1702 *ppstg
= &newStorage
->base
.IStorage_iface
;
1705 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1711 res
= STG_E_INSUFFICIENTMEMORY
;
1715 res
= STG_E_FILENOTFOUND
;
1718 TRACE("<-- %08x\n", res
);
1722 /************************************************************************
1723 * StorageBaseImpl_EnumElements (IStorage)
1725 * This method will create an enumerator object that can be used to
1726 * retrieve information about all the elements in the storage object.
1728 * See Windows documentation for more details on IStorage methods.
1730 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1732 DWORD reserved1
, /* [in] */
1733 void* reserved2
, /* [size_is][unique][in] */
1734 DWORD reserved3
, /* [in] */
1735 IEnumSTATSTG
** ppenum
) /* [out] */
1737 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1738 IEnumSTATSTGImpl
* newEnum
;
1740 TRACE("(%p, %d, %p, %d, %p)\n",
1741 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1744 return E_INVALIDARG
;
1747 return STG_E_REVERTED
;
1749 newEnum
= IEnumSTATSTGImpl_Construct(
1751 This
->storageDirEntry
);
1755 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1759 return E_OUTOFMEMORY
;
1762 /************************************************************************
1763 * StorageBaseImpl_Stat (IStorage)
1765 * This method will retrieve information about this storage object.
1767 * See Windows documentation for more details on IStorage methods.
1769 static HRESULT WINAPI
StorageBaseImpl_Stat(
1771 STATSTG
* pstatstg
, /* [out] */
1772 DWORD grfStatFlag
) /* [in] */
1774 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1775 DirEntry currentEntry
;
1776 HRESULT res
= STG_E_UNKNOWN
;
1778 TRACE("(%p, %p, %x)\n",
1779 iface
, pstatstg
, grfStatFlag
);
1789 res
= STG_E_REVERTED
;
1793 res
= StorageBaseImpl_ReadDirEntry(
1795 This
->storageDirEntry
,
1800 StorageUtl_CopyDirEntryToSTATSTG(
1806 pstatstg
->grfMode
= This
->openFlags
;
1807 pstatstg
->grfStateBits
= This
->stateBits
;
1813 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
);
1815 TRACE("<-- %08x\n", res
);
1819 /************************************************************************
1820 * StorageBaseImpl_RenameElement (IStorage)
1822 * This method will rename the specified element.
1824 * See Windows documentation for more details on IStorage methods.
1826 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1828 const OLECHAR
* pwcsOldName
, /* [in] */
1829 const OLECHAR
* pwcsNewName
) /* [in] */
1831 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1832 DirEntry currentEntry
;
1833 DirRef currentEntryRef
;
1835 TRACE("(%p, %s, %s)\n",
1836 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1839 return STG_E_REVERTED
;
1841 currentEntryRef
= findElement(This
,
1842 This
->storageDirEntry
,
1846 if (currentEntryRef
!= DIRENTRY_NULL
)
1849 * There is already an element with the new name
1851 return STG_E_FILEALREADYEXISTS
;
1855 * Search for the old element name
1857 currentEntryRef
= findElement(This
,
1858 This
->storageDirEntry
,
1862 if (currentEntryRef
!= DIRENTRY_NULL
)
1864 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1865 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1867 WARN("Element is already open; cannot rename.\n");
1868 return STG_E_ACCESSDENIED
;
1871 /* Remove the element from its current position in the tree */
1872 removeFromTree(This
, This
->storageDirEntry
,
1875 /* Change the name of the element */
1876 strcpyW(currentEntry
.name
, pwcsNewName
);
1878 /* Delete any sibling links */
1879 currentEntry
.leftChild
= DIRENTRY_NULL
;
1880 currentEntry
.rightChild
= DIRENTRY_NULL
;
1882 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1885 /* Insert the element in a new position in the tree */
1886 insertIntoTree(This
, This
->storageDirEntry
,
1892 * There is no element with the old name
1894 return STG_E_FILENOTFOUND
;
1897 return StorageBaseImpl_Flush(This
);
1900 /************************************************************************
1901 * StorageBaseImpl_CreateStream (IStorage)
1903 * This method will create a stream object within this storage
1905 * See Windows documentation for more details on IStorage methods.
1907 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1909 const OLECHAR
* pwcsName
, /* [string][in] */
1910 DWORD grfMode
, /* [in] */
1911 DWORD reserved1
, /* [in] */
1912 DWORD reserved2
, /* [in] */
1913 IStream
** ppstm
) /* [out] */
1915 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1916 StgStreamImpl
* newStream
;
1917 DirEntry currentEntry
, newStreamEntry
;
1918 DirRef currentEntryRef
, newStreamEntryRef
;
1921 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1922 iface
, debugstr_w(pwcsName
), grfMode
,
1923 reserved1
, reserved2
, ppstm
);
1926 return STG_E_INVALIDPOINTER
;
1929 return STG_E_INVALIDNAME
;
1931 if (reserved1
|| reserved2
)
1932 return STG_E_INVALIDPARAMETER
;
1934 if ( FAILED( validateSTGM(grfMode
) ))
1935 return STG_E_INVALIDFLAG
;
1937 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1938 return STG_E_INVALIDFLAG
;
1941 return STG_E_REVERTED
;
1946 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1947 (grfMode
& STGM_TRANSACTED
))
1948 return STG_E_INVALIDFUNCTION
;
1951 * Don't worry about permissions in transacted mode, as we can always write
1952 * changes; we just can't always commit them.
1954 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1955 /* Can't create a stream on read-only storage */
1956 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1957 return STG_E_ACCESSDENIED
;
1959 /* Can't create a stream with greater access than the parent. */
1960 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1961 return STG_E_ACCESSDENIED
;
1964 if(This
->openFlags
& STGM_SIMPLE
)
1965 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1969 currentEntryRef
= findElement(This
,
1970 This
->storageDirEntry
,
1974 if (currentEntryRef
!= DIRENTRY_NULL
)
1977 * An element with this name already exists
1979 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1981 IStorage_DestroyElement(iface
, pwcsName
);
1984 return STG_E_FILEALREADYEXISTS
;
1988 * memset the empty entry
1990 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
1992 newStreamEntry
.sizeOfNameString
=
1993 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1995 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1996 return STG_E_INVALIDNAME
;
1998 strcpyW(newStreamEntry
.name
, pwcsName
);
2000 newStreamEntry
.stgType
= STGTY_STREAM
;
2001 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2002 newStreamEntry
.size
.u
.LowPart
= 0;
2003 newStreamEntry
.size
.u
.HighPart
= 0;
2005 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
2006 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
2007 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
2009 /* call CoFileTime to get the current time
2010 newStreamEntry.ctime
2011 newStreamEntry.mtime
2014 /* newStreamEntry.clsid */
2017 * Create an entry with the new data
2019 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
2024 * Insert the new entry in the parent storage's tree.
2026 hr
= insertIntoTree(
2028 This
->storageDirEntry
,
2032 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
2037 * Open the stream to return it.
2039 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2043 *ppstm
= &newStream
->IStream_iface
;
2044 IStream_AddRef(*ppstm
);
2048 return STG_E_INSUFFICIENTMEMORY
;
2051 return StorageBaseImpl_Flush(This
);
2054 /************************************************************************
2055 * StorageBaseImpl_SetClass (IStorage)
2057 * This method will write the specified CLSID in the directory entry of this
2060 * See Windows documentation for more details on IStorage methods.
2062 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2064 REFCLSID clsid
) /* [in] */
2066 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2068 DirEntry currentEntry
;
2070 TRACE("(%p, %p)\n", iface
, clsid
);
2073 return STG_E_REVERTED
;
2075 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2076 This
->storageDirEntry
,
2078 if (SUCCEEDED(hRes
))
2080 currentEntry
.clsid
= *clsid
;
2082 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2083 This
->storageDirEntry
,
2087 if (SUCCEEDED(hRes
))
2088 hRes
= StorageBaseImpl_Flush(This
);
2093 /************************************************************************
2094 * StorageBaseImpl_CreateStorage (IStorage)
2096 * This method will create the storage object within the provided storage.
2098 * See Windows documentation for more details on IStorage methods.
2100 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2102 const OLECHAR
*pwcsName
, /* [string][in] */
2103 DWORD grfMode
, /* [in] */
2104 DWORD reserved1
, /* [in] */
2105 DWORD reserved2
, /* [in] */
2106 IStorage
**ppstg
) /* [out] */
2108 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2110 DirEntry currentEntry
;
2112 DirRef currentEntryRef
;
2116 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2117 iface
, debugstr_w(pwcsName
), grfMode
,
2118 reserved1
, reserved2
, ppstg
);
2121 return STG_E_INVALIDPOINTER
;
2123 if (This
->openFlags
& STGM_SIMPLE
)
2125 return STG_E_INVALIDFUNCTION
;
2129 return STG_E_INVALIDNAME
;
2133 if ( FAILED( validateSTGM(grfMode
) ) ||
2134 (grfMode
& STGM_DELETEONRELEASE
) )
2136 WARN("bad grfMode: 0x%x\n", grfMode
);
2137 return STG_E_INVALIDFLAG
;
2141 return STG_E_REVERTED
;
2144 * Check that we're compatible with the parent's storage mode
2146 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2147 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2149 WARN("access denied\n");
2150 return STG_E_ACCESSDENIED
;
2153 currentEntryRef
= findElement(This
,
2154 This
->storageDirEntry
,
2158 if (currentEntryRef
!= DIRENTRY_NULL
)
2161 * An element with this name already exists
2163 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2164 ((This
->openFlags
& STGM_TRANSACTED
) ||
2165 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2167 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2173 WARN("file already exists\n");
2174 return STG_E_FILEALREADYEXISTS
;
2177 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2178 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2180 WARN("read-only storage\n");
2181 return STG_E_ACCESSDENIED
;
2184 memset(&newEntry
, 0, sizeof(DirEntry
));
2186 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2188 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2190 FIXME("name too long\n");
2191 return STG_E_INVALIDNAME
;
2194 strcpyW(newEntry
.name
, pwcsName
);
2196 newEntry
.stgType
= STGTY_STORAGE
;
2197 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2198 newEntry
.size
.u
.LowPart
= 0;
2199 newEntry
.size
.u
.HighPart
= 0;
2201 newEntry
.leftChild
= DIRENTRY_NULL
;
2202 newEntry
.rightChild
= DIRENTRY_NULL
;
2203 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2205 /* call CoFileTime to get the current time
2210 /* newEntry.clsid */
2213 * Create a new directory entry for the storage
2215 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2220 * Insert the new directory entry into the parent storage's tree
2222 hr
= insertIntoTree(
2224 This
->storageDirEntry
,
2228 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2233 * Open it to get a pointer to return.
2235 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2237 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2243 hr
= StorageBaseImpl_Flush(This
);
2248 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2249 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2250 SNB snbExclude
, IStorage
*pstgDest
)
2255 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2258 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2261 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2262 skip_stream
, snbExclude
, pstgDest
);
2264 TRACE("<-- %08x\n", hr
);
2268 /*************************************************************************
2271 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2273 DWORD ciidExclude
, /* [in] */
2274 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2275 SNB snbExclude
, /* [unique][in] */
2276 IStorage
* pstgDest
) /* [unique][in] */
2278 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2280 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2283 TRACE("(%p, %d, %p, %p, %p)\n",
2284 iface
, ciidExclude
, rgiidExclude
,
2285 snbExclude
, pstgDest
);
2287 if ( pstgDest
== 0 )
2288 return STG_E_INVALIDPOINTER
;
2290 for(i
= 0; i
< ciidExclude
; ++i
)
2292 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2293 skip_storage
= TRUE
;
2294 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2297 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2302 /* Give up early if it looks like this would be infinitely recursive.
2303 * Oddly enough, this includes some cases that aren't really recursive, like
2304 * copying to a transacted child. */
2305 IStorage
*pstgDestAncestor
= pstgDest
;
2306 IStorage
*pstgDestAncestorChild
= NULL
;
2308 /* Go up the chain from the destination until we find the source storage. */
2309 while (pstgDestAncestor
!= iface
) {
2310 pstgDestAncestorChild
= pstgDest
;
2312 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2314 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2316 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2318 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2320 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2322 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2328 if (pstgDestAncestor
== iface
)
2332 if (pstgDestAncestorChild
&& snbExclude
)
2334 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2336 WCHAR
**snb
= snbExclude
;
2338 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2340 while ( *snb
!= NULL
&& fail
)
2342 if ( lstrcmpW(data
.name
, *snb
) == 0 )
2349 return STG_E_ACCESSDENIED
;
2353 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2354 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2357 /*************************************************************************
2358 * MoveElementTo (IStorage)
2360 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2362 const OLECHAR
*pwcsName
, /* [string][in] */
2363 IStorage
*pstgDest
, /* [unique][in] */
2364 const OLECHAR
*pwcsNewName
,/* [string][in] */
2365 DWORD grfFlags
) /* [in] */
2367 FIXME("(%p %s %p %s %u): stub\n", iface
,
2368 debugstr_w(pwcsName
), pstgDest
,
2369 debugstr_w(pwcsNewName
), grfFlags
);
2373 /*************************************************************************
2376 * Ensures that any changes made to a storage object open in transacted mode
2377 * are reflected in the parent storage
2379 * In a non-transacted mode, this ensures all cached writes are completed.
2381 static HRESULT WINAPI
StorageBaseImpl_Commit(
2383 DWORD grfCommitFlags
)/* [in] */
2385 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2386 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
2387 return StorageBaseImpl_Flush(This
);
2390 /*************************************************************************
2393 * Discard all changes that have been made since the last commit operation
2395 static HRESULT WINAPI
StorageBaseImpl_Revert(
2398 TRACE("(%p)\n", iface
);
2402 /*********************************************************************
2404 * Internal helper function for StorageBaseImpl_DestroyElement()
2406 * Delete the contents of a storage entry.
2409 static HRESULT
deleteStorageContents(
2410 StorageBaseImpl
*parentStorage
,
2411 DirRef indexToDelete
,
2412 DirEntry entryDataToDelete
)
2414 IEnumSTATSTG
*elements
= 0;
2415 IStorage
*childStorage
= 0;
2416 STATSTG currentElement
;
2418 HRESULT destroyHr
= S_OK
;
2419 StorageInternalImpl
*stg
, *stg2
;
2421 TRACE("%p,%d\n", parentStorage
, indexToDelete
);
2423 /* Invalidate any open storage objects. */
2424 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2426 if (stg
->base
.storageDirEntry
== indexToDelete
)
2428 StorageBaseImpl_Invalidate(&stg
->base
);
2433 * Open the storage and enumerate it
2435 hr
= IStorage_OpenStorage(
2436 &parentStorage
->IStorage_iface
,
2437 entryDataToDelete
.name
,
2439 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2446 TRACE("<-- %08x\n", hr
);
2451 * Enumerate the elements
2453 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2456 IStorage_Release(childStorage
);
2457 TRACE("<-- %08x\n", hr
);
2464 * Obtain the next element
2466 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2469 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2471 CoTaskMemFree(currentElement
.pwcsName
);
2475 * We need to Reset the enumeration every time because we delete elements
2476 * and the enumeration could be invalid
2478 IEnumSTATSTG_Reset(elements
);
2480 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2482 IStorage_Release(childStorage
);
2483 IEnumSTATSTG_Release(elements
);
2485 TRACE("%08x\n", hr
);
2489 /*********************************************************************
2491 * Internal helper function for StorageBaseImpl_DestroyElement()
2493 * Perform the deletion of a stream's data
2496 static HRESULT
deleteStreamContents(
2497 StorageBaseImpl
*parentStorage
,
2498 DirRef indexToDelete
,
2499 DirEntry entryDataToDelete
)
2503 ULARGE_INTEGER size
;
2504 StgStreamImpl
*strm
, *strm2
;
2506 /* Invalidate any open stream objects. */
2507 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2509 if (strm
->dirEntry
== indexToDelete
)
2511 TRACE("Stream deleted %p\n", strm
);
2512 strm
->parentStorage
= NULL
;
2513 list_remove(&strm
->StrmListEntry
);
2517 size
.u
.HighPart
= 0;
2520 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2521 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2525 TRACE("<-- %08x\n", hr
);
2532 hr
= IStream_SetSize(pis
, size
);
2536 TRACE("<-- %08x\n", hr
);
2541 * Release the stream object.
2543 IStream_Release(pis
);
2544 TRACE("<-- %08x\n", hr
);
2548 /*************************************************************************
2549 * DestroyElement (IStorage)
2551 * Strategy: This implementation is built this way for simplicity not for speed.
2552 * I always delete the topmost element of the enumeration and adjust
2553 * the deleted element pointer all the time. This takes longer to
2554 * do but allows reinvoking DestroyElement whenever we encounter a
2555 * storage object. The optimisation resides in the usage of another
2556 * enumeration strategy that would give all the leaves of a storage
2557 * first. (postfix order)
2559 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2561 const OLECHAR
*pwcsName
)/* [string][in] */
2563 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2566 DirEntry entryToDelete
;
2567 DirRef entryToDeleteRef
;
2570 iface
, debugstr_w(pwcsName
));
2573 return STG_E_INVALIDPOINTER
;
2576 return STG_E_REVERTED
;
2578 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2579 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2580 return STG_E_ACCESSDENIED
;
2582 entryToDeleteRef
= findElement(
2584 This
->storageDirEntry
,
2588 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2590 TRACE("<-- STG_E_FILENOTFOUND\n");
2591 return STG_E_FILENOTFOUND
;
2594 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2596 hr
= deleteStorageContents(
2601 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2603 hr
= deleteStreamContents(
2611 TRACE("<-- %08x\n", hr
);
2616 * Remove the entry from its parent storage
2618 hr
= removeFromTree(
2620 This
->storageDirEntry
,
2624 * Invalidate the entry
2627 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2630 hr
= StorageBaseImpl_Flush(This
);
2632 TRACE("<-- %08x\n", hr
);
2636 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2638 struct list
*cur
, *cur2
;
2639 StgStreamImpl
*strm
=NULL
;
2640 StorageInternalImpl
*childstg
=NULL
;
2642 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2643 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2644 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2645 strm
->parentStorage
= NULL
;
2649 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2650 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2651 StorageBaseImpl_Invalidate( &childstg
->base
);
2654 if (stg
->transactedChild
)
2656 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2658 stg
->transactedChild
= NULL
;
2662 /******************************************************************************
2663 * SetElementTimes (IStorage)
2665 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2667 const OLECHAR
*pwcsName
,/* [string][in] */
2668 const FILETIME
*pctime
, /* [in] */
2669 const FILETIME
*patime
, /* [in] */
2670 const FILETIME
*pmtime
) /* [in] */
2672 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2676 /******************************************************************************
2677 * SetStateBits (IStorage)
2679 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2681 DWORD grfStateBits
,/* [in] */
2682 DWORD grfMask
) /* [in] */
2684 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2687 return STG_E_REVERTED
;
2689 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2693 /******************************************************************************
2694 * Internal stream list handlers
2697 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2699 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2700 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2703 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2705 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2706 list_remove(&(strm
->StrmListEntry
));
2709 static HRESULT
StorageBaseImpl_CopyStream(
2710 StorageBaseImpl
*dst
, DirRef dst_entry
,
2711 StorageBaseImpl
*src
, DirRef src_entry
)
2716 ULARGE_INTEGER bytes_copied
;
2717 ULONG bytestocopy
, bytesread
, byteswritten
;
2719 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2723 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2725 bytes_copied
.QuadPart
= 0;
2726 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2728 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2730 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2732 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2735 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2736 data
, &byteswritten
);
2739 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2740 bytes_copied
.QuadPart
+= byteswritten
;
2748 static HRESULT
StorageBaseImpl_DupStorageTree(
2749 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2750 StorageBaseImpl
*src
, DirRef src_entry
)
2754 BOOL has_stream
=FALSE
;
2756 if (src_entry
== DIRENTRY_NULL
)
2758 *dst_entry
= DIRENTRY_NULL
;
2762 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2765 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2766 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2767 data
.size
.QuadPart
= 0;
2769 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2773 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2776 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2779 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2781 if (SUCCEEDED(hr
) && has_stream
)
2782 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2787 static HRESULT
StorageBaseImpl_CopyStorageTree(
2788 StorageBaseImpl
*dst
, DirRef dst_entry
,
2789 StorageBaseImpl
*src
, DirRef src_entry
)
2792 DirEntry src_data
, dst_data
;
2793 DirRef new_root_entry
;
2795 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2799 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2804 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2805 dst_data
.clsid
= src_data
.clsid
;
2806 dst_data
.ctime
= src_data
.ctime
;
2807 dst_data
.mtime
= src_data
.mtime
;
2808 dst_data
.dirRootEntry
= new_root_entry
;
2812 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2817 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2821 ULARGE_INTEGER zero
;
2823 if (entry
== DIRENTRY_NULL
)
2828 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2830 if (SUCCEEDED(hr
) && include_siblings
)
2831 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2833 if (SUCCEEDED(hr
) && include_siblings
)
2834 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2837 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2839 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2840 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2843 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2849 /************************************************************************
2850 * StorageImpl implementation
2851 ***********************************************************************/
2853 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2854 ULARGE_INTEGER offset
,
2859 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2862 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2863 ULARGE_INTEGER offset
,
2866 ULONG
* bytesWritten
)
2868 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2871 /******************************************************************************
2872 * StorageImpl_LoadFileHeader
2874 * This method will read in the file header
2876 static HRESULT
StorageImpl_LoadFileHeader(
2880 BYTE headerBigBlock
[HEADER_SIZE
];
2882 ULARGE_INTEGER offset
;
2887 * Get a pointer to the big block of data containing the header.
2889 offset
.u
.HighPart
= 0;
2890 offset
.u
.LowPart
= 0;
2891 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2892 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2893 hr
= STG_E_FILENOTFOUND
;
2896 * Extract the information from the header.
2901 * Check for the "magic number" signature and return an error if it is not
2904 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2906 return STG_E_OLDFORMAT
;
2909 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2911 return STG_E_INVALIDHEADER
;
2914 StorageUtl_ReadWord(
2916 OFFSET_BIGBLOCKSIZEBITS
,
2917 &This
->bigBlockSizeBits
);
2919 StorageUtl_ReadWord(
2921 OFFSET_SMALLBLOCKSIZEBITS
,
2922 &This
->smallBlockSizeBits
);
2924 StorageUtl_ReadDWord(
2926 OFFSET_BBDEPOTCOUNT
,
2927 &This
->bigBlockDepotCount
);
2929 StorageUtl_ReadDWord(
2931 OFFSET_ROOTSTARTBLOCK
,
2932 &This
->rootStartBlock
);
2934 StorageUtl_ReadDWord(
2936 OFFSET_TRANSACTIONSIG
,
2937 &This
->transactionSig
);
2939 StorageUtl_ReadDWord(
2941 OFFSET_SMALLBLOCKLIMIT
,
2942 &This
->smallBlockLimit
);
2944 StorageUtl_ReadDWord(
2946 OFFSET_SBDEPOTSTART
,
2947 &This
->smallBlockDepotStart
);
2949 StorageUtl_ReadDWord(
2951 OFFSET_EXTBBDEPOTSTART
,
2952 &This
->extBigBlockDepotStart
);
2954 StorageUtl_ReadDWord(
2956 OFFSET_EXTBBDEPOTCOUNT
,
2957 &This
->extBigBlockDepotCount
);
2959 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2961 StorageUtl_ReadDWord(
2963 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2964 &(This
->bigBlockDepotStart
[index
]));
2968 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2970 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2971 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2974 * Right now, the code is making some assumptions about the size of the
2975 * blocks, just make sure they are what we're expecting.
2977 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2978 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2979 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2981 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2982 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
2983 hr
= STG_E_INVALIDHEADER
;
2992 /******************************************************************************
2993 * StorageImpl_SaveFileHeader
2995 * This method will save to the file the header
2997 static void StorageImpl_SaveFileHeader(
3000 BYTE headerBigBlock
[HEADER_SIZE
];
3003 ULARGE_INTEGER offset
;
3004 DWORD bytes_read
, bytes_written
;
3005 DWORD major_version
, dirsectorcount
;
3008 * Get a pointer to the big block of data containing the header.
3010 offset
.u
.HighPart
= 0;
3011 offset
.u
.LowPart
= 0;
3012 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3013 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3014 hr
= STG_E_FILENOTFOUND
;
3016 if (This
->bigBlockSizeBits
== 0x9)
3018 else if (This
->bigBlockSizeBits
== 0xc)
3022 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3027 * If the block read failed, the file is probably new.
3032 * Initialize for all unknown fields.
3034 memset(headerBigBlock
, 0, HEADER_SIZE
);
3037 * Initialize the magic number.
3039 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3043 * Write the information to the header.
3045 StorageUtl_WriteWord(
3047 OFFSET_MINORVERSION
,
3050 StorageUtl_WriteWord(
3052 OFFSET_MAJORVERSION
,
3055 StorageUtl_WriteWord(
3057 OFFSET_BYTEORDERMARKER
,
3060 StorageUtl_WriteWord(
3062 OFFSET_BIGBLOCKSIZEBITS
,
3063 This
->bigBlockSizeBits
);
3065 StorageUtl_WriteWord(
3067 OFFSET_SMALLBLOCKSIZEBITS
,
3068 This
->smallBlockSizeBits
);
3070 if (major_version
>= 4)
3072 if (This
->rootBlockChain
)
3073 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3075 /* This file is being created, and it will start out with one block. */
3079 /* This field must be 0 in versions older than 4 */
3082 StorageUtl_WriteDWord(
3084 OFFSET_DIRSECTORCOUNT
,
3087 StorageUtl_WriteDWord(
3089 OFFSET_BBDEPOTCOUNT
,
3090 This
->bigBlockDepotCount
);
3092 StorageUtl_WriteDWord(
3094 OFFSET_ROOTSTARTBLOCK
,
3095 This
->rootStartBlock
);
3097 StorageUtl_WriteDWord(
3099 OFFSET_TRANSACTIONSIG
,
3100 This
->transactionSig
);
3102 StorageUtl_WriteDWord(
3104 OFFSET_SMALLBLOCKLIMIT
,
3105 This
->smallBlockLimit
);
3107 StorageUtl_WriteDWord(
3109 OFFSET_SBDEPOTSTART
,
3110 This
->smallBlockDepotStart
);
3112 StorageUtl_WriteDWord(
3114 OFFSET_SBDEPOTCOUNT
,
3115 This
->smallBlockDepotChain
?
3116 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3118 StorageUtl_WriteDWord(
3120 OFFSET_EXTBBDEPOTSTART
,
3121 This
->extBigBlockDepotStart
);
3123 StorageUtl_WriteDWord(
3125 OFFSET_EXTBBDEPOTCOUNT
,
3126 This
->extBigBlockDepotCount
);
3128 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3130 StorageUtl_WriteDWord(
3132 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3133 (This
->bigBlockDepotStart
[index
]));
3137 * Write the big block back to the file.
3139 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3143 /************************************************************************
3144 * StorageImpl implementation : DirEntry methods
3145 ***********************************************************************/
3147 /******************************************************************************
3148 * StorageImpl_ReadRawDirEntry
3150 * This method will read the raw data from a directory entry in the file.
3152 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3154 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3156 ULARGE_INTEGER offset
;
3160 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3162 hr
= BlockChainStream_ReadAt(
3163 This
->rootBlockChain
,
3169 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3170 return STG_E_READFAULT
;
3175 /******************************************************************************
3176 * StorageImpl_WriteRawDirEntry
3178 * This method will write the raw data from a directory entry in the file.
3180 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3182 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3184 ULARGE_INTEGER offset
;
3187 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3189 return BlockChainStream_WriteAt(
3190 This
->rootBlockChain
,
3197 /***************************************************************************
3201 * Mark a directory entry in the file as free.
3203 static HRESULT
StorageImpl_DestroyDirEntry(
3204 StorageBaseImpl
*base
,
3207 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3208 StorageImpl
*storage
= (StorageImpl
*)base
;
3210 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3212 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3215 /******************************************************************************
3218 * Update raw directory entry data from the fields in newData.
3220 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3222 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3224 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3227 buffer
+ OFFSET_PS_NAME
,
3229 DIRENTRY_NAME_BUFFER_LEN
);
3231 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3233 StorageUtl_WriteWord(
3235 OFFSET_PS_NAMELENGTH
,
3236 newData
->sizeOfNameString
);
3238 StorageUtl_WriteDWord(
3240 OFFSET_PS_LEFTCHILD
,
3241 newData
->leftChild
);
3243 StorageUtl_WriteDWord(
3245 OFFSET_PS_RIGHTCHILD
,
3246 newData
->rightChild
);
3248 StorageUtl_WriteDWord(
3251 newData
->dirRootEntry
);
3253 StorageUtl_WriteGUID(
3258 StorageUtl_WriteDWord(
3261 newData
->ctime
.dwLowDateTime
);
3263 StorageUtl_WriteDWord(
3265 OFFSET_PS_CTIMEHIGH
,
3266 newData
->ctime
.dwHighDateTime
);
3268 StorageUtl_WriteDWord(
3271 newData
->mtime
.dwLowDateTime
);
3273 StorageUtl_WriteDWord(
3275 OFFSET_PS_MTIMEHIGH
,
3276 newData
->ctime
.dwHighDateTime
);
3278 StorageUtl_WriteDWord(
3280 OFFSET_PS_STARTBLOCK
,
3281 newData
->startingBlock
);
3283 StorageUtl_WriteDWord(
3286 newData
->size
.u
.LowPart
);
3288 StorageUtl_WriteDWord(
3290 OFFSET_PS_SIZE_HIGH
,
3291 newData
->size
.u
.HighPart
);
3294 /***************************************************************************
3298 * Reserve a directory entry in the file and initialize it.
3300 static HRESULT
StorageImpl_CreateDirEntry(
3301 StorageBaseImpl
*base
,
3302 const DirEntry
*newData
,
3305 StorageImpl
*storage
= (StorageImpl
*)base
;
3306 ULONG currentEntryIndex
= 0;
3307 ULONG newEntryIndex
= DIRENTRY_NULL
;
3309 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3310 WORD sizeOfNameString
;
3314 hr
= StorageImpl_ReadRawDirEntry(storage
,
3320 StorageUtl_ReadWord(
3322 OFFSET_PS_NAMELENGTH
,
3325 if (sizeOfNameString
== 0)
3328 * The entry exists and is available, we found it.
3330 newEntryIndex
= currentEntryIndex
;
3336 * We exhausted the directory entries, we will create more space below
3338 newEntryIndex
= currentEntryIndex
;
3340 currentEntryIndex
++;
3342 } while (newEntryIndex
== DIRENTRY_NULL
);
3345 * grow the directory stream
3349 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3350 ULARGE_INTEGER newSize
;
3352 ULONG lastEntry
= 0;
3353 ULONG blockCount
= 0;
3356 * obtain the new count of blocks in the directory stream
3358 blockCount
= BlockChainStream_GetCount(
3359 storage
->rootBlockChain
)+1;
3362 * initialize the size used by the directory stream
3364 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3367 * add a block to the directory stream
3369 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3372 * memset the empty entry in order to initialize the unused newly
3375 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3380 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3383 entryIndex
= newEntryIndex
+ 1;
3384 entryIndex
< lastEntry
;
3387 StorageImpl_WriteRawDirEntry(
3393 StorageImpl_SaveFileHeader(storage
);
3396 UpdateRawDirEntry(currentData
, newData
);
3398 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3401 *index
= newEntryIndex
;
3406 /******************************************************************************
3407 * StorageImpl_ReadDirEntry
3409 * This method will read the specified directory entry.
3411 static HRESULT
StorageImpl_ReadDirEntry(
3416 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3419 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3421 if (SUCCEEDED(readRes
))
3423 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3426 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3427 DIRENTRY_NAME_BUFFER_LEN
);
3428 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3430 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3432 StorageUtl_ReadWord(
3434 OFFSET_PS_NAMELENGTH
,
3435 &buffer
->sizeOfNameString
);
3437 StorageUtl_ReadDWord(
3439 OFFSET_PS_LEFTCHILD
,
3440 &buffer
->leftChild
);
3442 StorageUtl_ReadDWord(
3444 OFFSET_PS_RIGHTCHILD
,
3445 &buffer
->rightChild
);
3447 StorageUtl_ReadDWord(
3450 &buffer
->dirRootEntry
);
3452 StorageUtl_ReadGUID(
3457 StorageUtl_ReadDWord(
3460 &buffer
->ctime
.dwLowDateTime
);
3462 StorageUtl_ReadDWord(
3464 OFFSET_PS_CTIMEHIGH
,
3465 &buffer
->ctime
.dwHighDateTime
);
3467 StorageUtl_ReadDWord(
3470 &buffer
->mtime
.dwLowDateTime
);
3472 StorageUtl_ReadDWord(
3474 OFFSET_PS_MTIMEHIGH
,
3475 &buffer
->mtime
.dwHighDateTime
);
3477 StorageUtl_ReadDWord(
3479 OFFSET_PS_STARTBLOCK
,
3480 &buffer
->startingBlock
);
3482 StorageUtl_ReadDWord(
3485 &buffer
->size
.u
.LowPart
);
3487 if (This
->bigBlockSize
< 4096)
3489 /* Version 3 files may have junk in the high part of size. */
3490 buffer
->size
.u
.HighPart
= 0;
3494 StorageUtl_ReadDWord(
3496 OFFSET_PS_SIZE_HIGH
,
3497 &buffer
->size
.u
.HighPart
);
3504 /*********************************************************************
3505 * Write the specified directory entry to the file
3507 static HRESULT
StorageImpl_WriteDirEntry(
3510 const DirEntry
* buffer
)
3512 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3514 UpdateRawDirEntry(currentEntry
, buffer
);
3516 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3520 /************************************************************************
3521 * StorageImpl implementation : Block methods
3522 ***********************************************************************/
3524 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3526 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3529 static HRESULT
StorageImpl_ReadBigBlock(
3535 ULARGE_INTEGER ulOffset
;
3539 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3541 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3543 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3545 /* File ends during this block; fill the rest with 0's. */
3546 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3549 if (out_read
) *out_read
= read
;
3554 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3560 ULARGE_INTEGER ulOffset
;
3564 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3565 ulOffset
.QuadPart
+= offset
;
3567 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3568 *value
= lendian32toh(tmp
);
3569 return (read
== sizeof(DWORD
));
3572 static BOOL
StorageImpl_WriteBigBlock(
3577 ULARGE_INTEGER ulOffset
;
3580 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3582 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3583 return (wrote
== This
->bigBlockSize
);
3586 static BOOL
StorageImpl_WriteDWordToBigBlock(
3592 ULARGE_INTEGER ulOffset
;
3595 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3596 ulOffset
.QuadPart
+= offset
;
3598 value
= htole32(value
);
3599 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3600 return (wrote
== sizeof(DWORD
));
3603 /******************************************************************************
3604 * Storage32Impl_SmallBlocksToBigBlocks
3606 * This method will convert a small block chain to a big block chain.
3607 * The small block chain will be destroyed.
3609 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3611 SmallBlockChainStream
** ppsbChain
)
3613 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3614 ULARGE_INTEGER size
, offset
;
3615 ULONG cbRead
, cbWritten
;
3616 ULARGE_INTEGER cbTotalRead
;
3617 DirRef streamEntryRef
;
3618 HRESULT resWrite
= S_OK
;
3620 DirEntry streamEntry
;
3622 BlockChainStream
*bbTempChain
= NULL
;
3623 BlockChainStream
*bigBlockChain
= NULL
;
3626 * Create a temporary big block chain that doesn't have
3627 * an associated directory entry. This temporary chain will be
3628 * used to copy data from small blocks to big blocks.
3630 bbTempChain
= BlockChainStream_Construct(This
,
3633 if(!bbTempChain
) return NULL
;
3635 * Grow the big block chain.
3637 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3638 BlockChainStream_SetSize(bbTempChain
, size
);
3641 * Copy the contents of the small block chain to the big block chain
3642 * by small block size increments.
3644 offset
.u
.LowPart
= 0;
3645 offset
.u
.HighPart
= 0;
3646 cbTotalRead
.QuadPart
= 0;
3648 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3651 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3653 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3656 if (FAILED(resRead
))
3661 cbTotalRead
.QuadPart
+= cbRead
;
3663 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3669 if (FAILED(resWrite
))
3672 offset
.u
.LowPart
+= cbRead
;
3676 resRead
= STG_E_READFAULT
;
3679 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3680 HeapFree(GetProcessHeap(),0,buffer
);
3682 size
.u
.HighPart
= 0;
3685 if (FAILED(resRead
) || FAILED(resWrite
))
3687 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3688 BlockChainStream_SetSize(bbTempChain
, size
);
3689 BlockChainStream_Destroy(bbTempChain
);
3694 * Destroy the small block chain.
3696 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3697 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3698 SmallBlockChainStream_Destroy(*ppsbChain
);
3702 * Change the directory entry. This chain is now a big block chain
3703 * and it doesn't reside in the small blocks chain anymore.
3705 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3707 streamEntry
.startingBlock
= bbHeadOfChain
;
3709 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3712 * Destroy the temporary entryless big block chain.
3713 * Create a new big block chain associated with this entry.
3715 BlockChainStream_Destroy(bbTempChain
);
3716 bigBlockChain
= BlockChainStream_Construct(This
,
3720 return bigBlockChain
;
3723 /******************************************************************************
3724 * Storage32Impl_BigBlocksToSmallBlocks
3726 * This method will convert a big block chain to a small block chain.
3727 * The big block chain will be destroyed on success.
3729 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3731 BlockChainStream
** ppbbChain
,
3732 ULARGE_INTEGER newSize
)
3734 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3735 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3736 DirRef streamEntryRef
;
3737 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3738 DirEntry streamEntry
;
3740 SmallBlockChainStream
* sbTempChain
;
3742 TRACE("%p %p\n", This
, ppbbChain
);
3744 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3750 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3751 size
= BlockChainStream_GetSize(*ppbbChain
);
3752 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3754 offset
.u
.HighPart
= 0;
3755 offset
.u
.LowPart
= 0;
3756 cbTotalRead
.QuadPart
= 0;
3757 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3758 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3760 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3761 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3769 cbTotalRead
.QuadPart
+= cbRead
;
3771 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3772 cbRead
, buffer
, &cbWritten
);
3774 if(FAILED(resWrite
))
3777 offset
.u
.LowPart
+= cbRead
;
3781 resRead
= STG_E_READFAULT
;
3785 HeapFree(GetProcessHeap(), 0, buffer
);
3787 size
.u
.HighPart
= 0;
3790 if(FAILED(resRead
) || FAILED(resWrite
))
3792 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3793 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3794 SmallBlockChainStream_Destroy(sbTempChain
);
3798 /* destroy the original big block chain */
3799 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3800 BlockChainStream_SetSize(*ppbbChain
, size
);
3801 BlockChainStream_Destroy(*ppbbChain
);
3804 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3805 streamEntry
.startingBlock
= sbHeadOfChain
;
3806 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3808 SmallBlockChainStream_Destroy(sbTempChain
);
3809 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3812 /******************************************************************************
3813 * Storage32Impl_AddBlockDepot
3815 * This will create a depot block, essentially it is a block initialized
3818 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3820 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3821 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3822 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3823 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3826 * Initialize blocks as free
3828 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3830 /* Reserve the range lock sector */
3831 if (depotIndex
== rangeLockDepot
)
3833 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3836 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3839 /******************************************************************************
3840 * Storage32Impl_GetExtDepotBlock
3842 * Returns the index of the block that corresponds to the specified depot
3843 * index. This method is only for depot indexes equal or greater than
3844 * COUNT_BBDEPOTINHEADER.
3846 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3848 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3849 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3850 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3851 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3852 ULONG blockIndex
= BLOCK_UNUSED
;
3853 ULONG extBlockIndex
;
3854 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3855 int index
, num_blocks
;
3857 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3859 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3860 return BLOCK_UNUSED
;
3862 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3864 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3866 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3868 num_blocks
= This
->bigBlockSize
/ 4;
3870 for (index
= 0; index
< num_blocks
; index
++)
3872 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3873 This
->extBlockDepotCached
[index
] = blockIndex
;
3876 This
->indexExtBlockDepotCached
= extBlockCount
;
3879 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3884 /******************************************************************************
3885 * Storage32Impl_SetExtDepotBlock
3887 * Associates the specified block index to the specified depot index.
3888 * This method is only for depot indexes equal or greater than
3889 * COUNT_BBDEPOTINHEADER.
3891 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3893 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3894 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3895 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3896 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3897 ULONG extBlockIndex
;
3899 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3901 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3903 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3905 if (extBlockIndex
!= BLOCK_UNUSED
)
3907 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3908 extBlockOffset
* sizeof(ULONG
),
3912 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3914 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3918 /******************************************************************************
3919 * Storage32Impl_AddExtBlockDepot
3921 * Creates an extended depot block.
3923 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3925 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3926 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3927 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3928 ULONG index
= BLOCK_UNUSED
;
3929 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3930 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3931 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3933 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3934 blocksPerDepotBlock
;
3936 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3939 * The first extended block.
3941 This
->extBigBlockDepotStart
= index
;
3946 * Find the last existing extended block.
3948 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3951 * Add the new extended block to the chain.
3953 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3958 * Initialize this block.
3960 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3961 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3963 /* Add the block to our cache. */
3964 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3966 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3967 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3969 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3970 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3972 This
->extBigBlockDepotLocations
= new_cache
;
3973 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3975 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3980 /************************************************************************
3981 * StorageImpl_GetNextBlockInChain
3983 * This method will retrieve the block index of the next big block in
3986 * Params: This - Pointer to the Storage object.
3987 * blockIndex - Index of the block to retrieve the chain
3989 * nextBlockIndex - receives the return value.
3991 * Returns: This method returns the index of the next block in the chain.
3992 * It will return the constants:
3993 * BLOCK_SPECIAL - If the block given was not part of a
3995 * BLOCK_END_OF_CHAIN - If the block given was the last in
3997 * BLOCK_UNUSED - If the block given was not past of a chain
3999 * BLOCK_EXTBBDEPOT - This block is part of the extended
4002 * See Windows documentation for more details on IStorage methods.
4004 static HRESULT
StorageImpl_GetNextBlockInChain(
4007 ULONG
* nextBlockIndex
)
4009 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4010 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4011 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4012 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4014 ULONG depotBlockIndexPos
;
4015 int index
, num_blocks
;
4017 *nextBlockIndex
= BLOCK_SPECIAL
;
4019 if(depotBlockCount
>= This
->bigBlockDepotCount
)
4021 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
4022 This
->bigBlockDepotCount
);
4023 return STG_E_READFAULT
;
4027 * Cache the currently accessed depot block.
4029 if (depotBlockCount
!= This
->indexBlockDepotCached
)
4031 This
->indexBlockDepotCached
= depotBlockCount
;
4033 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4035 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4040 * We have to look in the extended depot.
4042 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4045 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4048 return STG_E_READFAULT
;
4050 num_blocks
= This
->bigBlockSize
/ 4;
4052 for (index
= 0; index
< num_blocks
; index
++)
4054 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4055 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4059 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4064 /******************************************************************************
4065 * Storage32Impl_GetNextExtendedBlock
4067 * Given an extended block this method will return the next extended block.
4070 * The last ULONG of an extended block is the block index of the next
4071 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4075 * - The index of the next extended block
4076 * - BLOCK_UNUSED: there is no next extended block.
4077 * - Any other return values denotes failure.
4079 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4081 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4082 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4084 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4087 return nextBlockIndex
;
4090 /******************************************************************************
4091 * StorageImpl_SetNextBlockInChain
4093 * This method will write the index of the specified block's next block
4094 * in the big block depot.
4096 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4099 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4100 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4101 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4104 static void StorageImpl_SetNextBlockInChain(
4109 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4110 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4111 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4112 ULONG depotBlockIndexPos
;
4114 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4115 assert(blockIndex
!= nextBlock
);
4117 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4118 /* This should never happen (storage file format spec forbids it), but
4119 * older versions of Wine may have generated broken files. We don't want to
4120 * assert and potentially lose data, but we do want to know if this ever
4121 * happens in a newly-created file. */
4122 ERR("Using range lock page\n");
4124 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4126 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4131 * We have to look in the extended depot.
4133 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4136 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4139 * Update the cached block depot, if necessary.
4141 if (depotBlockCount
== This
->indexBlockDepotCached
)
4143 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4147 /******************************************************************************
4148 * StorageImpl_GetNextFreeBigBlock
4150 * Returns the index of the next free big block.
4151 * If the big block depot is filled, this method will enlarge it.
4154 static ULONG
StorageImpl_GetNextFreeBigBlock(
4157 ULONG depotBlockIndexPos
;
4158 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4159 ULONG depotBlockOffset
;
4160 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4161 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4163 ULONG freeBlock
= BLOCK_UNUSED
;
4165 ULARGE_INTEGER neededSize
;
4168 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4169 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4172 * Scan the entire big block depot until we find a block marked free
4174 while (nextBlockIndex
!= BLOCK_UNUSED
)
4176 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4178 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4181 * Grow the primary depot.
4183 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4185 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4188 * Add a block depot.
4190 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4191 This
->bigBlockDepotCount
++;
4192 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4195 * Flag it as a block depot.
4197 StorageImpl_SetNextBlockInChain(This
,
4201 /* Save new header information.
4203 StorageImpl_SaveFileHeader(This
);
4208 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4210 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4213 * Grow the extended depot.
4215 ULONG extIndex
= BLOCK_UNUSED
;
4216 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4217 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4219 if (extBlockOffset
== 0)
4221 /* We need an extended block.
4223 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4224 This
->extBigBlockDepotCount
++;
4225 depotBlockIndexPos
= extIndex
+ 1;
4228 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4231 * Add a block depot and mark it in the extended block.
4233 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4234 This
->bigBlockDepotCount
++;
4235 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4237 /* Flag the block depot.
4239 StorageImpl_SetNextBlockInChain(This
,
4243 /* If necessary, flag the extended depot block.
4245 if (extIndex
!= BLOCK_UNUSED
)
4246 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4248 /* Save header information.
4250 StorageImpl_SaveFileHeader(This
);
4254 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4258 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4259 ( nextBlockIndex
!= BLOCK_UNUSED
))
4261 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4263 if (nextBlockIndex
== BLOCK_UNUSED
)
4265 freeBlock
= (depotIndex
* blocksPerDepot
) +
4266 (depotBlockOffset
/sizeof(ULONG
));
4269 depotBlockOffset
+= sizeof(ULONG
);
4274 depotBlockOffset
= 0;
4278 * make sure that the block physically exists before using it
4280 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
4282 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4284 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4285 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4287 This
->prevFreeBlock
= freeBlock
;
4292 /******************************************************************************
4293 * StorageImpl_FreeBigBlock
4295 * This method will flag the specified block as free in the big block depot.
4297 static void StorageImpl_FreeBigBlock(
4301 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4303 if (blockIndex
< This
->prevFreeBlock
)
4304 This
->prevFreeBlock
= blockIndex
;
4308 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4309 DirRef index
, const DirEntry
*data
)
4311 StorageImpl
*This
= (StorageImpl
*)base
;
4312 return StorageImpl_WriteDirEntry(This
, index
, data
);
4315 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4316 DirRef index
, DirEntry
*data
)
4318 StorageImpl
*This
= (StorageImpl
*)base
;
4319 return StorageImpl_ReadDirEntry(This
, index
, data
);
4322 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4326 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4328 if (!This
->blockChainCache
[i
])
4330 return &This
->blockChainCache
[i
];
4334 i
= This
->blockChainToEvict
;
4336 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4337 This
->blockChainCache
[i
] = NULL
;
4339 This
->blockChainToEvict
++;
4340 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4341 This
->blockChainToEvict
= 0;
4343 return &This
->blockChainCache
[i
];
4346 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4349 int i
, free_index
=-1;
4351 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4353 if (!This
->blockChainCache
[i
])
4355 if (free_index
== -1) free_index
= i
;
4357 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4359 return &This
->blockChainCache
[i
];
4363 if (free_index
== -1)
4365 free_index
= This
->blockChainToEvict
;
4367 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4368 This
->blockChainCache
[free_index
] = NULL
;
4370 This
->blockChainToEvict
++;
4371 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4372 This
->blockChainToEvict
= 0;
4375 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4376 return &This
->blockChainCache
[free_index
];
4379 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4383 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4385 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4387 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4388 This
->blockChainCache
[i
] = NULL
;
4394 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4395 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4397 StorageImpl
*This
= (StorageImpl
*)base
;
4402 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4403 if (FAILED(hr
)) return hr
;
4405 if (data
.size
.QuadPart
== 0)
4411 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4413 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4420 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4422 SmallBlockChainStream
*stream
;
4424 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4425 if (!stream
) return E_OUTOFMEMORY
;
4427 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4429 SmallBlockChainStream_Destroy(stream
);
4435 BlockChainStream
*stream
= NULL
;
4437 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4438 if (!stream
) return E_OUTOFMEMORY
;
4440 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4446 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4447 ULARGE_INTEGER newsize
)
4449 StorageImpl
*This
= (StorageImpl
*)base
;
4452 SmallBlockChainStream
*smallblock
=NULL
;
4453 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4455 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4456 if (FAILED(hr
)) return hr
;
4458 /* In simple mode keep the stream size above the small block limit */
4459 if (This
->base
.openFlags
& STGM_SIMPLE
)
4460 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4462 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4465 /* Create a block chain object of the appropriate type */
4466 if (data
.size
.QuadPart
== 0)
4468 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4470 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4471 if (!smallblock
) return E_OUTOFMEMORY
;
4475 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4476 bigblock
= *pbigblock
;
4477 if (!bigblock
) return E_OUTOFMEMORY
;
4480 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4482 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4483 if (!smallblock
) return E_OUTOFMEMORY
;
4487 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4488 bigblock
= *pbigblock
;
4489 if (!bigblock
) return E_OUTOFMEMORY
;
4492 /* Change the block chain type if necessary. */
4493 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4495 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4498 SmallBlockChainStream_Destroy(smallblock
);
4502 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4503 *pbigblock
= bigblock
;
4505 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4507 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4512 /* Set the size of the block chain. */
4515 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4516 SmallBlockChainStream_Destroy(smallblock
);
4520 BlockChainStream_SetSize(bigblock
, newsize
);
4523 /* Set the size in the directory entry. */
4524 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4527 data
.size
= newsize
;
4529 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4534 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4535 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4537 StorageImpl
*This
= (StorageImpl
*)base
;
4540 ULARGE_INTEGER newSize
;
4542 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4543 if (FAILED(hr
)) return hr
;
4545 /* Grow the stream if necessary */
4546 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4548 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4550 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4554 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4555 if (FAILED(hr
)) return hr
;
4558 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4560 SmallBlockChainStream
*stream
;
4562 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4563 if (!stream
) return E_OUTOFMEMORY
;
4565 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4567 SmallBlockChainStream_Destroy(stream
);
4573 BlockChainStream
*stream
;
4575 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4576 if (!stream
) return E_OUTOFMEMORY
;
4578 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4582 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4585 StorageImpl
*This
= (StorageImpl
*)base
;
4586 DirEntry dst_data
, src_data
;
4589 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4592 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4596 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4597 dst_data
.startingBlock
= src_data
.startingBlock
;
4598 dst_data
.size
= src_data
.size
;
4600 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4606 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4609 DirEntry currentEntry
;
4610 DirRef currentEntryRef
;
4611 BlockChainStream
*blockChainStream
;
4615 ULARGE_INTEGER size
;
4616 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4618 /* Discard any existing data. */
4620 ILockBytes_SetSize(This
->lockBytes
, size
);
4623 * Initialize all header variables:
4624 * - The big block depot consists of one block and it is at block 0
4625 * - The directory table starts at block 1
4626 * - There is no small block depot
4628 memset( This
->bigBlockDepotStart
,
4630 sizeof(This
->bigBlockDepotStart
));
4632 This
->bigBlockDepotCount
= 1;
4633 This
->bigBlockDepotStart
[0] = 0;
4634 This
->rootStartBlock
= 1;
4635 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4636 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4637 if (This
->bigBlockSize
== 4096)
4638 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4640 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4641 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4642 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4643 This
->extBigBlockDepotCount
= 0;
4645 StorageImpl_SaveFileHeader(This
);
4648 * Add one block for the big block depot and one block for the directory table
4650 size
.u
.HighPart
= 0;
4651 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4652 ILockBytes_SetSize(This
->lockBytes
, size
);
4655 * Initialize the big block depot
4657 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4658 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4659 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4660 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4665 * Load the header for the file.
4667 hr
= StorageImpl_LoadFileHeader(This
);
4676 * There is no block depot cached yet.
4678 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4679 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4682 * Start searching for free blocks with block 0.
4684 This
->prevFreeBlock
= 0;
4686 This
->firstFreeSmallBlock
= 0;
4688 /* Read the extended big block depot locations. */
4689 if (This
->extBigBlockDepotCount
!= 0)
4691 ULONG current_block
= This
->extBigBlockDepotStart
;
4692 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4695 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4696 if (!This
->extBigBlockDepotLocations
)
4698 return E_OUTOFMEMORY
;
4701 This
->extBigBlockDepotLocationsSize
= cache_size
;
4703 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4705 if (current_block
== BLOCK_END_OF_CHAIN
)
4707 WARN("File has too few extended big block depot blocks.\n");
4708 return STG_E_DOCFILECORRUPT
;
4710 This
->extBigBlockDepotLocations
[i
] = current_block
;
4711 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4716 This
->extBigBlockDepotLocations
= NULL
;
4717 This
->extBigBlockDepotLocationsSize
= 0;
4721 * Create the block chain abstractions.
4723 if(!(blockChainStream
=
4724 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4726 return STG_E_READFAULT
;
4729 BlockChainStream_Destroy(This
->rootBlockChain
);
4730 This
->rootBlockChain
= blockChainStream
;
4732 if(!(blockChainStream
=
4733 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4736 return STG_E_READFAULT
;
4739 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4740 This
->smallBlockDepotChain
= blockChainStream
;
4743 * Write the root storage entry (memory only)
4747 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4750 * Initialize the directory table
4752 memset(&rootEntry
, 0, sizeof(rootEntry
));
4753 strcpyW(rootEntry
.name
, rootentryW
);
4754 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
4755 rootEntry
.stgType
= STGTY_ROOT
;
4756 rootEntry
.leftChild
= DIRENTRY_NULL
;
4757 rootEntry
.rightChild
= DIRENTRY_NULL
;
4758 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4759 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4760 rootEntry
.size
.u
.HighPart
= 0;
4761 rootEntry
.size
.u
.LowPart
= 0;
4763 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4767 * Find the ID of the root storage.
4769 currentEntryRef
= 0;
4773 hr
= StorageImpl_ReadDirEntry(
4780 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4781 (currentEntry
.stgType
== STGTY_ROOT
) )
4783 This
->base
.storageDirEntry
= currentEntryRef
;
4789 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4793 return STG_E_READFAULT
;
4797 * Create the block chain abstraction for the small block root chain.
4799 if(!(blockChainStream
=
4800 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4802 return STG_E_READFAULT
;
4805 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4806 This
->smallBlockRootChain
= blockChainStream
;
4811 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4813 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4814 This
->blockChainCache
[i
] = NULL
;
4821 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4822 ULONG
* result
, BOOL refresh
)
4824 StorageImpl
*This
= (StorageImpl
*)base
;
4826 DWORD oldTransactionSig
= This
->transactionSig
;
4830 ULARGE_INTEGER offset
;
4834 offset
.u
.HighPart
= 0;
4835 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4836 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4840 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4842 if (oldTransactionSig
!= This
->transactionSig
)
4844 /* Someone else wrote to this, so toss all cached information. */
4845 TRACE("signature changed\n");
4847 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4851 This
->transactionSig
= oldTransactionSig
;
4855 *result
= This
->transactionSig
;
4860 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4863 StorageImpl
*This
= (StorageImpl
*)base
;
4865 This
->transactionSig
= value
;
4866 StorageImpl_SaveFileHeader(This
);
4871 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4872 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4874 if ((dwLockType
& This
->locks_supported
) == 0)
4876 if (supported
) *supported
= FALSE
;
4880 if (supported
) *supported
= TRUE
;
4881 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4884 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4885 ULARGE_INTEGER cb
, DWORD dwLockType
)
4887 if ((dwLockType
& This
->locks_supported
) == 0)
4890 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4893 /* Internal function */
4894 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4895 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4899 DWORD start_time
= GetTickCount();
4900 DWORD last_sanity_check
= start_time
;
4901 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4903 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4904 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4908 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4910 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4912 DWORD current_time
= GetTickCount();
4913 if (current_time
- start_time
>= 20000)
4918 if (current_time
- last_sanity_check
>= 500)
4920 /* Any storage implementation with the file open in a
4921 * shared mode should not lock these bytes for writing. However,
4922 * some programs (LibreOffice Writer) will keep ALL bytes locked
4923 * when opening in exclusive mode. We can use a read lock to
4924 * detect this case early, and not hang a full 20 seconds.
4926 * This can collide with another attempt to open the file in
4927 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4928 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4929 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4933 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4934 hr
= STG_E_ACCESSDENIED
;
4937 last_sanity_check
= current_time
;
4940 if (delay
< 150) delay
++;
4942 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4947 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4949 StorageImpl
*This
= (StorageImpl
*)base
;
4951 ULARGE_INTEGER offset
, cb
;
4955 /* Synchronous grab of second priority range, the commit lock, and the
4956 * lock-checking lock. */
4957 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4958 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4962 offset
.QuadPart
= RANGELOCK_COMMIT
;
4966 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4971 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4973 StorageImpl
*This
= (StorageImpl
*)base
;
4975 ULARGE_INTEGER offset
, cb
;
4979 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4980 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4984 offset
.QuadPart
= RANGELOCK_COMMIT
;
4988 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4993 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4995 StorageImpl
*This
= (StorageImpl
*) iface
;
4999 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
5001 *result
= statstg
.pwcsName
;
5006 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
5007 ULONG end
, HRESULT fail_hr
)
5010 ULARGE_INTEGER offset
, cb
;
5012 offset
.QuadPart
= start
;
5013 cb
.QuadPart
= 1 + end
- start
;
5015 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5016 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5024 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
5028 ULARGE_INTEGER offset
, cb
;
5032 for (i
=start
; i
<=end
; i
++)
5034 offset
.QuadPart
= i
;
5035 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5036 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
5042 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
5044 if (This
->locked_bytes
[j
] == 0)
5046 This
->locked_bytes
[j
] = i
;
5055 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5058 ULARGE_INTEGER offset
;
5060 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5063 if (openFlags
& STGM_NOSNAPSHOT
)
5065 /* STGM_NOSNAPSHOT implies deny write */
5066 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5067 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5070 /* Wrap all other locking inside a single lock so we can check ranges safely */
5071 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5073 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5075 /* If the ILockBytes doesn't support locking that's ok. */
5076 if (!supported
) return S_OK
;
5077 else if (FAILED(hr
)) return hr
;
5081 /* First check for any conflicting locks. */
5082 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5083 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5085 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5086 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5088 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5089 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5091 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5092 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5094 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5095 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5097 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5099 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5102 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5105 /* Then grab our locks. */
5106 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5108 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5110 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5113 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5114 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5116 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5117 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5119 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5120 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5122 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5123 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5125 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5126 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5128 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5130 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5135 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5137 StorageImpl
*This
= (StorageImpl
*)storage
;
5140 TRACE("(%p)\n", This
);
5142 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5145 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5148 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5150 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5151 if (This
->blockChainCache
[i
])
5152 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5155 hr
= ILockBytes_Flush(This
->lockBytes
);
5160 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5162 StorageImpl
*This
= (StorageImpl
*) iface
;
5164 StorageBaseImpl_DeleteAll(&This
->base
);
5166 This
->base
.reverted
= TRUE
;
5169 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5171 StorageImpl
*This
= (StorageImpl
*) iface
;
5173 TRACE("(%p)\n", This
);
5175 StorageImpl_Flush(iface
);
5177 StorageImpl_Invalidate(iface
);
5179 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5181 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5182 BlockChainStream_Destroy(This
->rootBlockChain
);
5183 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5185 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5186 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5188 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
5190 ULARGE_INTEGER offset
, cb
;
5192 if (This
->locked_bytes
[i
] != 0)
5194 offset
.QuadPart
= This
->locked_bytes
[i
];
5195 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5199 if (This
->lockBytes
)
5200 ILockBytes_Release(This
->lockBytes
);
5201 HeapFree(GetProcessHeap(), 0, This
);
5205 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5207 StorageImpl_Destroy
,
5208 StorageImpl_Invalidate
,
5210 StorageImpl_GetFilename
,
5211 StorageImpl_CreateDirEntry
,
5212 StorageImpl_BaseWriteDirEntry
,
5213 StorageImpl_BaseReadDirEntry
,
5214 StorageImpl_DestroyDirEntry
,
5215 StorageImpl_StreamReadAt
,
5216 StorageImpl_StreamWriteAt
,
5217 StorageImpl_StreamSetSize
,
5218 StorageImpl_StreamLink
,
5219 StorageImpl_GetTransactionSig
,
5220 StorageImpl_SetTransactionSig
,
5221 StorageImpl_LockTransaction
,
5222 StorageImpl_UnlockTransaction
5227 * Virtual function table for the IStorageBaseImpl class.
5229 static const IStorageVtbl StorageImpl_Vtbl
=
5231 StorageBaseImpl_QueryInterface
,
5232 StorageBaseImpl_AddRef
,
5233 StorageBaseImpl_Release
,
5234 StorageBaseImpl_CreateStream
,
5235 StorageBaseImpl_OpenStream
,
5236 StorageBaseImpl_CreateStorage
,
5237 StorageBaseImpl_OpenStorage
,
5238 StorageBaseImpl_CopyTo
,
5239 StorageBaseImpl_MoveElementTo
,
5240 StorageBaseImpl_Commit
,
5241 StorageBaseImpl_Revert
,
5242 StorageBaseImpl_EnumElements
,
5243 StorageBaseImpl_DestroyElement
,
5244 StorageBaseImpl_RenameElement
,
5245 StorageBaseImpl_SetElementTimes
,
5246 StorageBaseImpl_SetClass
,
5247 StorageBaseImpl_SetStateBits
,
5248 StorageBaseImpl_Stat
5251 static HRESULT
StorageImpl_Construct(
5259 StorageImpl
** result
)
5265 if ( FAILED( validateSTGM(openFlags
) ))
5266 return STG_E_INVALIDFLAG
;
5268 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5270 return E_OUTOFMEMORY
;
5272 memset(This
, 0, sizeof(StorageImpl
));
5274 list_init(&This
->base
.strmHead
);
5276 list_init(&This
->base
.storageHead
);
5278 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5279 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5280 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5281 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5282 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5284 This
->base
.create
= create
;
5286 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5287 This
->base
.lockingrole
= SWMR_Writer
;
5288 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5289 This
->base
.lockingrole
= SWMR_Reader
;
5291 This
->base
.lockingrole
= SWMR_None
;
5293 This
->base
.reverted
= FALSE
;
5296 * Initialize the big block cache.
5298 This
->bigBlockSize
= sector_size
;
5299 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5301 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5304 This
->lockBytes
= pLkbyt
;
5305 ILockBytes_AddRef(pLkbyt
);
5309 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5313 This
->locks_supported
= stat
.grfLocksSupported
;
5315 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5316 This
->locks_supported
&= ~WINE_LOCK_READ
;
5318 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5322 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5326 IStorage_Release(&This
->base
.IStorage_iface
);
5331 StorageImpl_Flush(&This
->base
);
5339 /************************************************************************
5340 * StorageInternalImpl implementation
5341 ***********************************************************************/
5343 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5345 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5347 if (!This
->base
.reverted
)
5349 TRACE("Storage invalidated (stg=%p)\n", This
);
5351 This
->base
.reverted
= TRUE
;
5353 This
->parentStorage
= NULL
;
5355 StorageBaseImpl_DeleteAll(&This
->base
);
5357 list_remove(&This
->ParentListEntry
);
5361 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5363 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5365 StorageInternalImpl_Invalidate(&This
->base
);
5367 HeapFree(GetProcessHeap(), 0, This
);
5370 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5372 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5374 return StorageBaseImpl_Flush(This
->parentStorage
);
5377 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5379 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5381 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5384 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5385 const DirEntry
*newData
, DirRef
*index
)
5387 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5389 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5393 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5394 DirRef index
, const DirEntry
*data
)
5396 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5398 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5402 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5403 DirRef index
, DirEntry
*data
)
5405 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5407 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5411 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5414 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5416 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5420 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5421 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5423 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5425 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5426 index
, offset
, size
, buffer
, bytesRead
);
5429 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5430 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5432 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5434 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5435 index
, offset
, size
, buffer
, bytesWritten
);
5438 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5439 DirRef index
, ULARGE_INTEGER newsize
)
5441 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5443 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5447 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5448 DirRef dst
, DirRef src
)
5450 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5452 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5456 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5457 ULONG
* result
, BOOL refresh
)
5462 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5468 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5473 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5478 /******************************************************************************
5480 ** StorageInternalImpl_Commit
5483 static HRESULT WINAPI
StorageInternalImpl_Commit(
5485 DWORD grfCommitFlags
) /* [in] */
5487 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5488 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5489 return StorageBaseImpl_Flush(This
);
5492 /******************************************************************************
5494 ** StorageInternalImpl_Revert
5497 static HRESULT WINAPI
StorageInternalImpl_Revert(
5500 FIXME("(%p): stub\n", iface
);
5505 * Virtual function table for the StorageInternalImpl class.
5507 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5509 StorageBaseImpl_QueryInterface
,
5510 StorageBaseImpl_AddRef
,
5511 StorageBaseImpl_Release
,
5512 StorageBaseImpl_CreateStream
,
5513 StorageBaseImpl_OpenStream
,
5514 StorageBaseImpl_CreateStorage
,
5515 StorageBaseImpl_OpenStorage
,
5516 StorageBaseImpl_CopyTo
,
5517 StorageBaseImpl_MoveElementTo
,
5518 StorageInternalImpl_Commit
,
5519 StorageInternalImpl_Revert
,
5520 StorageBaseImpl_EnumElements
,
5521 StorageBaseImpl_DestroyElement
,
5522 StorageBaseImpl_RenameElement
,
5523 StorageBaseImpl_SetElementTimes
,
5524 StorageBaseImpl_SetClass
,
5525 StorageBaseImpl_SetStateBits
,
5526 StorageBaseImpl_Stat
5529 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5531 StorageInternalImpl_Destroy
,
5532 StorageInternalImpl_Invalidate
,
5533 StorageInternalImpl_Flush
,
5534 StorageInternalImpl_GetFilename
,
5535 StorageInternalImpl_CreateDirEntry
,
5536 StorageInternalImpl_WriteDirEntry
,
5537 StorageInternalImpl_ReadDirEntry
,
5538 StorageInternalImpl_DestroyDirEntry
,
5539 StorageInternalImpl_StreamReadAt
,
5540 StorageInternalImpl_StreamWriteAt
,
5541 StorageInternalImpl_StreamSetSize
,
5542 StorageInternalImpl_StreamLink
,
5543 StorageInternalImpl_GetTransactionSig
,
5544 StorageInternalImpl_SetTransactionSig
,
5545 StorageInternalImpl_LockTransaction
,
5546 StorageInternalImpl_UnlockTransaction
5549 static StorageInternalImpl
* StorageInternalImpl_Construct(
5550 StorageBaseImpl
* parentStorage
,
5552 DirRef storageDirEntry
)
5554 StorageInternalImpl
* newStorage
;
5556 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5560 list_init(&newStorage
->base
.strmHead
);
5562 list_init(&newStorage
->base
.storageHead
);
5565 * Initialize the virtual function table.
5567 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5568 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5569 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5570 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5572 newStorage
->base
.reverted
= FALSE
;
5574 newStorage
->base
.ref
= 1;
5576 newStorage
->parentStorage
= parentStorage
;
5579 * Keep a reference to the directory entry of this storage
5581 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5583 newStorage
->base
.create
= FALSE
;
5592 /************************************************************************
5593 * TransactedSnapshotImpl implementation
5594 ***********************************************************************/
5596 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5598 DirRef result
=This
->firstFreeEntry
;
5600 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5603 if (result
== This
->entries_size
)
5605 ULONG new_size
= This
->entries_size
* 2;
5606 TransactedDirEntry
*new_entries
;
5608 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5609 if (!new_entries
) return DIRENTRY_NULL
;
5611 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5612 HeapFree(GetProcessHeap(), 0, This
->entries
);
5614 This
->entries
= new_entries
;
5615 This
->entries_size
= new_size
;
5618 This
->entries
[result
].inuse
= TRUE
;
5620 This
->firstFreeEntry
= result
+1;
5625 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5626 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5628 DirRef stubEntryRef
;
5629 TransactedDirEntry
*entry
;
5631 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5633 if (stubEntryRef
!= DIRENTRY_NULL
)
5635 entry
= &This
->entries
[stubEntryRef
];
5637 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5639 entry
->read
= FALSE
;
5642 return stubEntryRef
;
5645 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5646 TransactedSnapshotImpl
*This
, DirRef entry
)
5651 if (!This
->entries
[entry
].read
)
5653 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5654 This
->entries
[entry
].transactedParentEntry
,
5657 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5659 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5661 if (data
.leftChild
== DIRENTRY_NULL
)
5665 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5667 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5669 if (data
.rightChild
== DIRENTRY_NULL
)
5673 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5675 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5677 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5683 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5684 This
->entries
[entry
].read
= TRUE
;
5691 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5692 TransactedSnapshotImpl
*This
, DirRef entry
)
5696 if (!This
->entries
[entry
].stream_dirty
)
5698 DirEntry new_entrydata
;
5700 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5701 new_entrydata
.name
[0] = 'S';
5702 new_entrydata
.sizeOfNameString
= 1;
5703 new_entrydata
.stgType
= STGTY_STREAM
;
5704 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5705 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5706 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5707 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5709 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5710 &This
->entries
[entry
].stream_entry
);
5712 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5714 hr
= StorageBaseImpl_CopyStream(
5715 This
->scratch
, This
->entries
[entry
].stream_entry
,
5716 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5719 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5723 This
->entries
[entry
].stream_dirty
= TRUE
;
5725 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5727 /* Since this entry is modified, and we aren't using its stream data, we
5728 * no longer care about the original entry. */
5730 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5732 if (delete_ref
!= DIRENTRY_NULL
)
5733 This
->entries
[delete_ref
].deleted
= TRUE
;
5735 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5742 /* Find the first entry in a depth-first traversal. */
5743 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5744 TransactedSnapshotImpl
* This
, DirRef parent
)
5746 DirRef cursor
, prev
;
5747 TransactedDirEntry
*entry
;
5750 entry
= &This
->entries
[cursor
];
5753 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5756 cursor
= entry
->data
.leftChild
;
5757 entry
= &This
->entries
[cursor
];
5758 entry
->parent
= prev
;
5760 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5763 cursor
= entry
->data
.rightChild
;
5764 entry
= &This
->entries
[cursor
];
5765 entry
->parent
= prev
;
5767 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5770 cursor
= entry
->data
.dirRootEntry
;
5771 entry
= &This
->entries
[cursor
];
5772 entry
->parent
= prev
;
5781 /* Find the next entry in a depth-first traversal. */
5782 static DirRef
TransactedSnapshotImpl_FindNextChild(
5783 TransactedSnapshotImpl
* This
, DirRef current
)
5786 TransactedDirEntry
*parent_entry
;
5788 parent
= This
->entries
[current
].parent
;
5789 parent_entry
= &This
->entries
[parent
];
5791 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5793 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5795 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5796 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5799 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5801 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5802 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5809 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5810 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5811 TransactedSnapshotImpl
* This
, DirRef entry
)
5813 return entry
!= DIRENTRY_NULL
&&
5814 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5817 /* Destroy the entries created by CopyTree. */
5818 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5819 TransactedSnapshotImpl
* This
, DirRef stop
)
5822 TransactedDirEntry
*entry
;
5823 ULARGE_INTEGER zero
;
5827 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5830 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5832 if (cursor
== DIRENTRY_NULL
)
5835 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5837 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5839 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5841 entry
= &This
->entries
[cursor
];
5843 if (entry
->stream_dirty
)
5844 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5845 entry
->newTransactedParentEntry
, zero
);
5847 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5848 entry
->newTransactedParentEntry
);
5850 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5853 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5857 /* Make a copy of our edited tree that we can use in the parent. */
5858 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5861 TransactedDirEntry
*entry
;
5864 cursor
= This
->base
.storageDirEntry
;
5865 entry
= &This
->entries
[cursor
];
5866 entry
->parent
= DIRENTRY_NULL
;
5867 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5869 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5872 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5874 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5875 entry
= &This
->entries
[cursor
];
5877 while (cursor
!= DIRENTRY_NULL
)
5879 /* Make a copy of this entry in the transacted parent. */
5881 (!entry
->dirty
&& !entry
->stream_dirty
&&
5882 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5883 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5884 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5885 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5890 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5892 newData
.size
.QuadPart
= 0;
5893 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5895 if (newData
.leftChild
!= DIRENTRY_NULL
)
5896 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5898 if (newData
.rightChild
!= DIRENTRY_NULL
)
5899 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5901 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5902 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5904 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5905 &entry
->newTransactedParentEntry
);
5908 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5912 if (entry
->stream_dirty
)
5914 hr
= StorageBaseImpl_CopyStream(
5915 This
->transactedParent
, entry
->newTransactedParentEntry
,
5916 This
->scratch
, entry
->stream_entry
);
5918 else if (entry
->data
.size
.QuadPart
)
5920 hr
= StorageBaseImpl_StreamLink(
5921 This
->transactedParent
, entry
->newTransactedParentEntry
,
5922 entry
->transactedParentEntry
);
5927 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5928 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5933 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5934 entry
= &This
->entries
[cursor
];
5940 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5942 DWORD grfCommitFlags
) /* [in] */
5944 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5945 TransactedDirEntry
*root_entry
;
5946 DirRef i
, dir_root_ref
;
5948 ULARGE_INTEGER zero
;
5950 ULONG transactionSig
;
5954 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5956 /* Cannot commit a read-only transacted storage */
5957 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5958 return STG_E_ACCESSDENIED
;
5960 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5961 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5964 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5967 if (transactionSig
!= This
->lastTransactionSig
)
5969 ERR("file was externally modified\n");
5970 hr
= STG_E_NOTCURRENT
;
5975 This
->lastTransactionSig
= transactionSig
+1;
5976 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5979 else if (hr
== E_NOTIMPL
)
5982 if (FAILED(hr
)) goto end
;
5984 /* To prevent data loss, we create the new structure in the file before we
5985 * delete the old one, so that in case of errors the old data is intact. We
5986 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5987 * needed in the rare situation where we have just enough free disk space to
5988 * overwrite the existing data. */
5990 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5992 if (!root_entry
->read
)
5995 hr
= TransactedSnapshotImpl_CopyTree(This
);
5996 if (FAILED(hr
)) goto end
;
5998 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5999 dir_root_ref
= DIRENTRY_NULL
;
6001 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
6003 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6005 /* Update the storage to use the new data in one step. */
6007 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
6008 root_entry
->transactedParentEntry
, &data
);
6012 data
.dirRootEntry
= dir_root_ref
;
6013 data
.clsid
= root_entry
->data
.clsid
;
6014 data
.ctime
= root_entry
->data
.ctime
;
6015 data
.mtime
= root_entry
->data
.mtime
;
6017 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
6018 root_entry
->transactedParentEntry
, &data
);
6021 /* Try to flush after updating the root storage, but if the flush fails, keep
6022 * going, on the theory that it'll either succeed later or the subsequent
6023 * writes will fail. */
6024 StorageBaseImpl_Flush(This
->transactedParent
);
6028 /* Destroy the old now-orphaned data. */
6029 for (i
=0; i
<This
->entries_size
; i
++)
6031 TransactedDirEntry
*entry
= &This
->entries
[i
];
6036 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
6037 entry
->transactedParentEntry
, zero
);
6038 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6039 entry
->transactedParentEntry
);
6040 memset(entry
, 0, sizeof(TransactedDirEntry
));
6041 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
6043 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
6045 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
6046 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6047 entry
->transactedParentEntry
);
6048 if (entry
->stream_dirty
)
6050 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
6051 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
6052 entry
->stream_dirty
= FALSE
;
6054 entry
->dirty
= FALSE
;
6055 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
6062 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6066 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6068 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6071 TRACE("<-- %08x\n", hr
);
6075 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6078 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6079 ULARGE_INTEGER zero
;
6082 TRACE("(%p)\n", iface
);
6084 /* Destroy the open objects. */
6085 StorageBaseImpl_DeleteAll(&This
->base
);
6087 /* Clear out the scratch file. */
6089 for (i
=0; i
<This
->entries_size
; i
++)
6091 if (This
->entries
[i
].stream_dirty
)
6093 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6096 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6100 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6102 This
->firstFreeEntry
= 0;
6103 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6108 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6110 if (!This
->reverted
)
6112 TRACE("Storage invalidated (stg=%p)\n", This
);
6114 This
->reverted
= TRUE
;
6116 StorageBaseImpl_DeleteAll(This
);
6120 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6122 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6124 IStorage_Revert(&This
->base
.IStorage_iface
);
6125 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6126 IStorage_Release(&This
->scratch
->IStorage_iface
);
6127 HeapFree(GetProcessHeap(), 0, This
->entries
);
6128 HeapFree(GetProcessHeap(), 0, This
);
6131 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6133 /* We only need to flush when committing. */
6137 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6139 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6141 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6144 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6145 const DirEntry
*newData
, DirRef
*index
)
6147 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6149 TransactedDirEntry
*new_entry
;
6151 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6152 if (new_ref
== DIRENTRY_NULL
)
6153 return E_OUTOFMEMORY
;
6155 new_entry
= &This
->entries
[new_ref
];
6157 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6158 new_entry
->read
= TRUE
;
6159 new_entry
->dirty
= TRUE
;
6160 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6164 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6169 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6170 DirRef index
, const DirEntry
*data
)
6172 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6175 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6177 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6180 TRACE("<-- %08x\n", hr
);
6184 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6186 if (index
!= This
->base
.storageDirEntry
)
6188 This
->entries
[index
].dirty
= TRUE
;
6190 if (data
->size
.QuadPart
== 0 &&
6191 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6193 /* Since this entry is modified, and we aren't using its stream data, we
6194 * no longer care about the original entry. */
6196 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6198 if (delete_ref
!= DIRENTRY_NULL
)
6199 This
->entries
[delete_ref
].deleted
= TRUE
;
6201 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6204 TRACE("<-- S_OK\n");
6208 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6209 DirRef index
, DirEntry
*data
)
6211 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6214 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6217 TRACE("<-- %08x\n", hr
);
6221 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6223 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6228 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6231 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6233 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6234 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6236 /* If we deleted this entry while it has stream data. We must have left the
6237 * data because some other entry is using it, and we need to leave the
6238 * original entry alone. */
6239 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6240 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6244 This
->entries
[index
].deleted
= TRUE
;
6250 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6251 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6253 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6255 if (This
->entries
[index
].stream_dirty
)
6257 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6258 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6260 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6262 /* This stream doesn't live in the parent, and we haven't allocated storage
6269 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6270 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6274 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6275 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6277 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6280 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6283 TRACE("<-- %08x\n", hr
);
6287 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6290 TRACE("<-- %08x\n", hr
);
6294 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6295 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6297 if (SUCCEEDED(hr
) && size
!= 0)
6298 This
->entries
[index
].data
.size
.QuadPart
= max(
6299 This
->entries
[index
].data
.size
.QuadPart
,
6300 offset
.QuadPart
+ size
);
6302 TRACE("<-- %08x\n", hr
);
6306 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6307 DirRef index
, ULARGE_INTEGER newsize
)
6309 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6312 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6315 TRACE("<-- %08x\n", hr
);
6319 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6322 if (newsize
.QuadPart
== 0)
6324 /* Destroy any parent references or entries in the scratch file. */
6325 if (This
->entries
[index
].stream_dirty
)
6327 ULARGE_INTEGER zero
;
6329 StorageBaseImpl_StreamSetSize(This
->scratch
,
6330 This
->entries
[index
].stream_entry
, zero
);
6331 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6332 This
->entries
[index
].stream_entry
);
6333 This
->entries
[index
].stream_dirty
= FALSE
;
6335 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6338 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6340 if (delete_ref
!= DIRENTRY_NULL
)
6341 This
->entries
[delete_ref
].deleted
= TRUE
;
6343 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6348 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6349 if (FAILED(hr
)) return hr
;
6351 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6352 This
->entries
[index
].stream_entry
, newsize
);
6356 This
->entries
[index
].data
.size
= newsize
;
6358 TRACE("<-- %08x\n", hr
);
6362 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6363 DirRef dst
, DirRef src
)
6365 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6367 TransactedDirEntry
*dst_entry
, *src_entry
;
6369 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6372 TRACE("<-- %08x\n", hr
);
6376 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6379 TRACE("<-- %08x\n", hr
);
6383 dst_entry
= &This
->entries
[dst
];
6384 src_entry
= &This
->entries
[src
];
6386 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6387 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6388 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6389 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6390 dst_entry
->data
.size
= src_entry
->data
.size
;
6395 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6396 ULONG
* result
, BOOL refresh
)
6401 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6407 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6412 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6417 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6419 StorageBaseImpl_QueryInterface
,
6420 StorageBaseImpl_AddRef
,
6421 StorageBaseImpl_Release
,
6422 StorageBaseImpl_CreateStream
,
6423 StorageBaseImpl_OpenStream
,
6424 StorageBaseImpl_CreateStorage
,
6425 StorageBaseImpl_OpenStorage
,
6426 StorageBaseImpl_CopyTo
,
6427 StorageBaseImpl_MoveElementTo
,
6428 TransactedSnapshotImpl_Commit
,
6429 TransactedSnapshotImpl_Revert
,
6430 StorageBaseImpl_EnumElements
,
6431 StorageBaseImpl_DestroyElement
,
6432 StorageBaseImpl_RenameElement
,
6433 StorageBaseImpl_SetElementTimes
,
6434 StorageBaseImpl_SetClass
,
6435 StorageBaseImpl_SetStateBits
,
6436 StorageBaseImpl_Stat
6439 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6441 TransactedSnapshotImpl_Destroy
,
6442 TransactedSnapshotImpl_Invalidate
,
6443 TransactedSnapshotImpl_Flush
,
6444 TransactedSnapshotImpl_GetFilename
,
6445 TransactedSnapshotImpl_CreateDirEntry
,
6446 TransactedSnapshotImpl_WriteDirEntry
,
6447 TransactedSnapshotImpl_ReadDirEntry
,
6448 TransactedSnapshotImpl_DestroyDirEntry
,
6449 TransactedSnapshotImpl_StreamReadAt
,
6450 TransactedSnapshotImpl_StreamWriteAt
,
6451 TransactedSnapshotImpl_StreamSetSize
,
6452 TransactedSnapshotImpl_StreamLink
,
6453 TransactedSnapshotImpl_GetTransactionSig
,
6454 TransactedSnapshotImpl_SetTransactionSig
,
6455 TransactedSnapshotImpl_LockTransaction
,
6456 TransactedSnapshotImpl_UnlockTransaction
6459 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6460 TransactedSnapshotImpl
** result
)
6464 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6469 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6471 /* This is OK because the property set storage functions use the IStorage functions. */
6472 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6473 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6475 list_init(&(*result
)->base
.strmHead
);
6477 list_init(&(*result
)->base
.storageHead
);
6479 (*result
)->base
.ref
= 1;
6481 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6483 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6484 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6486 /* Create a new temporary storage to act as the scratch file. */
6487 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6489 (*result
)->scratch
= impl_from_IStorage(scratch
);
6493 ULONG num_entries
= 20;
6495 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6496 (*result
)->entries_size
= num_entries
;
6497 (*result
)->firstFreeEntry
= 0;
6499 if ((*result
)->entries
)
6501 /* parentStorage already has 1 reference, which we take over here. */
6502 (*result
)->transactedParent
= parentStorage
;
6504 parentStorage
->transactedChild
= &(*result
)->base
;
6506 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6510 IStorage_Release(scratch
);
6516 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6521 return E_OUTOFMEMORY
;
6525 /************************************************************************
6526 * TransactedSharedImpl implementation
6527 ***********************************************************************/
6529 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6531 if (!This
->reverted
)
6533 TRACE("Storage invalidated (stg=%p)\n", This
);
6535 This
->reverted
= TRUE
;
6537 StorageBaseImpl_DeleteAll(This
);
6541 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6543 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6545 TransactedSharedImpl_Invalidate(&This
->base
);
6546 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6547 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6548 HeapFree(GetProcessHeap(), 0, This
);
6551 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6553 /* We only need to flush when committing. */
6557 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6559 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6561 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6564 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6565 const DirEntry
*newData
, DirRef
*index
)
6567 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6569 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6573 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6574 DirRef index
, const DirEntry
*data
)
6576 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6578 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6582 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6583 DirRef index
, DirEntry
*data
)
6585 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6587 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6591 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6594 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6596 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6600 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6601 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6603 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6605 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6606 index
, offset
, size
, buffer
, bytesRead
);
6609 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6610 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6612 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6614 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6615 index
, offset
, size
, buffer
, bytesWritten
);
6618 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6619 DirRef index
, ULARGE_INTEGER newsize
)
6621 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6623 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6627 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6628 DirRef dst
, DirRef src
)
6630 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6632 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6636 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6637 ULONG
* result
, BOOL refresh
)
6642 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6648 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6653 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6658 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6660 DWORD grfCommitFlags
) /* [in] */
6662 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6663 DirRef new_storage_ref
, prev_storage_ref
;
6664 DirEntry src_data
, dst_data
;
6666 ULONG transactionSig
;
6668 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6670 /* Cannot commit a read-only transacted storage */
6671 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6672 return STG_E_ACCESSDENIED
;
6674 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6675 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6678 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6681 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6682 hr
= STG_E_NOTCURRENT
;
6685 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6687 else if (hr
== E_NOTIMPL
)
6691 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6693 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6695 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6698 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6701 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6705 prev_storage_ref
= dst_data
.dirRootEntry
;
6706 dst_data
.dirRootEntry
= new_storage_ref
;
6707 dst_data
.clsid
= src_data
.clsid
;
6708 dst_data
.ctime
= src_data
.ctime
;
6709 dst_data
.mtime
= src_data
.mtime
;
6710 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6715 /* Try to flush after updating the root storage, but if the flush fails, keep
6716 * going, on the theory that it'll either succeed later or the subsequent
6717 * writes will fail. */
6718 StorageBaseImpl_Flush(This
->transactedParent
);
6720 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6724 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6726 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6729 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6733 This
->lastTransactionSig
= transactionSig
+1;
6736 TRACE("<-- %08x\n", hr
);
6740 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6743 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6745 TRACE("(%p)\n", iface
);
6747 /* Destroy the open objects. */
6748 StorageBaseImpl_DeleteAll(&This
->base
);
6750 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6753 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6755 StorageBaseImpl_QueryInterface
,
6756 StorageBaseImpl_AddRef
,
6757 StorageBaseImpl_Release
,
6758 StorageBaseImpl_CreateStream
,
6759 StorageBaseImpl_OpenStream
,
6760 StorageBaseImpl_CreateStorage
,
6761 StorageBaseImpl_OpenStorage
,
6762 StorageBaseImpl_CopyTo
,
6763 StorageBaseImpl_MoveElementTo
,
6764 TransactedSharedImpl_Commit
,
6765 TransactedSharedImpl_Revert
,
6766 StorageBaseImpl_EnumElements
,
6767 StorageBaseImpl_DestroyElement
,
6768 StorageBaseImpl_RenameElement
,
6769 StorageBaseImpl_SetElementTimes
,
6770 StorageBaseImpl_SetClass
,
6771 StorageBaseImpl_SetStateBits
,
6772 StorageBaseImpl_Stat
6775 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6777 TransactedSharedImpl_Destroy
,
6778 TransactedSharedImpl_Invalidate
,
6779 TransactedSharedImpl_Flush
,
6780 TransactedSharedImpl_GetFilename
,
6781 TransactedSharedImpl_CreateDirEntry
,
6782 TransactedSharedImpl_WriteDirEntry
,
6783 TransactedSharedImpl_ReadDirEntry
,
6784 TransactedSharedImpl_DestroyDirEntry
,
6785 TransactedSharedImpl_StreamReadAt
,
6786 TransactedSharedImpl_StreamWriteAt
,
6787 TransactedSharedImpl_StreamSetSize
,
6788 TransactedSharedImpl_StreamLink
,
6789 TransactedSharedImpl_GetTransactionSig
,
6790 TransactedSharedImpl_SetTransactionSig
,
6791 TransactedSharedImpl_LockTransaction
,
6792 TransactedSharedImpl_UnlockTransaction
6795 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6796 TransactedSharedImpl
** result
)
6800 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6805 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6807 /* This is OK because the property set storage functions use the IStorage functions. */
6808 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6809 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6811 list_init(&(*result
)->base
.strmHead
);
6813 list_init(&(*result
)->base
.storageHead
);
6815 (*result
)->base
.ref
= 1;
6817 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6819 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6825 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6826 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6830 stgo
.ulSectorSize
= 4096;
6831 stgo
.pwcsTemplateFile
= NULL
;
6833 /* Create a new temporary storage to act as the scratch file. */
6834 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6835 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6836 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6840 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6841 parentStorage
, parentStorage
->storageDirEntry
);
6845 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6847 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6848 (*result
)->transactedParent
= parentStorage
;
6852 IStorage_Release(scratch
);
6855 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6858 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6863 return E_OUTOFMEMORY
;
6866 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6867 BOOL toplevel
, StorageBaseImpl
** result
)
6869 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6871 if (parentStorage
->openFlags
& fixme_flags
)
6873 fixme_flags
&= ~parentStorage
->openFlags
;
6874 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6877 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6878 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6879 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6881 /* Need to create a temp file for the snapshot */
6882 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6885 return TransactedSnapshotImpl_Construct(parentStorage
,
6886 (TransactedSnapshotImpl
**)result
);
6889 static HRESULT
Storage_Construct(
6897 StorageBaseImpl
** result
)
6899 StorageImpl
*newStorage
;
6900 StorageBaseImpl
*newTransactedStorage
;
6903 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6904 if (FAILED(hr
)) goto end
;
6906 if (openFlags
& STGM_TRANSACTED
)
6908 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6910 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6912 *result
= newTransactedStorage
;
6915 *result
= &newStorage
->base
;
6922 /************************************************************************
6923 * StorageUtl helper functions
6924 ***********************************************************************/
6926 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6930 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6931 *value
= lendian16toh(tmp
);
6934 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6936 value
= htole16(value
);
6937 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6940 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6944 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6945 *value
= lendian32toh(tmp
);
6948 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6950 value
= htole32(value
);
6951 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6954 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6955 ULARGE_INTEGER
* value
)
6957 #ifdef WORDS_BIGENDIAN
6960 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6961 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6962 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6964 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6968 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6969 const ULARGE_INTEGER
*value
)
6971 #ifdef WORDS_BIGENDIAN
6974 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6975 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6976 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6978 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6982 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6984 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6985 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6986 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6988 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6991 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6993 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6994 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6995 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6997 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
7000 void StorageUtl_CopyDirEntryToSTATSTG(
7001 StorageBaseImpl
* storage
,
7002 STATSTG
* destination
,
7003 const DirEntry
* source
,
7007 * The copy of the string occurs only when the flag is not set
7009 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
7011 /* Use the filename for the root storage. */
7012 destination
->pwcsName
= 0;
7013 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
7015 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
7016 (source
->name
[0] == 0) )
7018 destination
->pwcsName
= 0;
7022 destination
->pwcsName
=
7023 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
7025 strcpyW(destination
->pwcsName
, source
->name
);
7028 switch (source
->stgType
)
7032 destination
->type
= STGTY_STORAGE
;
7035 destination
->type
= STGTY_STREAM
;
7038 destination
->type
= STGTY_STREAM
;
7042 destination
->cbSize
= source
->size
;
7044 currentReturnStruct->mtime = {0}; TODO
7045 currentReturnStruct->ctime = {0};
7046 currentReturnStruct->atime = {0};
7048 destination
->grfMode
= 0;
7049 destination
->grfLocksSupported
= 0;
7050 destination
->clsid
= source
->clsid
;
7051 destination
->grfStateBits
= 0;
7052 destination
->reserved
= 0;
7056 /************************************************************************
7057 * BlockChainStream implementation
7058 ***********************************************************************/
7060 /******************************************************************************
7061 * BlockChainStream_GetHeadOfChain
7063 * Returns the head of this stream chain.
7064 * Some special chains don't have directory entries, their heads are kept in
7065 * This->headOfStreamPlaceHolder.
7068 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7070 DirEntry chainEntry
;
7073 if (This
->headOfStreamPlaceHolder
!= 0)
7074 return *(This
->headOfStreamPlaceHolder
);
7076 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7078 hr
= StorageImpl_ReadDirEntry(
7079 This
->parentStorage
,
7080 This
->ownerDirEntry
,
7083 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7084 return chainEntry
.startingBlock
;
7087 return BLOCK_END_OF_CHAIN
;
7090 /* Read and save the index of all blocks in this stream. */
7091 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
7093 ULONG next_sector
, next_offset
;
7095 struct BlockChainRun
*last_run
;
7097 if (This
->indexCacheLen
== 0)
7101 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7105 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7106 next_offset
= last_run
->lastOffset
+1;
7107 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7108 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7110 if (FAILED(hr
)) return hr
;
7113 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7115 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7117 /* Add the current block to the cache. */
7118 if (This
->indexCacheSize
== 0)
7120 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7121 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7122 This
->indexCacheSize
= 16;
7124 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7126 struct BlockChainRun
*new_cache
;
7129 new_size
= This
->indexCacheSize
* 2;
7130 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7131 if (!new_cache
) return E_OUTOFMEMORY
;
7132 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7134 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7135 This
->indexCache
= new_cache
;
7136 This
->indexCacheSize
= new_size
;
7139 This
->indexCacheLen
++;
7140 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7141 last_run
->firstSector
= next_sector
;
7142 last_run
->firstOffset
= next_offset
;
7145 last_run
->lastOffset
= next_offset
;
7147 /* Find the next block. */
7149 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7150 if (FAILED(hr
)) return hr
;
7153 if (This
->indexCacheLen
)
7155 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7156 This
->numBlocks
= last_run
->lastOffset
+1;
7160 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7161 This
->numBlocks
= 0;
7167 /* Locate the nth block in this stream. */
7168 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7170 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7171 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7173 if (offset
>= This
->numBlocks
)
7174 return BLOCK_END_OF_CHAIN
;
7176 while (min_run
< max_run
)
7178 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7179 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7181 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7182 max_run
= run_to_check
-1;
7184 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7186 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7187 min_run
= run_to_check
+1;
7190 /* Block is in this run. */
7191 min_run
= max_run
= run_to_check
;
7194 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7197 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7198 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7200 BlockChainBlock
*result
=NULL
;
7204 if (This
->cachedBlocks
[i
].index
== index
)
7206 *sector
= This
->cachedBlocks
[i
].sector
;
7207 *block
= &This
->cachedBlocks
[i
];
7211 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7212 if (*sector
== BLOCK_END_OF_CHAIN
)
7213 return STG_E_DOCFILECORRUPT
;
7217 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7218 result
= &This
->cachedBlocks
[0];
7219 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7220 result
= &This
->cachedBlocks
[1];
7223 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7224 if (This
->blockToEvict
== 2)
7225 This
->blockToEvict
= 0;
7230 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7231 return STG_E_WRITEFAULT
;
7232 result
->dirty
= FALSE
;
7235 result
->read
= FALSE
;
7236 result
->index
= index
;
7237 result
->sector
= *sector
;
7244 BlockChainStream
* BlockChainStream_Construct(
7245 StorageImpl
* parentStorage
,
7246 ULONG
* headOfStreamPlaceHolder
,
7249 BlockChainStream
* newStream
;
7251 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7253 newStream
->parentStorage
= parentStorage
;
7254 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7255 newStream
->ownerDirEntry
= dirEntry
;
7256 newStream
->indexCache
= NULL
;
7257 newStream
->indexCacheLen
= 0;
7258 newStream
->indexCacheSize
= 0;
7259 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7260 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7261 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7262 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7263 newStream
->blockToEvict
= 0;
7265 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7267 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7268 HeapFree(GetProcessHeap(), 0, newStream
);
7275 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7278 if (!This
) return S_OK
;
7281 if (This
->cachedBlocks
[i
].dirty
)
7283 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7284 This
->cachedBlocks
[i
].dirty
= FALSE
;
7286 return STG_E_WRITEFAULT
;
7292 void BlockChainStream_Destroy(BlockChainStream
* This
)
7296 BlockChainStream_Flush(This
);
7297 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7299 HeapFree(GetProcessHeap(), 0, This
);
7302 /******************************************************************************
7303 * BlockChainStream_Shrink
7305 * Shrinks this chain in the big block depot.
7307 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7308 ULARGE_INTEGER newSize
)
7315 * Figure out how many blocks are needed to contain the new size
7317 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7319 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7325 * Go to the new end of chain
7327 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7329 /* Mark the new end of chain */
7330 StorageImpl_SetNextBlockInChain(
7331 This
->parentStorage
,
7333 BLOCK_END_OF_CHAIN
);
7335 This
->tailIndex
= blockIndex
;
7339 if (This
->headOfStreamPlaceHolder
!= 0)
7341 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7345 DirEntry chainEntry
;
7346 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7348 StorageImpl_ReadDirEntry(
7349 This
->parentStorage
,
7350 This
->ownerDirEntry
,
7353 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7355 StorageImpl_WriteDirEntry(
7356 This
->parentStorage
,
7357 This
->ownerDirEntry
,
7361 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7364 This
->numBlocks
= numBlocks
;
7367 * Mark the extra blocks as free
7369 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7371 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7372 StorageImpl_FreeBigBlock(This
->parentStorage
,
7373 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7374 if (last_run
->lastOffset
== last_run
->firstOffset
)
7375 This
->indexCacheLen
--;
7377 last_run
->lastOffset
--;
7381 * Reset the last accessed block cache.
7385 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7387 This
->cachedBlocks
[i
].index
= 0xffffffff;
7388 This
->cachedBlocks
[i
].dirty
= FALSE
;
7395 /******************************************************************************
7396 * BlockChainStream_Enlarge
7398 * Grows this chain in the big block depot.
7400 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7401 ULARGE_INTEGER newSize
)
7403 ULONG blockIndex
, currentBlock
;
7405 ULONG oldNumBlocks
= 0;
7407 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7410 * Empty chain. Create the head.
7412 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7414 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7415 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7417 BLOCK_END_OF_CHAIN
);
7419 if (This
->headOfStreamPlaceHolder
!= 0)
7421 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7425 DirEntry chainEntry
;
7426 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7428 StorageImpl_ReadDirEntry(
7429 This
->parentStorage
,
7430 This
->ownerDirEntry
,
7433 chainEntry
.startingBlock
= blockIndex
;
7435 StorageImpl_WriteDirEntry(
7436 This
->parentStorage
,
7437 This
->ownerDirEntry
,
7441 This
->tailIndex
= blockIndex
;
7442 This
->numBlocks
= 1;
7446 * Figure out how many blocks are needed to contain this stream
7448 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7450 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7454 * Go to the current end of chain
7456 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7458 currentBlock
= blockIndex
;
7460 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7463 currentBlock
= blockIndex
;
7465 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7470 This
->tailIndex
= currentBlock
;
7473 currentBlock
= This
->tailIndex
;
7474 oldNumBlocks
= This
->numBlocks
;
7477 * Add new blocks to the chain
7479 if (oldNumBlocks
< newNumBlocks
)
7481 while (oldNumBlocks
< newNumBlocks
)
7483 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7485 StorageImpl_SetNextBlockInChain(
7486 This
->parentStorage
,
7490 StorageImpl_SetNextBlockInChain(
7491 This
->parentStorage
,
7493 BLOCK_END_OF_CHAIN
);
7495 currentBlock
= blockIndex
;
7499 This
->tailIndex
= blockIndex
;
7500 This
->numBlocks
= newNumBlocks
;
7503 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7510 /******************************************************************************
7511 * BlockChainStream_GetSize
7513 * Returns the size of this chain.
7514 * Will return the block count if this chain doesn't have a directory entry.
7516 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7518 DirEntry chainEntry
;
7520 if(This
->headOfStreamPlaceHolder
== NULL
)
7523 * This chain has a directory entry so use the size value from there.
7525 StorageImpl_ReadDirEntry(
7526 This
->parentStorage
,
7527 This
->ownerDirEntry
,
7530 return chainEntry
.size
;
7535 * this chain is a chain that does not have a directory entry, figure out the
7536 * size by making the product number of used blocks times the
7539 ULARGE_INTEGER result
;
7541 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7542 This
->parentStorage
->bigBlockSize
;
7548 /******************************************************************************
7549 * BlockChainStream_SetSize
7551 * Sets the size of this stream. The big block depot will be updated.
7552 * The file will grow if we grow the chain.
7554 * TODO: Free the actual blocks in the file when we shrink the chain.
7555 * Currently, the blocks are still in the file. So the file size
7556 * doesn't shrink even if we shrink streams.
7558 BOOL
BlockChainStream_SetSize(
7559 BlockChainStream
* This
,
7560 ULARGE_INTEGER newSize
)
7562 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7564 if (newSize
.QuadPart
== size
.QuadPart
)
7567 if (newSize
.QuadPart
< size
.QuadPart
)
7569 BlockChainStream_Shrink(This
, newSize
);
7573 BlockChainStream_Enlarge(This
, newSize
);
7579 /******************************************************************************
7580 * BlockChainStream_ReadAt
7582 * Reads a specified number of bytes from this chain at the specified offset.
7583 * bytesRead may be NULL.
7584 * Failure will be returned if the specified number of bytes has not been read.
7586 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7587 ULARGE_INTEGER offset
,
7592 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7593 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7594 ULONG bytesToReadInBuffer
;
7597 ULARGE_INTEGER stream_size
;
7599 BlockChainBlock
*cachedBlock
;
7601 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7604 * Find the first block in the stream that contains part of the buffer.
7606 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7610 stream_size
= BlockChainStream_GetSize(This
);
7611 if (stream_size
.QuadPart
> offset
.QuadPart
)
7612 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7617 * Start reading the buffer.
7619 bufferWalker
= buffer
;
7623 ULARGE_INTEGER ulOffset
;
7627 * Calculate how many bytes we can copy from this big block.
7629 bytesToReadInBuffer
=
7630 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7632 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7639 /* Not in cache, and we're going to read past the end of the block. */
7640 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7643 StorageImpl_ReadAt(This
->parentStorage
,
7646 bytesToReadInBuffer
,
7651 if (!cachedBlock
->read
)
7654 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7655 return STG_E_READFAULT
;
7657 cachedBlock
->read
= TRUE
;
7660 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7661 bytesReadAt
= bytesToReadInBuffer
;
7664 blockNoInSequence
++;
7665 bufferWalker
+= bytesReadAt
;
7666 size
-= bytesReadAt
;
7667 *bytesRead
+= bytesReadAt
;
7668 offsetInBlock
= 0; /* There is no offset on the next block */
7670 if (bytesToReadInBuffer
!= bytesReadAt
)
7677 /******************************************************************************
7678 * BlockChainStream_WriteAt
7680 * Writes the specified number of bytes to this chain at the specified offset.
7681 * Will fail if not all specified number of bytes have been written.
7683 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7684 ULARGE_INTEGER offset
,
7687 ULONG
* bytesWritten
)
7689 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7690 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7693 const BYTE
* bufferWalker
;
7695 BlockChainBlock
*cachedBlock
;
7698 bufferWalker
= buffer
;
7702 ULARGE_INTEGER ulOffset
;
7703 DWORD bytesWrittenAt
;
7706 * Calculate how many bytes we can copy to this big block.
7709 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7711 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7713 /* BlockChainStream_SetSize should have already been called to ensure we have
7714 * enough blocks in the chain to write into */
7717 ERR("not enough blocks in chain to write data\n");
7723 /* Not in cache, and we're going to write past the end of the block. */
7724 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7727 StorageImpl_WriteAt(This
->parentStorage
,
7735 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7738 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7739 return STG_E_READFAULT
;
7742 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7743 bytesWrittenAt
= bytesToWrite
;
7744 cachedBlock
->read
= TRUE
;
7745 cachedBlock
->dirty
= TRUE
;
7748 blockNoInSequence
++;
7749 bufferWalker
+= bytesWrittenAt
;
7750 size
-= bytesWrittenAt
;
7751 *bytesWritten
+= bytesWrittenAt
;
7752 offsetInBlock
= 0; /* There is no offset on the next block */
7754 if (bytesWrittenAt
!= bytesToWrite
)
7758 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7762 /************************************************************************
7763 * SmallBlockChainStream implementation
7764 ***********************************************************************/
7766 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7767 StorageImpl
* parentStorage
,
7768 ULONG
* headOfStreamPlaceHolder
,
7771 SmallBlockChainStream
* newStream
;
7773 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7775 newStream
->parentStorage
= parentStorage
;
7776 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7777 newStream
->ownerDirEntry
= dirEntry
;
7782 void SmallBlockChainStream_Destroy(
7783 SmallBlockChainStream
* This
)
7785 HeapFree(GetProcessHeap(), 0, This
);
7788 /******************************************************************************
7789 * SmallBlockChainStream_GetHeadOfChain
7791 * Returns the head of this chain of small blocks.
7793 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7794 SmallBlockChainStream
* This
)
7796 DirEntry chainEntry
;
7799 if (This
->headOfStreamPlaceHolder
!= NULL
)
7800 return *(This
->headOfStreamPlaceHolder
);
7802 if (This
->ownerDirEntry
)
7804 hr
= StorageImpl_ReadDirEntry(
7805 This
->parentStorage
,
7806 This
->ownerDirEntry
,
7809 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7810 return chainEntry
.startingBlock
;
7813 return BLOCK_END_OF_CHAIN
;
7816 /******************************************************************************
7817 * SmallBlockChainStream_GetNextBlockInChain
7819 * Returns the index of the next small block in this chain.
7822 * - BLOCK_END_OF_CHAIN: end of this chain
7823 * - BLOCK_UNUSED: small block 'blockIndex' is free
7825 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7826 SmallBlockChainStream
* This
,
7828 ULONG
* nextBlockInChain
)
7830 ULARGE_INTEGER offsetOfBlockInDepot
;
7835 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7837 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7840 * Read those bytes in the buffer from the small block file.
7842 res
= BlockChainStream_ReadAt(
7843 This
->parentStorage
->smallBlockDepotChain
,
7844 offsetOfBlockInDepot
,
7849 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7850 res
= STG_E_READFAULT
;
7854 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7861 /******************************************************************************
7862 * SmallBlockChainStream_SetNextBlockInChain
7864 * Writes the index of the next block of the specified block in the small
7866 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7867 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7869 static void SmallBlockChainStream_SetNextBlockInChain(
7870 SmallBlockChainStream
* This
,
7874 ULARGE_INTEGER offsetOfBlockInDepot
;
7878 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7880 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7883 * Read those bytes in the buffer from the small block file.
7885 BlockChainStream_WriteAt(
7886 This
->parentStorage
->smallBlockDepotChain
,
7887 offsetOfBlockInDepot
,
7893 /******************************************************************************
7894 * SmallBlockChainStream_FreeBlock
7896 * Flag small block 'blockIndex' as free in the small block depot.
7898 static void SmallBlockChainStream_FreeBlock(
7899 SmallBlockChainStream
* This
,
7902 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7905 /******************************************************************************
7906 * SmallBlockChainStream_GetNextFreeBlock
7908 * Returns the index of a free small block. The small block depot will be
7909 * enlarged if necessary. The small block chain will also be enlarged if
7912 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7913 SmallBlockChainStream
* This
)
7915 ULARGE_INTEGER offsetOfBlockInDepot
;
7918 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7919 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7921 ULONG smallBlocksPerBigBlock
;
7923 ULONG blocksRequired
;
7924 ULARGE_INTEGER old_size
, size_required
;
7926 offsetOfBlockInDepot
.u
.HighPart
= 0;
7929 * Scan the small block depot for a free block
7931 while (nextBlockIndex
!= BLOCK_UNUSED
)
7933 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7935 res
= BlockChainStream_ReadAt(
7936 This
->parentStorage
->smallBlockDepotChain
,
7937 offsetOfBlockInDepot
,
7943 * If we run out of space for the small block depot, enlarge it
7945 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7947 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7949 if (nextBlockIndex
!= BLOCK_UNUSED
)
7955 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7957 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7958 ULARGE_INTEGER newSize
, offset
;
7961 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7962 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7965 * Initialize all the small blocks to free
7967 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7968 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7969 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7970 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7972 StorageImpl_SaveFileHeader(This
->parentStorage
);
7976 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7978 smallBlocksPerBigBlock
=
7979 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7982 * Verify if we have to allocate big blocks to contain small blocks
7984 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7986 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7988 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7990 if (size_required
.QuadPart
> old_size
.QuadPart
)
7992 BlockChainStream_SetSize(
7993 This
->parentStorage
->smallBlockRootChain
,
7996 StorageImpl_ReadDirEntry(
7997 This
->parentStorage
,
7998 This
->parentStorage
->base
.storageDirEntry
,
8001 rootEntry
.size
= size_required
;
8003 StorageImpl_WriteDirEntry(
8004 This
->parentStorage
,
8005 This
->parentStorage
->base
.storageDirEntry
,
8012 /******************************************************************************
8013 * SmallBlockChainStream_ReadAt
8015 * Reads a specified number of bytes from this chain at the specified offset.
8016 * bytesRead may be NULL.
8017 * Failure will be returned if the specified number of bytes has not been read.
8019 HRESULT
SmallBlockChainStream_ReadAt(
8020 SmallBlockChainStream
* This
,
8021 ULARGE_INTEGER offset
,
8027 ULARGE_INTEGER offsetInBigBlockFile
;
8028 ULONG blockNoInSequence
=
8029 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8031 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8032 ULONG bytesToReadInBuffer
;
8034 ULONG bytesReadFromBigBlockFile
;
8036 ULARGE_INTEGER stream_size
;
8039 * This should never happen on a small block file.
8041 assert(offset
.u
.HighPart
==0);
8045 stream_size
= SmallBlockChainStream_GetSize(This
);
8046 if (stream_size
.QuadPart
> offset
.QuadPart
)
8047 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
8052 * Find the first block in the stream that contains part of the buffer.
8054 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8056 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8058 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8061 blockNoInSequence
--;
8065 * Start reading the buffer.
8067 bufferWalker
= buffer
;
8069 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8072 * Calculate how many bytes we can copy from this small block.
8074 bytesToReadInBuffer
=
8075 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8078 * Calculate the offset of the small block in the small block file.
8080 offsetInBigBlockFile
.QuadPart
=
8081 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8083 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8086 * Read those bytes in the buffer from the small block file.
8087 * The small block has already been identified so it shouldn't fail
8088 * unless the file is corrupt.
8090 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
8091 offsetInBigBlockFile
,
8092 bytesToReadInBuffer
,
8094 &bytesReadFromBigBlockFile
);
8099 if (!bytesReadFromBigBlockFile
)
8100 return STG_E_DOCFILECORRUPT
;
8103 * Step to the next big block.
8105 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8107 return STG_E_DOCFILECORRUPT
;
8109 bufferWalker
+= bytesReadFromBigBlockFile
;
8110 size
-= bytesReadFromBigBlockFile
;
8111 *bytesRead
+= bytesReadFromBigBlockFile
;
8112 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8118 /******************************************************************************
8119 * SmallBlockChainStream_WriteAt
8121 * Writes the specified number of bytes to this chain at the specified offset.
8122 * Will fail if not all specified number of bytes have been written.
8124 HRESULT
SmallBlockChainStream_WriteAt(
8125 SmallBlockChainStream
* This
,
8126 ULARGE_INTEGER offset
,
8129 ULONG
* bytesWritten
)
8131 ULARGE_INTEGER offsetInBigBlockFile
;
8132 ULONG blockNoInSequence
=
8133 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8135 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8136 ULONG bytesToWriteInBuffer
;
8138 ULONG bytesWrittenToBigBlockFile
;
8139 const BYTE
* bufferWalker
;
8143 * This should never happen on a small block file.
8145 assert(offset
.u
.HighPart
==0);
8148 * Find the first block in the stream that contains part of the buffer.
8150 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8152 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8154 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8155 return STG_E_DOCFILECORRUPT
;
8156 blockNoInSequence
--;
8160 * Start writing the buffer.
8163 bufferWalker
= buffer
;
8164 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8167 * Calculate how many bytes we can copy to this small block.
8169 bytesToWriteInBuffer
=
8170 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8173 * Calculate the offset of the small block in the small block file.
8175 offsetInBigBlockFile
.QuadPart
=
8176 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8178 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8181 * Write those bytes in the buffer to the small block file.
8183 res
= BlockChainStream_WriteAt(
8184 This
->parentStorage
->smallBlockRootChain
,
8185 offsetInBigBlockFile
,
8186 bytesToWriteInBuffer
,
8188 &bytesWrittenToBigBlockFile
);
8193 * Step to the next big block.
8195 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8198 bufferWalker
+= bytesWrittenToBigBlockFile
;
8199 size
-= bytesWrittenToBigBlockFile
;
8200 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8201 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8204 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8207 /******************************************************************************
8208 * SmallBlockChainStream_Shrink
8210 * Shrinks this chain in the small block depot.
8212 static BOOL
SmallBlockChainStream_Shrink(
8213 SmallBlockChainStream
* This
,
8214 ULARGE_INTEGER newSize
)
8216 ULONG blockIndex
, extraBlock
;
8220 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8222 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8225 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8228 * Go to the new end of chain
8230 while (count
< numBlocks
)
8232 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8239 * If the count is 0, we have a special case, the head of the chain was
8244 DirEntry chainEntry
;
8246 StorageImpl_ReadDirEntry(This
->parentStorage
,
8247 This
->ownerDirEntry
,
8250 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8252 StorageImpl_WriteDirEntry(This
->parentStorage
,
8253 This
->ownerDirEntry
,
8257 * We start freeing the chain at the head block.
8259 extraBlock
= blockIndex
;
8263 /* Get the next block before marking the new end */
8264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8268 /* Mark the new end of chain */
8269 SmallBlockChainStream_SetNextBlockInChain(
8272 BLOCK_END_OF_CHAIN
);
8276 * Mark the extra blocks as free
8278 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8280 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8283 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8284 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8285 extraBlock
= blockIndex
;
8291 /******************************************************************************
8292 * SmallBlockChainStream_Enlarge
8294 * Grows this chain in the small block depot.
8296 static BOOL
SmallBlockChainStream_Enlarge(
8297 SmallBlockChainStream
* This
,
8298 ULARGE_INTEGER newSize
)
8300 ULONG blockIndex
, currentBlock
;
8302 ULONG oldNumBlocks
= 0;
8304 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8307 * Empty chain. Create the head.
8309 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8311 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8312 SmallBlockChainStream_SetNextBlockInChain(
8315 BLOCK_END_OF_CHAIN
);
8317 if (This
->headOfStreamPlaceHolder
!= NULL
)
8319 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8323 DirEntry chainEntry
;
8325 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8328 chainEntry
.startingBlock
= blockIndex
;
8330 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8335 currentBlock
= blockIndex
;
8338 * Figure out how many blocks are needed to contain this stream
8340 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8342 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8346 * Go to the current end of chain
8348 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8351 currentBlock
= blockIndex
;
8352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8357 * Add new blocks to the chain
8359 while (oldNumBlocks
< newNumBlocks
)
8361 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8362 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8364 SmallBlockChainStream_SetNextBlockInChain(
8367 BLOCK_END_OF_CHAIN
);
8369 currentBlock
= blockIndex
;
8376 /******************************************************************************
8377 * SmallBlockChainStream_SetSize
8379 * Sets the size of this stream.
8380 * The file will grow if we grow the chain.
8382 * TODO: Free the actual blocks in the file when we shrink the chain.
8383 * Currently, the blocks are still in the file. So the file size
8384 * doesn't shrink even if we shrink streams.
8386 BOOL
SmallBlockChainStream_SetSize(
8387 SmallBlockChainStream
* This
,
8388 ULARGE_INTEGER newSize
)
8390 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8392 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8395 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8397 SmallBlockChainStream_Shrink(This
, newSize
);
8401 SmallBlockChainStream_Enlarge(This
, newSize
);
8407 /******************************************************************************
8408 * SmallBlockChainStream_GetCount
8410 * Returns the number of small blocks that comprises this chain.
8411 * This is not the size of the stream as the last block may not be full!
8414 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8419 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8421 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8425 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8426 blockIndex
, &blockIndex
)))
8433 /******************************************************************************
8434 * SmallBlockChainStream_GetSize
8436 * Returns the size of this chain.
8438 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8440 DirEntry chainEntry
;
8442 if(This
->headOfStreamPlaceHolder
!= NULL
)
8444 ULARGE_INTEGER result
;
8445 result
.u
.HighPart
= 0;
8447 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8448 This
->parentStorage
->smallBlockSize
;
8453 StorageImpl_ReadDirEntry(
8454 This
->parentStorage
,
8455 This
->ownerDirEntry
,
8458 return chainEntry
.size
;
8462 /************************************************************************
8463 * Miscellaneous storage functions
8464 ***********************************************************************/
8466 static HRESULT
create_storagefile(
8470 STGOPTIONS
* pStgOptions
,
8474 StorageBaseImpl
* newStorage
= 0;
8475 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8476 HRESULT hr
= STG_E_INVALIDFLAG
;
8480 DWORD fileAttributes
;
8481 WCHAR tempFileName
[MAX_PATH
];
8484 return STG_E_INVALIDPOINTER
;
8486 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8487 return STG_E_INVALIDPARAMETER
;
8489 /* if no share mode given then DENY_NONE is the default */
8490 if (STGM_SHARE_MODE(grfMode
) == 0)
8491 grfMode
|= STGM_SHARE_DENY_NONE
;
8493 if ( FAILED( validateSTGM(grfMode
) ))
8496 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8497 switch(STGM_ACCESS_MODE(grfMode
))
8500 case STGM_READWRITE
:
8506 /* in direct mode, can only use SHARE_EXCLUSIVE */
8507 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8510 /* but in transacted mode, any share mode is valid */
8513 * Generate a unique name.
8517 WCHAR tempPath
[MAX_PATH
];
8518 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8520 memset(tempPath
, 0, sizeof(tempPath
));
8521 memset(tempFileName
, 0, sizeof(tempFileName
));
8523 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8526 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8527 pwcsName
= tempFileName
;
8530 hr
= STG_E_INSUFFICIENTMEMORY
;
8534 creationMode
= TRUNCATE_EXISTING
;
8538 creationMode
= GetCreationModeFromSTGM(grfMode
);
8542 * Interpret the STGM value grfMode
8544 shareMode
= GetShareModeFromSTGM(grfMode
);
8545 accessMode
= GetAccessModeFromSTGM(grfMode
);
8547 if (grfMode
& STGM_DELETEONRELEASE
)
8548 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8550 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8554 hFile
= CreateFileW(pwcsName
,
8562 if (hFile
== INVALID_HANDLE_VALUE
)
8564 if(GetLastError() == ERROR_FILE_EXISTS
)
8565 hr
= STG_E_FILEALREADYEXISTS
;
8572 * Allocate and initialize the new IStorage object.
8574 hr
= Storage_Construct(
8581 pStgOptions
->ulSectorSize
,
8589 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8590 IStorage_Release(&newStorage
->IStorage_iface
);
8593 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8598 /******************************************************************************
8599 * StgCreateDocfile [OLE32.@]
8600 * Creates a new compound file storage object
8603 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8604 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8605 * reserved [ ?] unused?, usually 0
8606 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8609 * S_OK if the file was successfully created
8610 * some STG_E_ value if error
8612 * if pwcsName is NULL, create file with new unique name
8613 * the function can returns
8614 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8617 HRESULT WINAPI
StgCreateDocfile(
8621 IStorage
**ppstgOpen
)
8623 STGOPTIONS stgoptions
= {1, 0, 512};
8625 TRACE("(%s, %x, %d, %p)\n",
8626 debugstr_w(pwcsName
), grfMode
,
8627 reserved
, ppstgOpen
);
8630 return STG_E_INVALIDPOINTER
;
8632 return STG_E_INVALIDPARAMETER
;
8634 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8637 /******************************************************************************
8638 * StgCreateStorageEx [OLE32.@]
8640 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8642 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8643 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8645 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8647 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8648 return STG_E_INVALIDPARAMETER
;
8651 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8653 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8654 return STG_E_INVALIDPARAMETER
;
8657 if (stgfmt
== STGFMT_FILE
)
8659 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8660 return STG_E_INVALIDPARAMETER
;
8663 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8665 STGOPTIONS defaultOptions
= {1, 0, 512};
8667 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8668 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8672 ERR("Invalid stgfmt argument\n");
8673 return STG_E_INVALIDPARAMETER
;
8676 /******************************************************************************
8677 * StgCreatePropSetStg [OLE32.@]
8679 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8680 IPropertySetStorage
**propset
)
8682 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8684 return STG_E_INVALIDPARAMETER
;
8686 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8689 /******************************************************************************
8690 * StgOpenStorageEx [OLE32.@]
8692 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8694 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8695 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8697 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8699 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8700 return STG_E_INVALIDPARAMETER
;
8706 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8707 return STG_E_INVALIDPARAMETER
;
8709 case STGFMT_STORAGE
:
8712 case STGFMT_DOCFILE
:
8713 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8715 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8716 return STG_E_INVALIDPARAMETER
;
8718 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8722 WARN("STGFMT_ANY assuming storage\n");
8726 return STG_E_INVALIDPARAMETER
;
8729 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8733 /******************************************************************************
8734 * StgOpenStorage [OLE32.@]
8736 HRESULT WINAPI
StgOpenStorage(
8737 const OLECHAR
*pwcsName
,
8738 IStorage
*pstgPriority
,
8742 IStorage
**ppstgOpen
)
8744 StorageBaseImpl
* newStorage
= 0;
8749 LPWSTR temp_name
= NULL
;
8751 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8752 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8753 snbExclude
, reserved
, ppstgOpen
);
8757 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8758 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8759 if (FAILED(hr
)) goto end
;
8760 pwcsName
= temp_name
;
8761 TRACE("using filename %s\n", debugstr_w(temp_name
));
8766 hr
= STG_E_INVALIDNAME
;
8772 hr
= STG_E_INVALIDPOINTER
;
8778 hr
= STG_E_INVALIDPARAMETER
;
8782 if (grfMode
& STGM_PRIORITY
)
8784 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8785 return STG_E_INVALIDFLAG
;
8786 if (grfMode
& STGM_DELETEONRELEASE
)
8787 return STG_E_INVALIDFUNCTION
;
8788 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8789 return STG_E_INVALIDFLAG
;
8790 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8791 grfMode
|= STGM_SHARE_DENY_NONE
;
8795 * Validate the sharing mode
8797 if (grfMode
& STGM_DIRECT_SWMR
)
8799 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8800 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8802 hr
= STG_E_INVALIDFLAG
;
8806 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8807 switch(STGM_SHARE_MODE(grfMode
))
8809 case STGM_SHARE_EXCLUSIVE
:
8810 case STGM_SHARE_DENY_WRITE
:
8813 hr
= STG_E_INVALIDFLAG
;
8817 if ( FAILED( validateSTGM(grfMode
) ) ||
8818 (grfMode
&STGM_CREATE
))
8820 hr
= STG_E_INVALIDFLAG
;
8824 /* shared reading requires transacted or single writer mode */
8825 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8826 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8827 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8829 hr
= STG_E_INVALIDFLAG
;
8834 * Interpret the STGM value grfMode
8836 shareMode
= GetShareModeFromSTGM(grfMode
);
8837 accessMode
= GetAccessModeFromSTGM(grfMode
);
8841 hFile
= CreateFileW( pwcsName
,
8846 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8849 if (hFile
==INVALID_HANDLE_VALUE
)
8851 DWORD last_error
= GetLastError();
8857 case ERROR_FILE_NOT_FOUND
:
8858 hr
= STG_E_FILENOTFOUND
;
8861 case ERROR_PATH_NOT_FOUND
:
8862 hr
= STG_E_PATHNOTFOUND
;
8865 case ERROR_ACCESS_DENIED
:
8866 case ERROR_WRITE_PROTECT
:
8867 hr
= STG_E_ACCESSDENIED
;
8870 case ERROR_SHARING_VIOLATION
:
8871 hr
= STG_E_SHAREVIOLATION
;
8882 * Refuse to open the file if it's too small to be a structured storage file
8883 * FIXME: verify the file when reading instead of here
8885 if (GetFileSize(hFile
, NULL
) < 0x100)
8888 hr
= STG_E_FILEALREADYEXISTS
;
8893 * Allocate and initialize the new IStorage object.
8895 hr
= Storage_Construct(
8908 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8910 if(hr
== STG_E_INVALIDHEADER
)
8911 hr
= STG_E_FILEALREADYEXISTS
;
8915 *ppstgOpen
= &newStorage
->IStorage_iface
;
8918 CoTaskMemFree(temp_name
);
8919 if (pstgPriority
) IStorage_Release(pstgPriority
);
8920 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8924 /******************************************************************************
8925 * StgCreateDocfileOnILockBytes [OLE32.@]
8927 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8931 IStorage
** ppstgOpen
)
8933 StorageBaseImpl
* newStorage
= 0;
8936 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8937 return STG_E_INVALIDPOINTER
;
8940 * Allocate and initialize the new IStorage object.
8942 hr
= Storage_Construct(
8957 *ppstgOpen
= &newStorage
->IStorage_iface
;
8962 /******************************************************************************
8963 * StgOpenStorageOnILockBytes [OLE32.@]
8965 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8967 IStorage
*pstgPriority
,
8971 IStorage
**ppstgOpen
)
8973 StorageBaseImpl
* newStorage
= 0;
8976 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8977 return STG_E_INVALIDPOINTER
;
8979 if ( FAILED( validateSTGM(grfMode
) ))
8980 return STG_E_INVALIDFLAG
;
8985 * Allocate and initialize the new IStorage object.
8987 hr
= Storage_Construct(
9002 *ppstgOpen
= &newStorage
->IStorage_iface
;
9007 /******************************************************************************
9008 * StgSetTimes [ole32.@]
9009 * StgSetTimes [OLE32.@]
9013 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
9014 FILETIME
const *patime
, FILETIME
const *pmtime
)
9016 IStorage
*stg
= NULL
;
9019 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
9021 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
9025 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
9026 IStorage_Release(stg
);
9032 /******************************************************************************
9033 * StgIsStorageILockBytes [OLE32.@]
9035 * Determines if the ILockBytes contains a storage object.
9037 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
9039 BYTE sig
[sizeof(STORAGE_magic
)];
9040 ULARGE_INTEGER offset
;
9043 offset
.u
.HighPart
= 0;
9044 offset
.u
.LowPart
= 0;
9046 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
9048 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
9054 /******************************************************************************
9055 * WriteClassStg [OLE32.@]
9057 * This method will store the specified CLSID in the specified storage object
9059 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
9062 return E_INVALIDARG
;
9065 return STG_E_INVALIDPOINTER
;
9067 return IStorage_SetClass(pStg
, rclsid
);
9070 /***********************************************************************
9071 * ReadClassStg (OLE32.@)
9073 * This method reads the CLSID previously written to a storage object with
9074 * the WriteClassStg.
9077 * pstg [I] IStorage pointer
9078 * pclsid [O] Pointer to where the CLSID is written
9082 * Failure: HRESULT code.
9084 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
9089 TRACE("(%p, %p)\n", pstg
, pclsid
);
9091 if(!pstg
|| !pclsid
)
9092 return E_INVALIDARG
;
9095 * read a STATSTG structure (contains the clsid) from the storage
9097 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9100 *pclsid
=pstatstg
.clsid
;
9105 /***********************************************************************
9106 * OleLoadFromStream (OLE32.@)
9108 * This function loads an object from stream
9110 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9114 LPPERSISTSTREAM xstm
;
9116 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9118 res
=ReadClassStm(pStm
,&clsid
);
9121 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9124 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9126 IUnknown_Release((IUnknown
*)*ppvObj
);
9129 res
=IPersistStream_Load(xstm
,pStm
);
9130 IPersistStream_Release(xstm
);
9131 /* FIXME: all refcounts ok at this point? I think they should be:
9134 * xstm : 0 (released)
9139 /***********************************************************************
9140 * OleSaveToStream (OLE32.@)
9142 * This function saves an object with the IPersistStream interface on it
9143 * to the specified stream.
9145 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9151 TRACE("(%p,%p)\n",pPStm
,pStm
);
9153 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9155 if (SUCCEEDED(res
)){
9157 res
=WriteClassStm(pStm
,&clsid
);
9161 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9164 TRACE("Finished Save\n");
9168 /*************************************************************************
9169 * STORAGE_CreateOleStream [Internal]
9171 * Creates the "\001OLE" stream in the IStorage if necessary.
9174 * storage [I] Dest storage to create the stream in
9175 * flags [I] flags to be set for newly created stream
9178 * HRESULT return value
9182 * This stream is still unknown, MS Word seems to have extra data
9183 * but since the data is stored in the OLESTREAM there should be
9184 * no need to recreate the stream. If the stream is manually
9185 * deleted it will create it with this default data.
9188 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9190 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9191 static const DWORD version_magic
= 0x02000001;
9195 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9198 struct empty_1ole_stream
{
9199 DWORD version_magic
;
9201 DWORD update_options
;
9203 DWORD mon_stream_size
;
9205 struct empty_1ole_stream stream_data
;
9207 stream_data
.version_magic
= version_magic
;
9208 stream_data
.flags
= flags
;
9209 stream_data
.update_options
= 0;
9210 stream_data
.reserved
= 0;
9211 stream_data
.mon_stream_size
= 0;
9213 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9214 IStream_Release(stream
);
9220 /* write a string to a stream, preceded by its length */
9221 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9228 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9229 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9234 str
= CoTaskMemAlloc( len
);
9235 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9236 r
= IStream_Write( stm
, str
, len
, NULL
);
9237 CoTaskMemFree( str
);
9241 /* read a string preceded by its length from a stream */
9242 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9245 DWORD len
, count
= 0;
9249 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9252 if( count
!= sizeof(len
) )
9253 return E_OUTOFMEMORY
;
9255 TRACE("%d bytes\n",len
);
9257 str
= CoTaskMemAlloc( len
);
9259 return E_OUTOFMEMORY
;
9261 r
= IStream_Read( stm
, str
, len
, &count
);
9264 CoTaskMemFree( str
);
9269 CoTaskMemFree( str
);
9270 return E_OUTOFMEMORY
;
9273 TRACE("Read string %s\n",debugstr_an(str
,len
));
9275 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9276 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9279 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9282 CoTaskMemFree( str
);
9290 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9291 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9295 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9297 static const BYTE unknown1
[12] =
9298 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9299 0xFF, 0xFF, 0xFF, 0xFF};
9300 static const BYTE unknown2
[16] =
9301 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9304 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9305 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9306 debugstr_w(szProgIDName
));
9308 /* Create a CompObj stream */
9309 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9310 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9314 /* Write CompObj Structure to stream */
9315 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9317 if( SUCCEEDED( r
) )
9318 r
= WriteClassStm( pstm
, clsid
);
9320 if( SUCCEEDED( r
) )
9321 r
= STREAM_WriteString( pstm
, lpszUserType
);
9322 if( SUCCEEDED( r
) )
9323 r
= STREAM_WriteString( pstm
, szClipName
);
9324 if( SUCCEEDED( r
) )
9325 r
= STREAM_WriteString( pstm
, szProgIDName
);
9326 if( SUCCEEDED( r
) )
9327 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9329 IStream_Release( pstm
);
9334 /***********************************************************************
9335 * WriteFmtUserTypeStg (OLE32.@)
9337 HRESULT WINAPI
WriteFmtUserTypeStg(
9338 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9342 WCHAR szwClipName
[0x40];
9344 LPWSTR wstrProgID
= NULL
;
9347 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9349 /* get the clipboard format name */
9352 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9353 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9357 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9359 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9365 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9367 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9369 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9370 cf
? szwClipName
: NULL
, wstrProgID
);
9372 CoTaskMemFree(wstrProgID
);
9378 /******************************************************************************
9379 * ReadFmtUserTypeStg [OLE32.@]
9381 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9385 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9386 unsigned char unknown1
[12];
9387 unsigned char unknown2
[16];
9389 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9392 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9394 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9395 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9398 WARN("Failed to open stream r = %08x\n", r
);
9402 /* read the various parts of the structure */
9403 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9404 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9406 r
= ReadClassStm( stm
, &clsid
);
9410 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9414 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9418 r
= STREAM_ReadString( stm
, &szProgIDName
);
9422 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9423 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9426 /* ok, success... now we just need to store what we found */
9428 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9430 if( lplpszUserType
)
9432 *lplpszUserType
= szCLSIDName
;
9437 CoTaskMemFree( szCLSIDName
);
9438 CoTaskMemFree( szOleTypeName
);
9439 CoTaskMemFree( szProgIDName
);
9440 IStream_Release( stm
);
9445 /******************************************************************************
9446 * StgIsStorageFile [OLE32.@]
9447 * Verify if the file contains a storage object
9453 * S_OK if file has magic bytes as a storage object
9454 * S_FALSE if file is not storage
9457 StgIsStorageFile(LPCOLESTR fn
)
9463 TRACE("%s\n", debugstr_w(fn
));
9464 hf
= CreateFileW(fn
, GENERIC_READ
,
9465 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9466 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9468 if (hf
== INVALID_HANDLE_VALUE
)
9469 return STG_E_FILENOTFOUND
;
9471 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9473 WARN(" unable to read file\n");
9480 if (bytes_read
!= 8) {
9481 TRACE(" too short\n");
9485 if (!memcmp(magic
,STORAGE_magic
,8)) {
9490 TRACE(" -> Invalid header.\n");
9494 /***********************************************************************
9495 * WriteClassStm (OLE32.@)
9497 * Writes a CLSID to a stream.
9500 * pStm [I] Stream to write to.
9501 * rclsid [I] CLSID to write.
9505 * Failure: HRESULT code.
9507 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9509 TRACE("(%p,%p)\n",pStm
,rclsid
);
9511 if (!pStm
|| !rclsid
)
9512 return E_INVALIDARG
;
9514 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9517 /***********************************************************************
9518 * ReadClassStm (OLE32.@)
9520 * Reads a CLSID from a stream.
9523 * pStm [I] Stream to read from.
9524 * rclsid [O] CLSID to read.
9528 * Failure: HRESULT code.
9530 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9535 TRACE("(%p,%p)\n",pStm
,pclsid
);
9537 if (!pStm
|| !pclsid
)
9538 return E_INVALIDARG
;
9540 /* clear the output args */
9541 *pclsid
= CLSID_NULL
;
9543 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9548 if (nbByte
!= sizeof(CLSID
))
9549 return STG_E_READFAULT
;
9555 /************************************************************************
9556 * OleConvert Functions
9557 ***********************************************************************/
9559 #define OLESTREAM_ID 0x501
9560 #define OLESTREAM_MAX_STR_LEN 255
9562 /* OLESTREAM memory structure to use for Get and Put Routines */
9567 DWORD dwOleTypeNameLength
;
9568 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9569 CHAR
*pstrOleObjFileName
;
9570 DWORD dwOleObjFileNameLength
;
9571 DWORD dwMetaFileWidth
;
9572 DWORD dwMetaFileHeight
;
9573 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9576 } OLECONVERT_OLESTREAM_DATA
;
9578 /* CompObj Stream structure */
9581 BYTE byUnknown1
[12];
9583 DWORD dwCLSIDNameLength
;
9584 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9585 DWORD dwOleTypeNameLength
;
9586 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9587 DWORD dwProgIDNameLength
;
9588 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9589 BYTE byUnknown2
[16];
9590 } OLECONVERT_ISTORAGE_COMPOBJ
;
9592 /* Ole Presentation Stream structure */
9595 BYTE byUnknown1
[28];
9600 } OLECONVERT_ISTORAGE_OLEPRES
;
9603 /*************************************************************************
9604 * OLECONVERT_LoadOLE10 [Internal]
9606 * Loads the OLE10 STREAM to memory
9609 * pOleStream [I] The OLESTREAM
9610 * pData [I] Data Structure for the OLESTREAM Data
9614 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9615 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9618 * This function is used by OleConvertOLESTREAMToIStorage only.
9620 * Memory allocated for pData must be freed by the caller
9622 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9625 HRESULT hRes
= S_OK
;
9629 pData
->pData
= NULL
;
9630 pData
->pstrOleObjFileName
= NULL
;
9632 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9635 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9636 if(dwSize
!= sizeof(pData
->dwOleID
))
9638 hRes
= CONVERT10_E_OLESTREAM_GET
;
9640 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9642 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9653 /* Get the TypeID... more info needed for this field */
9654 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9655 if(dwSize
!= sizeof(pData
->dwTypeID
))
9657 hRes
= CONVERT10_E_OLESTREAM_GET
;
9662 if(pData
->dwTypeID
!= 0)
9664 /* Get the length of the OleTypeName */
9665 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9666 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9668 hRes
= CONVERT10_E_OLESTREAM_GET
;
9673 if(pData
->dwOleTypeNameLength
> 0)
9675 /* Get the OleTypeName */
9676 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9677 if(dwSize
!= pData
->dwOleTypeNameLength
)
9679 hRes
= CONVERT10_E_OLESTREAM_GET
;
9685 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9686 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9688 hRes
= CONVERT10_E_OLESTREAM_GET
;
9692 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9693 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9694 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9695 if(pData
->pstrOleObjFileName
)
9697 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9698 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9700 hRes
= CONVERT10_E_OLESTREAM_GET
;
9704 hRes
= CONVERT10_E_OLESTREAM_GET
;
9709 /* Get the Width of the Metafile */
9710 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9711 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9713 hRes
= CONVERT10_E_OLESTREAM_GET
;
9717 /* Get the Height of the Metafile */
9718 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9719 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9721 hRes
= CONVERT10_E_OLESTREAM_GET
;
9727 /* Get the Length of the Data */
9728 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9729 if(dwSize
!= sizeof(pData
->dwDataLength
))
9731 hRes
= CONVERT10_E_OLESTREAM_GET
;
9735 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9737 if(!bStrem1
) /* if it is a second OLE stream data */
9739 pData
->dwDataLength
-= 8;
9740 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9741 if(dwSize
!= sizeof(pData
->strUnknown
))
9743 hRes
= CONVERT10_E_OLESTREAM_GET
;
9749 if(pData
->dwDataLength
> 0)
9751 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9753 /* Get Data (ex. IStorage, Metafile, or BMP) */
9756 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9757 if(dwSize
!= pData
->dwDataLength
)
9759 hRes
= CONVERT10_E_OLESTREAM_GET
;
9764 hRes
= CONVERT10_E_OLESTREAM_GET
;
9773 /*************************************************************************
9774 * OLECONVERT_SaveOLE10 [Internal]
9776 * Saves the OLE10 STREAM From memory
9779 * pData [I] Data Structure for the OLESTREAM Data
9780 * pOleStream [I] The OLESTREAM to save
9784 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9787 * This function is used by OleConvertIStorageToOLESTREAM only.
9790 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9793 HRESULT hRes
= S_OK
;
9797 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9798 if(dwSize
!= sizeof(pData
->dwOleID
))
9800 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9805 /* Set the TypeID */
9806 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9807 if(dwSize
!= sizeof(pData
->dwTypeID
))
9809 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9813 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9815 /* Set the Length of the OleTypeName */
9816 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9817 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9819 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9824 if(pData
->dwOleTypeNameLength
> 0)
9826 /* Set the OleTypeName */
9827 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9828 if(dwSize
!= pData
->dwOleTypeNameLength
)
9830 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9837 /* Set the width of the Metafile */
9838 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9839 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9841 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9847 /* Set the height of the Metafile */
9848 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9849 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9851 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9857 /* Set the length of the Data */
9858 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9859 if(dwSize
!= sizeof(pData
->dwDataLength
))
9861 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9867 if(pData
->dwDataLength
> 0)
9869 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9870 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9871 if(dwSize
!= pData
->dwDataLength
)
9873 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9881 /*************************************************************************
9882 * OLECONVERT_GetOLE20FromOLE10[Internal]
9884 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9885 * opens it, and copies the content to the dest IStorage for
9886 * OleConvertOLESTREAMToIStorage
9890 * pDestStorage [I] The IStorage to copy the data to
9891 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9892 * nBufferLength [I] The size of the buffer
9901 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9905 IStorage
*pTempStorage
;
9906 DWORD dwNumOfBytesWritten
;
9907 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9908 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9910 /* Create a temp File */
9911 GetTempPathW(MAX_PATH
, wstrTempDir
);
9912 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9913 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9915 if(hFile
!= INVALID_HANDLE_VALUE
)
9917 /* Write IStorage Data to File */
9918 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9921 /* Open and copy temp storage to the Dest Storage */
9922 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9925 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9926 IStorage_Release(pTempStorage
);
9928 DeleteFileW(wstrTempFile
);
9933 /*************************************************************************
9934 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9936 * Saves the OLE10 STREAM From memory
9939 * pStorage [I] The Src IStorage to copy
9940 * pData [I] The Dest Memory to write to.
9943 * The size in bytes allocated for pData
9946 * Memory allocated for pData must be freed by the caller
9948 * Used by OleConvertIStorageToOLESTREAM only.
9951 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9955 DWORD nDataLength
= 0;
9956 IStorage
*pTempStorage
;
9957 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9958 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9962 /* Create temp Storage */
9963 GetTempPathW(MAX_PATH
, wstrTempDir
);
9964 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9965 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9969 /* Copy Src Storage to the Temp Storage */
9970 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9971 IStorage_Release(pTempStorage
);
9973 /* Open Temp Storage as a file and copy to memory */
9974 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9975 if(hFile
!= INVALID_HANDLE_VALUE
)
9977 nDataLength
= GetFileSize(hFile
, NULL
);
9978 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9979 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9982 DeleteFileW(wstrTempFile
);
9987 /*************************************************************************
9988 * OLECONVERT_CreateCompObjStream [Internal]
9990 * Creates a "\001CompObj" is the destination IStorage if necessary.
9993 * pStorage [I] The dest IStorage to create the CompObj Stream
9995 * strOleTypeName [I] The ProgID
9999 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10002 * This function is used by OleConvertOLESTREAMToIStorage only.
10004 * The stream data is stored in the OLESTREAM and there should be
10005 * no need to recreate the stream. If the stream is manually
10006 * deleted it will attempt to create it by querying the registry.
10010 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
10013 HRESULT hStorageRes
, hRes
= S_OK
;
10014 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
10015 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10016 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
10018 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10019 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
10021 /* Initialize the CompObj structure */
10022 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
10023 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
10024 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
10027 /* Create a CompObj stream if it doesn't exist */
10028 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10029 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10030 if(hStorageRes
== S_OK
)
10032 /* copy the OleTypeName to the compobj struct */
10033 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
10034 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
10036 /* copy the OleTypeName to the compobj struct */
10037 /* Note: in the test made, these were Identical */
10038 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
10039 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
10041 /* Get the CLSID */
10042 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
10043 bufferW
, OLESTREAM_MAX_STR_LEN
);
10044 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
10050 /* Get the CLSID Default Name from the Registry */
10051 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
10052 if(hErr
== ERROR_SUCCESS
)
10054 char strTemp
[OLESTREAM_MAX_STR_LEN
];
10055 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
10056 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
10057 if(hErr
== ERROR_SUCCESS
)
10059 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
10065 /* Write CompObj Structure to stream */
10066 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
10068 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
10070 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
10071 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
10073 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
10075 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
10076 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
10078 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
10080 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
10081 if(IStorageCompObj
.dwProgIDNameLength
> 0)
10083 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
10085 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
10086 IStream_Release(pStream
);
10092 /*************************************************************************
10093 * OLECONVERT_CreateOlePresStream[Internal]
10095 * Creates the "\002OlePres000" Stream with the Metafile data
10098 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10099 * dwExtentX [I] Width of the Metafile
10100 * dwExtentY [I] Height of the Metafile
10101 * pData [I] Metafile data
10102 * dwDataLength [I] Size of the Metafile data
10106 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10109 * This function is used by OleConvertOLESTREAMToIStorage only.
10112 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10116 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10117 BYTE pOlePresStreamHeader
[] =
10119 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10120 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10121 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10122 0x00, 0x00, 0x00, 0x00
10125 BYTE pOlePresStreamHeaderEmpty
[] =
10127 0x00, 0x00, 0x00, 0x00,
10128 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10129 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10130 0x00, 0x00, 0x00, 0x00
10133 /* Create the OlePres000 Stream */
10134 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10135 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10140 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10142 memset(&OlePres
, 0, sizeof(OlePres
));
10143 /* Do we have any metafile data to save */
10144 if(dwDataLength
> 0)
10146 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10147 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10151 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10152 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10154 /* Set width and height of the metafile */
10155 OlePres
.dwExtentX
= dwExtentX
;
10156 OlePres
.dwExtentY
= -dwExtentY
;
10158 /* Set Data and Length */
10159 if(dwDataLength
> sizeof(METAFILEPICT16
))
10161 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10162 OlePres
.pData
= &(pData
[8]);
10164 /* Save OlePres000 Data to Stream */
10165 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10166 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10167 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10168 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10169 if(OlePres
.dwSize
> 0)
10171 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10173 IStream_Release(pStream
);
10177 /*************************************************************************
10178 * OLECONVERT_CreateOle10NativeStream [Internal]
10180 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10183 * pStorage [I] Dest storage to create the stream in
10184 * pData [I] Ole10 Native Data (ex. bmp)
10185 * dwDataLength [I] Size of the Ole10 Native Data
10191 * This function is used by OleConvertOLESTREAMToIStorage only.
10193 * Might need to verify the data and return appropriate error message
10196 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10200 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10202 /* Create the Ole10Native Stream */
10203 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10204 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10208 /* Write info to stream */
10209 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10210 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10211 IStream_Release(pStream
);
10216 /*************************************************************************
10217 * OLECONVERT_GetOLE10ProgID [Internal]
10219 * Finds the ProgID (or OleTypeID) from the IStorage
10222 * pStorage [I] The Src IStorage to get the ProgID
10223 * strProgID [I] the ProgID string to get
10224 * dwSize [I] the size of the string
10228 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10231 * This function is used by OleConvertIStorageToOLESTREAM only.
10235 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10239 LARGE_INTEGER iSeekPos
;
10240 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10241 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10243 /* Open the CompObj Stream */
10244 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10245 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10249 /*Get the OleType from the CompObj Stream */
10250 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10251 iSeekPos
.u
.HighPart
= 0;
10253 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10254 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10255 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10256 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10257 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10258 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10259 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10261 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10264 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10266 IStream_Release(pStream
);
10271 LPOLESTR wstrProgID
;
10273 /* Get the OleType from the registry */
10274 REFCLSID clsid
= &(stat
.clsid
);
10275 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10276 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10279 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10280 CoTaskMemFree(wstrProgID
);
10287 /*************************************************************************
10288 * OLECONVERT_GetOle10PresData [Internal]
10290 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10293 * pStorage [I] Src IStroage
10294 * pOleStream [I] Dest OleStream Mem Struct
10300 * This function is used by OleConvertIStorageToOLESTREAM only.
10302 * Memory allocated for pData must be freed by the caller
10306 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10311 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10313 /* Initialize Default data for OLESTREAM */
10314 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10315 pOleStreamData
[0].dwTypeID
= 2;
10316 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10317 pOleStreamData
[1].dwTypeID
= 0;
10318 pOleStreamData
[0].dwMetaFileWidth
= 0;
10319 pOleStreamData
[0].dwMetaFileHeight
= 0;
10320 pOleStreamData
[0].pData
= NULL
;
10321 pOleStreamData
[1].pData
= NULL
;
10323 /* Open Ole10Native Stream */
10324 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10325 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10329 /* Read Size and Data */
10330 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10331 if(pOleStreamData
->dwDataLength
> 0)
10333 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10334 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10336 IStream_Release(pStream
);
10342 /*************************************************************************
10343 * OLECONVERT_GetOle20PresData[Internal]
10345 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10348 * pStorage [I] Src IStroage
10349 * pOleStreamData [I] Dest OleStream Mem Struct
10355 * This function is used by OleConvertIStorageToOLESTREAM only.
10357 * Memory allocated for pData must be freed by the caller
10359 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10363 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10364 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10366 /* Initialize Default data for OLESTREAM */
10367 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10368 pOleStreamData
[0].dwTypeID
= 2;
10369 pOleStreamData
[0].dwMetaFileWidth
= 0;
10370 pOleStreamData
[0].dwMetaFileHeight
= 0;
10371 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10372 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10373 pOleStreamData
[1].dwTypeID
= 0;
10374 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10375 pOleStreamData
[1].strOleTypeName
[0] = 0;
10376 pOleStreamData
[1].dwMetaFileWidth
= 0;
10377 pOleStreamData
[1].dwMetaFileHeight
= 0;
10378 pOleStreamData
[1].pData
= NULL
;
10379 pOleStreamData
[1].dwDataLength
= 0;
10382 /* Open OlePress000 stream */
10383 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10384 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10387 LARGE_INTEGER iSeekPos
;
10388 METAFILEPICT16 MetaFilePict
;
10389 static const char strMetafilePictName
[] = "METAFILEPICT";
10391 /* Set the TypeID for a Metafile */
10392 pOleStreamData
[1].dwTypeID
= 5;
10394 /* Set the OleTypeName to Metafile */
10395 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10396 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10398 iSeekPos
.u
.HighPart
= 0;
10399 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10401 /* Get Presentation Data */
10402 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10403 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10404 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10405 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10407 /*Set width and Height */
10408 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10409 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10410 if(olePress
.dwSize
> 0)
10413 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10415 /* Set MetaFilePict struct */
10416 MetaFilePict
.mm
= 8;
10417 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10418 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10419 MetaFilePict
.hMF
= 0;
10421 /* Get Metafile Data */
10422 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10423 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10424 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10426 IStream_Release(pStream
);
10430 /*************************************************************************
10431 * OleConvertOLESTREAMToIStorage [OLE32.@]
10433 * Read info on MSDN
10436 * DVTARGETDEVICE parameter is not handled
10437 * Still unsure of some mem fields for OLE 10 Stream
10438 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10439 * and "\001OLE" streams
10442 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10443 LPOLESTREAM pOleStream
,
10445 const DVTARGETDEVICE
* ptd
)
10449 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10451 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10453 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10457 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10460 if(pstg
== NULL
|| pOleStream
== NULL
)
10462 hRes
= E_INVALIDARG
;
10467 /* Load the OLESTREAM to Memory */
10468 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10473 /* Load the OLESTREAM to Memory (part 2)*/
10474 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10480 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10482 /* Do we have the IStorage Data in the OLESTREAM */
10483 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10485 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10486 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10490 /* It must be an original OLE 1.0 source */
10491 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10496 /* It must be an original OLE 1.0 source */
10497 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10500 /* Create CompObj Stream if necessary */
10501 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10504 /*Create the Ole Stream if necessary */
10505 STORAGE_CreateOleStream(pstg
, 0);
10510 /* Free allocated memory */
10511 for(i
=0; i
< 2; i
++)
10513 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10514 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10515 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10520 /*************************************************************************
10521 * OleConvertIStorageToOLESTREAM [OLE32.@]
10523 * Read info on MSDN
10525 * Read info on MSDN
10528 * Still unsure of some mem fields for OLE 10 Stream
10529 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10530 * and "\001OLE" streams.
10533 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10535 LPOLESTREAM pOleStream
)
10538 HRESULT hRes
= S_OK
;
10540 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10541 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10543 TRACE("%p %p\n", pstg
, pOleStream
);
10545 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10547 if(pstg
== NULL
|| pOleStream
== NULL
)
10549 hRes
= E_INVALIDARG
;
10553 /* Get the ProgID */
10554 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10555 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10559 /* Was it originally Ole10 */
10560 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10563 IStream_Release(pStream
);
10564 /* Get Presentation Data for Ole10Native */
10565 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10569 /* Get Presentation Data (OLE20) */
10570 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10573 /* Save OLESTREAM */
10574 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10577 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10582 /* Free allocated memory */
10583 for(i
=0; i
< 2; i
++)
10585 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10591 enum stream_1ole_flags
{
10592 OleStream_LinkedObject
= 0x00000001,
10593 OleStream_Convert
= 0x00000004
10596 /***********************************************************************
10597 * GetConvertStg (OLE32.@)
10599 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10601 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10602 static const DWORD version_magic
= 0x02000001;
10607 TRACE("%p\n", stg
);
10609 if (!stg
) return E_INVALIDARG
;
10611 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10612 if (FAILED(hr
)) return hr
;
10614 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10615 IStream_Release(stream
);
10616 if (FAILED(hr
)) return hr
;
10618 if (header
[0] != version_magic
)
10620 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10624 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10627 /***********************************************************************
10628 * SetConvertStg (OLE32.@)
10630 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10632 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10633 DWORD flags
= convert
? OleStream_Convert
: 0;
10638 TRACE("(%p, %d)\n", storage
, convert
);
10640 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10643 if (hr
!= STG_E_FILENOTFOUND
)
10646 return STORAGE_CreateOleStream(storage
, flags
);
10649 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10652 IStream_Release(stream
);
10656 /* update flag if differs */
10657 if ((header
[1] ^ flags
) & OleStream_Convert
)
10659 LARGE_INTEGER pos
= {{0}};
10661 if (header
[1] & OleStream_Convert
)
10662 flags
= header
[1] & ~OleStream_Convert
;
10664 flags
= header
[1] | OleStream_Convert
;
10666 pos
.QuadPart
= sizeof(DWORD
);
10667 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10670 IStream_Release(stream
);
10674 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10677 IStream_Release(stream
);