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 to find
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
);
898 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
899 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
901 *ppvObject
= &This
->IEnumSTATSTG_iface
;
902 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
906 return E_NOINTERFACE
;
909 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
912 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
913 return InterlockedIncrement(&This
->ref
);
916 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
919 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
923 newRef
= InterlockedDecrement(&This
->ref
);
927 IEnumSTATSTGImpl_Destroy(This
);
933 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
934 IEnumSTATSTGImpl
* This
,
937 DirRef result
= DIRENTRY_NULL
;
941 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
943 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
944 This
->parentStorage
->storageDirEntry
, &entry
);
945 searchNode
= entry
.dirRootEntry
;
947 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
949 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
953 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
957 searchNode
= entry
.rightChild
;
962 memcpy(result_name
, entry
.name
, sizeof(result_name
));
963 searchNode
= entry
.leftChild
;
971 if (result
!= DIRENTRY_NULL
)
972 memcpy(This
->name
, result_name
, sizeof(result_name
));
978 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
984 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
986 DirEntry currentEntry
;
987 STATSTG
* currentReturnStruct
= rgelt
;
988 ULONG objectFetched
= 0;
989 DirRef currentSearchNode
;
992 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
995 if (This
->parentStorage
->reverted
)
996 return STG_E_REVERTED
;
999 * To avoid the special case, get another pointer to a ULONG value if
1000 * the caller didn't supply one.
1002 if (pceltFetched
==0)
1003 pceltFetched
= &objectFetched
;
1006 * Start the iteration, we will iterate until we hit the end of the
1007 * linked list or until we hit the number of items to iterate through
1011 while ( *pceltFetched
< celt
)
1013 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1015 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1019 * Read the entry from the storage.
1021 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
1026 * Copy the information to the return buffer.
1028 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
1029 currentReturnStruct
,
1034 * Step to the next item in the iteration
1037 currentReturnStruct
++;
1040 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
1047 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
1048 IEnumSTATSTG
* iface
,
1051 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1053 ULONG objectFetched
= 0;
1054 DirRef currentSearchNode
;
1057 if (This
->parentStorage
->reverted
)
1058 return STG_E_REVERTED
;
1060 while ( (objectFetched
< celt
) )
1062 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1064 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1070 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
1076 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
1077 IEnumSTATSTG
* iface
)
1079 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1081 if (This
->parentStorage
->reverted
)
1082 return STG_E_REVERTED
;
1089 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
*,DirRef
);
1091 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
1092 IEnumSTATSTG
* iface
,
1093 IEnumSTATSTG
** ppenum
)
1095 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1096 IEnumSTATSTGImpl
* newClone
;
1098 if (This
->parentStorage
->reverted
)
1099 return STG_E_REVERTED
;
1102 return E_INVALIDARG
;
1104 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
1105 This
->storageDirEntry
);
1109 return E_OUTOFMEMORY
;
1113 * The new clone enumeration must point to the same current node as
1116 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
1118 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
1124 * Virtual function table for the IEnumSTATSTGImpl class.
1126 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
1128 IEnumSTATSTGImpl_QueryInterface
,
1129 IEnumSTATSTGImpl_AddRef
,
1130 IEnumSTATSTGImpl_Release
,
1131 IEnumSTATSTGImpl_Next
,
1132 IEnumSTATSTGImpl_Skip
,
1133 IEnumSTATSTGImpl_Reset
,
1134 IEnumSTATSTGImpl_Clone
1137 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
1138 StorageBaseImpl
* parentStorage
,
1139 DirRef storageDirEntry
)
1141 IEnumSTATSTGImpl
* newEnumeration
;
1143 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
1147 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
1148 newEnumeration
->ref
= 1;
1149 newEnumeration
->name
[0] = 0;
1152 * We want to nail-down the reference to the storage in case the
1153 * enumeration out-lives the storage in the client application.
1155 newEnumeration
->parentStorage
= parentStorage
;
1156 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
1158 newEnumeration
->storageDirEntry
= storageDirEntry
;
1161 return newEnumeration
;
1165 /************************************************************************
1166 * StorageBaseImpl implementation
1167 ***********************************************************************/
1169 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
1171 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
1174 /************************************************************************
1175 * StorageBaseImpl_QueryInterface (IUnknown)
1177 * This method implements the common QueryInterface for all IStorage
1178 * implementations contained in this file.
1180 * See Windows documentation for more details on IUnknown methods.
1182 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
1187 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1190 return E_INVALIDARG
;
1194 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
1195 IsEqualGUID(&IID_IStorage
, riid
))
1197 *ppvObject
= &This
->IStorage_iface
;
1199 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
1201 *ppvObject
= &This
->IPropertySetStorage_iface
;
1203 /* locking interface is reported for writer only */
1204 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
1206 *ppvObject
= &This
->IDirectWriterLock_iface
;
1209 return E_NOINTERFACE
;
1211 IStorage_AddRef(iface
);
1216 /************************************************************************
1217 * StorageBaseImpl_AddRef (IUnknown)
1219 * This method implements the common AddRef for all IStorage
1220 * implementations contained in this file.
1222 * See Windows documentation for more details on IUnknown methods.
1224 static ULONG WINAPI
StorageBaseImpl_AddRef(
1227 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1228 ULONG ref
= InterlockedIncrement(&This
->ref
);
1230 TRACE("(%p) AddRef to %d\n", This
, ref
);
1235 /************************************************************************
1236 * StorageBaseImpl_Release (IUnknown)
1238 * This method implements the common Release for all IStorage
1239 * implementations contained in this file.
1241 * See Windows documentation for more details on IUnknown methods.
1243 static ULONG WINAPI
StorageBaseImpl_Release(
1246 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1248 ULONG ref
= InterlockedDecrement(&This
->ref
);
1250 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
1255 * Since we are using a system of base-classes, we want to call the
1256 * destructor of the appropriate derived class. To do this, we are
1257 * using virtual functions to implement the destructor.
1259 StorageBaseImpl_Destroy(This
);
1265 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1266 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1267 SNB snbExclude
, IStorage
*pstgDest
);
1269 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1270 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1271 SNB snbExclude
, IStorage
*pstgDest
)
1277 IStream
*pstrChild
, *pstrTmp
;
1280 if (srcEntry
== DIRENTRY_NULL
)
1283 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1290 WCHAR
**snb
= snbExclude
;
1292 while ( *snb
!= NULL
&& !skip
)
1294 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1302 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1305 * create a new storage in destination storage
1307 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1308 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1313 * if it already exist, don't create a new one use this one
1315 if (hr
== STG_E_FILEALREADYEXISTS
)
1317 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1318 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1319 NULL
, 0, &pstgTmp
);
1324 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1325 skip_stream
, NULL
, pstgTmp
);
1327 IStorage_Release(pstgTmp
);
1330 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1333 * create a new stream in destination storage. If the stream already
1334 * exist, it will be deleted and a new one will be created.
1336 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1337 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1341 * open child stream storage. This operation must succeed even if the
1342 * stream is already open, so we use internal functions to do it.
1346 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1350 pstrChild
= &streamimpl
->IStream_iface
;
1352 IStream_AddRef(pstrChild
);
1364 * Get the size of the source stream
1366 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1369 * Set the size of the destination stream.
1371 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1376 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1379 IStream_Release( pstrChild
);
1382 IStream_Release( pstrTmp
);
1388 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1389 skip_stream
, snbExclude
, pstgDest
);
1392 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1393 skip_stream
, snbExclude
, pstgDest
);
1398 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1400 StgStreamImpl
*strm
;
1402 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1404 if (strm
->dirEntry
== streamEntry
)
1413 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1415 StorageInternalImpl
*childstg
;
1417 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1419 if (childstg
->base
.storageDirEntry
== storageEntry
)
1428 /************************************************************************
1429 * StorageBaseImpl_OpenStream (IStorage)
1431 * This method will open the specified stream object from the current storage.
1433 * See Windows documentation for more details on IStorage methods.
1435 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
1437 const OLECHAR
* pwcsName
, /* [string][in] */
1438 void* reserved1
, /* [unique][in] */
1439 DWORD grfMode
, /* [in] */
1440 DWORD reserved2
, /* [in] */
1441 IStream
** ppstm
) /* [out] */
1443 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1444 StgStreamImpl
* newStream
;
1445 DirEntry currentEntry
;
1446 DirRef streamEntryRef
;
1447 HRESULT res
= STG_E_UNKNOWN
;
1449 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1450 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1452 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1460 if ( FAILED( validateSTGM(grfMode
) ) ||
1461 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1463 res
= STG_E_INVALIDFLAG
;
1470 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1472 res
= STG_E_INVALIDFUNCTION
;
1478 res
= STG_E_REVERTED
;
1483 * Check that we're compatible with the parent's storage mode, but
1484 * only if we are not in transacted mode
1486 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1487 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1489 res
= STG_E_INVALIDFLAG
;
1495 * Search for the element with the given name
1497 streamEntryRef
= findElement(
1499 This
->storageDirEntry
,
1504 * If it was found, construct the stream object and return a pointer to it.
1506 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1507 (currentEntry
.stgType
==STGTY_STREAM
) )
1509 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1511 /* A single stream cannot be opened a second time. */
1512 res
= STG_E_ACCESSDENIED
;
1516 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1520 newStream
->grfMode
= grfMode
;
1521 *ppstm
= &newStream
->IStream_iface
;
1523 IStream_AddRef(*ppstm
);
1529 res
= E_OUTOFMEMORY
;
1533 res
= STG_E_FILENOTFOUND
;
1537 TRACE("<-- IStream %p\n", *ppstm
);
1538 TRACE("<-- %08x\n", res
);
1542 /************************************************************************
1543 * StorageBaseImpl_OpenStorage (IStorage)
1545 * This method will open a new storage object from the current storage.
1547 * See Windows documentation for more details on IStorage methods.
1549 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1551 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1552 IStorage
* pstgPriority
, /* [unique][in] */
1553 DWORD grfMode
, /* [in] */
1554 SNB snbExclude
, /* [unique][in] */
1555 DWORD reserved
, /* [in] */
1556 IStorage
** ppstg
) /* [out] */
1558 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1559 StorageInternalImpl
* newStorage
;
1560 StorageBaseImpl
* newTransactedStorage
;
1561 DirEntry currentEntry
;
1562 DirRef storageEntryRef
;
1563 HRESULT res
= STG_E_UNKNOWN
;
1565 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1566 iface
, debugstr_w(pwcsName
), pstgPriority
,
1567 grfMode
, snbExclude
, reserved
, ppstg
);
1569 if ((pwcsName
==NULL
) || (ppstg
==0) )
1575 if (This
->openFlags
& STGM_SIMPLE
)
1577 res
= STG_E_INVALIDFUNCTION
;
1582 if (snbExclude
!= NULL
)
1584 res
= STG_E_INVALIDPARAMETER
;
1588 if ( FAILED( validateSTGM(grfMode
) ))
1590 res
= STG_E_INVALIDFLAG
;
1597 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1598 (grfMode
& STGM_DELETEONRELEASE
) ||
1599 (grfMode
& STGM_PRIORITY
) )
1601 res
= STG_E_INVALIDFUNCTION
;
1606 return STG_E_REVERTED
;
1609 * Check that we're compatible with the parent's storage mode,
1610 * but only if we are not transacted
1612 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1613 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1615 res
= STG_E_ACCESSDENIED
;
1622 storageEntryRef
= findElement(
1624 This
->storageDirEntry
,
1628 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1629 (currentEntry
.stgType
==STGTY_STORAGE
) )
1631 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1633 /* A single storage cannot be opened a second time. */
1634 res
= STG_E_ACCESSDENIED
;
1638 newStorage
= StorageInternalImpl_Construct(
1643 if (newStorage
!= 0)
1645 if (grfMode
& STGM_TRANSACTED
)
1647 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1651 HeapFree(GetProcessHeap(), 0, newStorage
);
1655 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1659 *ppstg
= &newStorage
->base
.IStorage_iface
;
1662 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1668 res
= STG_E_INSUFFICIENTMEMORY
;
1672 res
= STG_E_FILENOTFOUND
;
1675 TRACE("<-- %08x\n", res
);
1679 /************************************************************************
1680 * StorageBaseImpl_EnumElements (IStorage)
1682 * This method will create an enumerator object that can be used to
1683 * retrieve information about all the elements in the storage object.
1685 * See Windows documentation for more details on IStorage methods.
1687 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1689 DWORD reserved1
, /* [in] */
1690 void* reserved2
, /* [size_is][unique][in] */
1691 DWORD reserved3
, /* [in] */
1692 IEnumSTATSTG
** ppenum
) /* [out] */
1694 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1695 IEnumSTATSTGImpl
* newEnum
;
1697 TRACE("(%p, %d, %p, %d, %p)\n",
1698 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1701 return E_INVALIDARG
;
1704 return STG_E_REVERTED
;
1706 newEnum
= IEnumSTATSTGImpl_Construct(
1708 This
->storageDirEntry
);
1712 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1716 return E_OUTOFMEMORY
;
1719 /************************************************************************
1720 * StorageBaseImpl_Stat (IStorage)
1722 * This method will retrieve information about this storage object.
1724 * See Windows documentation for more details on IStorage methods.
1726 static HRESULT WINAPI
StorageBaseImpl_Stat(
1728 STATSTG
* pstatstg
, /* [out] */
1729 DWORD grfStatFlag
) /* [in] */
1731 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1732 DirEntry currentEntry
;
1733 HRESULT res
= STG_E_UNKNOWN
;
1735 TRACE("(%p, %p, %x)\n",
1736 iface
, pstatstg
, grfStatFlag
);
1746 res
= STG_E_REVERTED
;
1750 res
= StorageBaseImpl_ReadDirEntry(
1752 This
->storageDirEntry
,
1757 StorageUtl_CopyDirEntryToSTATSTG(
1763 pstatstg
->grfMode
= This
->openFlags
;
1764 pstatstg
->grfStateBits
= This
->stateBits
;
1770 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
);
1772 TRACE("<-- %08x\n", res
);
1776 /************************************************************************
1777 * StorageBaseImpl_RenameElement (IStorage)
1779 * This method will rename the specified element.
1781 * See Windows documentation for more details on IStorage methods.
1783 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1785 const OLECHAR
* pwcsOldName
, /* [in] */
1786 const OLECHAR
* pwcsNewName
) /* [in] */
1788 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1789 DirEntry currentEntry
;
1790 DirRef currentEntryRef
;
1792 TRACE("(%p, %s, %s)\n",
1793 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1796 return STG_E_REVERTED
;
1798 currentEntryRef
= findElement(This
,
1799 This
->storageDirEntry
,
1803 if (currentEntryRef
!= DIRENTRY_NULL
)
1806 * There is already an element with the new name
1808 return STG_E_FILEALREADYEXISTS
;
1812 * Search for the old element name
1814 currentEntryRef
= findElement(This
,
1815 This
->storageDirEntry
,
1819 if (currentEntryRef
!= DIRENTRY_NULL
)
1821 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1822 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1824 WARN("Element is already open; cannot rename.\n");
1825 return STG_E_ACCESSDENIED
;
1828 /* Remove the element from its current position in the tree */
1829 removeFromTree(This
, This
->storageDirEntry
,
1832 /* Change the name of the element */
1833 strcpyW(currentEntry
.name
, pwcsNewName
);
1835 /* Delete any sibling links */
1836 currentEntry
.leftChild
= DIRENTRY_NULL
;
1837 currentEntry
.rightChild
= DIRENTRY_NULL
;
1839 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1842 /* Insert the element in a new position in the tree */
1843 insertIntoTree(This
, This
->storageDirEntry
,
1849 * There is no element with the old name
1851 return STG_E_FILENOTFOUND
;
1854 return StorageBaseImpl_Flush(This
);
1857 /************************************************************************
1858 * StorageBaseImpl_CreateStream (IStorage)
1860 * This method will create a stream object within this storage
1862 * See Windows documentation for more details on IStorage methods.
1864 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1866 const OLECHAR
* pwcsName
, /* [string][in] */
1867 DWORD grfMode
, /* [in] */
1868 DWORD reserved1
, /* [in] */
1869 DWORD reserved2
, /* [in] */
1870 IStream
** ppstm
) /* [out] */
1872 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1873 StgStreamImpl
* newStream
;
1874 DirEntry currentEntry
, newStreamEntry
;
1875 DirRef currentEntryRef
, newStreamEntryRef
;
1878 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1879 iface
, debugstr_w(pwcsName
), grfMode
,
1880 reserved1
, reserved2
, ppstm
);
1883 return STG_E_INVALIDPOINTER
;
1886 return STG_E_INVALIDNAME
;
1888 if (reserved1
|| reserved2
)
1889 return STG_E_INVALIDPARAMETER
;
1891 if ( FAILED( validateSTGM(grfMode
) ))
1892 return STG_E_INVALIDFLAG
;
1894 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1895 return STG_E_INVALIDFLAG
;
1898 return STG_E_REVERTED
;
1903 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1904 (grfMode
& STGM_TRANSACTED
))
1905 return STG_E_INVALIDFUNCTION
;
1908 * Don't worry about permissions in transacted mode, as we can always write
1909 * changes; we just can't always commit them.
1911 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1912 /* Can't create a stream on read-only storage */
1913 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1914 return STG_E_ACCESSDENIED
;
1916 /* Can't create a stream with greater access than the parent. */
1917 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1918 return STG_E_ACCESSDENIED
;
1921 if(This
->openFlags
& STGM_SIMPLE
)
1922 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1926 currentEntryRef
= findElement(This
,
1927 This
->storageDirEntry
,
1931 if (currentEntryRef
!= DIRENTRY_NULL
)
1934 * An element with this name already exists
1936 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1938 IStorage_DestroyElement(iface
, pwcsName
);
1941 return STG_E_FILEALREADYEXISTS
;
1945 * memset the empty entry
1947 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
1949 newStreamEntry
.sizeOfNameString
=
1950 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1952 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1953 return STG_E_INVALIDNAME
;
1955 strcpyW(newStreamEntry
.name
, pwcsName
);
1957 newStreamEntry
.stgType
= STGTY_STREAM
;
1958 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1959 newStreamEntry
.size
.u
.LowPart
= 0;
1960 newStreamEntry
.size
.u
.HighPart
= 0;
1962 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
1963 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
1964 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
1966 /* call CoFileTime to get the current time
1967 newStreamEntry.ctime
1968 newStreamEntry.mtime
1971 /* newStreamEntry.clsid */
1974 * Create an entry with the new data
1976 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
1981 * Insert the new entry in the parent storage's tree.
1983 hr
= insertIntoTree(
1985 This
->storageDirEntry
,
1989 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1994 * Open the stream to return it.
1996 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2000 *ppstm
= &newStream
->IStream_iface
;
2001 IStream_AddRef(*ppstm
);
2005 return STG_E_INSUFFICIENTMEMORY
;
2008 return StorageBaseImpl_Flush(This
);
2011 /************************************************************************
2012 * StorageBaseImpl_SetClass (IStorage)
2014 * This method will write the specified CLSID in the directory entry of this
2017 * See Windows documentation for more details on IStorage methods.
2019 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2021 REFCLSID clsid
) /* [in] */
2023 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2025 DirEntry currentEntry
;
2027 TRACE("(%p, %p)\n", iface
, clsid
);
2030 return STG_E_REVERTED
;
2032 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2033 This
->storageDirEntry
,
2035 if (SUCCEEDED(hRes
))
2037 currentEntry
.clsid
= *clsid
;
2039 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2040 This
->storageDirEntry
,
2044 if (SUCCEEDED(hRes
))
2045 hRes
= StorageBaseImpl_Flush(This
);
2050 /************************************************************************
2051 * StorageBaseImpl_CreateStorage (IStorage)
2053 * This method will create the storage object within the provided storage.
2055 * See Windows documentation for more details on IStorage methods.
2057 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2059 const OLECHAR
*pwcsName
, /* [string][in] */
2060 DWORD grfMode
, /* [in] */
2061 DWORD reserved1
, /* [in] */
2062 DWORD reserved2
, /* [in] */
2063 IStorage
**ppstg
) /* [out] */
2065 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2067 DirEntry currentEntry
;
2069 DirRef currentEntryRef
;
2073 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2074 iface
, debugstr_w(pwcsName
), grfMode
,
2075 reserved1
, reserved2
, ppstg
);
2078 return STG_E_INVALIDPOINTER
;
2080 if (This
->openFlags
& STGM_SIMPLE
)
2082 return STG_E_INVALIDFUNCTION
;
2086 return STG_E_INVALIDNAME
;
2090 if ( FAILED( validateSTGM(grfMode
) ) ||
2091 (grfMode
& STGM_DELETEONRELEASE
) )
2093 WARN("bad grfMode: 0x%x\n", grfMode
);
2094 return STG_E_INVALIDFLAG
;
2098 return STG_E_REVERTED
;
2101 * Check that we're compatible with the parent's storage mode
2103 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2104 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2106 WARN("access denied\n");
2107 return STG_E_ACCESSDENIED
;
2110 currentEntryRef
= findElement(This
,
2111 This
->storageDirEntry
,
2115 if (currentEntryRef
!= DIRENTRY_NULL
)
2118 * An element with this name already exists
2120 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2121 ((This
->openFlags
& STGM_TRANSACTED
) ||
2122 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2124 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2130 WARN("file already exists\n");
2131 return STG_E_FILEALREADYEXISTS
;
2134 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2135 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2137 WARN("read-only storage\n");
2138 return STG_E_ACCESSDENIED
;
2141 memset(&newEntry
, 0, sizeof(DirEntry
));
2143 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2145 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2147 FIXME("name too long\n");
2148 return STG_E_INVALIDNAME
;
2151 strcpyW(newEntry
.name
, pwcsName
);
2153 newEntry
.stgType
= STGTY_STORAGE
;
2154 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2155 newEntry
.size
.u
.LowPart
= 0;
2156 newEntry
.size
.u
.HighPart
= 0;
2158 newEntry
.leftChild
= DIRENTRY_NULL
;
2159 newEntry
.rightChild
= DIRENTRY_NULL
;
2160 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2162 /* call CoFileTime to get the current time
2167 /* newEntry.clsid */
2170 * Create a new directory entry for the storage
2172 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2177 * Insert the new directory entry into the parent storage's tree
2179 hr
= insertIntoTree(
2181 This
->storageDirEntry
,
2185 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2190 * Open it to get a pointer to return.
2192 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2194 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2200 hr
= StorageBaseImpl_Flush(This
);
2205 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2206 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2207 SNB snbExclude
, IStorage
*pstgDest
)
2212 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2215 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2218 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2219 skip_stream
, snbExclude
, pstgDest
);
2224 /*************************************************************************
2227 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2229 DWORD ciidExclude
, /* [in] */
2230 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2231 SNB snbExclude
, /* [unique][in] */
2232 IStorage
* pstgDest
) /* [unique][in] */
2234 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2236 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2239 TRACE("(%p, %d, %p, %p, %p)\n",
2240 iface
, ciidExclude
, rgiidExclude
,
2241 snbExclude
, pstgDest
);
2243 if ( pstgDest
== 0 )
2244 return STG_E_INVALIDPOINTER
;
2246 for(i
= 0; i
< ciidExclude
; ++i
)
2248 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2249 skip_storage
= TRUE
;
2250 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2253 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2258 /* Give up early if it looks like this would be infinitely recursive.
2259 * Oddly enough, this includes some cases that aren't really recursive, like
2260 * copying to a transacted child. */
2261 IStorage
*pstgDestAncestor
= pstgDest
;
2262 IStorage
*pstgDestAncestorChild
= NULL
;
2264 /* Go up the chain from the destination until we find the source storage. */
2265 while (pstgDestAncestor
!= iface
) {
2266 pstgDestAncestorChild
= pstgDest
;
2268 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2270 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2272 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2274 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2276 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2278 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2284 if (pstgDestAncestor
== iface
)
2288 if (pstgDestAncestorChild
&& snbExclude
)
2290 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2292 WCHAR
**snb
= snbExclude
;
2294 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2296 while ( *snb
!= NULL
&& fail
)
2298 if ( lstrcmpW(data
.name
, *snb
) == 0 )
2305 return STG_E_ACCESSDENIED
;
2309 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2310 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2313 /*************************************************************************
2314 * MoveElementTo (IStorage)
2316 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2318 const OLECHAR
*pwcsName
, /* [string][in] */
2319 IStorage
*pstgDest
, /* [unique][in] */
2320 const OLECHAR
*pwcsNewName
,/* [string][in] */
2321 DWORD grfFlags
) /* [in] */
2323 FIXME("(%p %s %p %s %u): stub\n", iface
,
2324 debugstr_w(pwcsName
), pstgDest
,
2325 debugstr_w(pwcsNewName
), grfFlags
);
2329 /*************************************************************************
2332 * Ensures that any changes made to a storage object open in transacted mode
2333 * are reflected in the parent storage
2335 * In a non-transacted mode, this ensures all cached writes are completed.
2337 static HRESULT WINAPI
StorageBaseImpl_Commit(
2339 DWORD grfCommitFlags
)/* [in] */
2341 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2342 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
2343 return StorageBaseImpl_Flush(This
);
2346 /*************************************************************************
2349 * Discard all changes that have been made since the last commit operation
2351 static HRESULT WINAPI
StorageBaseImpl_Revert(
2354 TRACE("(%p)\n", iface
);
2358 /*********************************************************************
2360 * Internal helper function for StorageBaseImpl_DestroyElement()
2362 * Delete the contents of a storage entry.
2365 static HRESULT
deleteStorageContents(
2366 StorageBaseImpl
*parentStorage
,
2367 DirRef indexToDelete
,
2368 DirEntry entryDataToDelete
)
2370 IEnumSTATSTG
*elements
= 0;
2371 IStorage
*childStorage
= 0;
2372 STATSTG currentElement
;
2374 HRESULT destroyHr
= S_OK
;
2375 StorageInternalImpl
*stg
, *stg2
;
2377 /* Invalidate any open storage objects. */
2378 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2380 if (stg
->base
.storageDirEntry
== indexToDelete
)
2382 StorageBaseImpl_Invalidate(&stg
->base
);
2387 * Open the storage and enumerate it
2389 hr
= IStorage_OpenStorage(
2390 &parentStorage
->IStorage_iface
,
2391 entryDataToDelete
.name
,
2393 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2404 * Enumerate the elements
2406 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2409 IStorage_Release(childStorage
);
2416 * Obtain the next element
2418 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2421 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2423 CoTaskMemFree(currentElement
.pwcsName
);
2427 * We need to Reset the enumeration every time because we delete elements
2428 * and the enumeration could be invalid
2430 IEnumSTATSTG_Reset(elements
);
2432 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2434 IStorage_Release(childStorage
);
2435 IEnumSTATSTG_Release(elements
);
2440 /*********************************************************************
2442 * Internal helper function for StorageBaseImpl_DestroyElement()
2444 * Perform the deletion of a stream's data
2447 static HRESULT
deleteStreamContents(
2448 StorageBaseImpl
*parentStorage
,
2449 DirRef indexToDelete
,
2450 DirEntry entryDataToDelete
)
2454 ULARGE_INTEGER size
;
2455 StgStreamImpl
*strm
, *strm2
;
2457 /* Invalidate any open stream objects. */
2458 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2460 if (strm
->dirEntry
== indexToDelete
)
2462 TRACE("Stream deleted %p\n", strm
);
2463 strm
->parentStorage
= NULL
;
2464 list_remove(&strm
->StrmListEntry
);
2468 size
.u
.HighPart
= 0;
2471 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2472 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2482 hr
= IStream_SetSize(pis
, size
);
2490 * Release the stream object.
2492 IStream_Release(pis
);
2497 /*************************************************************************
2498 * DestroyElement (IStorage)
2500 * Strategy: This implementation is built this way for simplicity not for speed.
2501 * I always delete the topmost element of the enumeration and adjust
2502 * the deleted element pointer all the time. This takes longer to
2503 * do but allow to reinvoke DestroyElement whenever we encounter a
2504 * storage object. The optimisation resides in the usage of another
2505 * enumeration strategy that would give all the leaves of a storage
2506 * first. (postfix order)
2508 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2510 const OLECHAR
*pwcsName
)/* [string][in] */
2512 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2515 DirEntry entryToDelete
;
2516 DirRef entryToDeleteRef
;
2519 iface
, debugstr_w(pwcsName
));
2522 return STG_E_INVALIDPOINTER
;
2525 return STG_E_REVERTED
;
2527 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2528 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2529 return STG_E_ACCESSDENIED
;
2531 entryToDeleteRef
= findElement(
2533 This
->storageDirEntry
,
2537 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2539 return STG_E_FILENOTFOUND
;
2542 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2544 hr
= deleteStorageContents(
2549 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2551 hr
= deleteStreamContents(
2561 * Remove the entry from its parent storage
2563 hr
= removeFromTree(
2565 This
->storageDirEntry
,
2569 * Invalidate the entry
2572 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2575 hr
= StorageBaseImpl_Flush(This
);
2580 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2582 struct list
*cur
, *cur2
;
2583 StgStreamImpl
*strm
=NULL
;
2584 StorageInternalImpl
*childstg
=NULL
;
2586 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2587 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2588 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2589 strm
->parentStorage
= NULL
;
2593 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2594 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2595 StorageBaseImpl_Invalidate( &childstg
->base
);
2598 if (stg
->transactedChild
)
2600 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2602 stg
->transactedChild
= NULL
;
2606 /******************************************************************************
2607 * SetElementTimes (IStorage)
2609 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2611 const OLECHAR
*pwcsName
,/* [string][in] */
2612 const FILETIME
*pctime
, /* [in] */
2613 const FILETIME
*patime
, /* [in] */
2614 const FILETIME
*pmtime
) /* [in] */
2616 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2620 /******************************************************************************
2621 * SetStateBits (IStorage)
2623 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2625 DWORD grfStateBits
,/* [in] */
2626 DWORD grfMask
) /* [in] */
2628 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2631 return STG_E_REVERTED
;
2633 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2637 /******************************************************************************
2638 * Internal stream list handlers
2641 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2643 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2644 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2647 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2649 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2650 list_remove(&(strm
->StrmListEntry
));
2653 static HRESULT
StorageBaseImpl_CopyStream(
2654 StorageBaseImpl
*dst
, DirRef dst_entry
,
2655 StorageBaseImpl
*src
, DirRef src_entry
)
2660 ULARGE_INTEGER bytes_copied
;
2661 ULONG bytestocopy
, bytesread
, byteswritten
;
2663 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2667 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2669 bytes_copied
.QuadPart
= 0;
2670 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2672 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2674 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2676 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2679 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2680 data
, &byteswritten
);
2683 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2684 bytes_copied
.QuadPart
+= byteswritten
;
2692 static HRESULT
StorageBaseImpl_DupStorageTree(
2693 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2694 StorageBaseImpl
*src
, DirRef src_entry
)
2698 BOOL has_stream
=FALSE
;
2700 if (src_entry
== DIRENTRY_NULL
)
2702 *dst_entry
= DIRENTRY_NULL
;
2706 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2709 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2710 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2711 data
.size
.QuadPart
= 0;
2713 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2717 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2720 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2723 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2725 if (SUCCEEDED(hr
) && has_stream
)
2726 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2731 static HRESULT
StorageBaseImpl_CopyStorageTree(
2732 StorageBaseImpl
*dst
, DirRef dst_entry
,
2733 StorageBaseImpl
*src
, DirRef src_entry
)
2736 DirEntry src_data
, dst_data
;
2737 DirRef new_root_entry
;
2739 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2743 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2748 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2749 dst_data
.clsid
= src_data
.clsid
;
2750 dst_data
.ctime
= src_data
.ctime
;
2751 dst_data
.mtime
= src_data
.mtime
;
2752 dst_data
.dirRootEntry
= new_root_entry
;
2756 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2761 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2765 ULARGE_INTEGER zero
;
2767 if (entry
== DIRENTRY_NULL
)
2772 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2774 if (SUCCEEDED(hr
) && include_siblings
)
2775 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2777 if (SUCCEEDED(hr
) && include_siblings
)
2778 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2781 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2783 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2784 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2787 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2793 /************************************************************************
2794 * StorageImpl implementation
2795 ***********************************************************************/
2797 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2798 ULARGE_INTEGER offset
,
2803 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2806 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2807 ULARGE_INTEGER offset
,
2810 ULONG
* bytesWritten
)
2812 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2815 /******************************************************************************
2816 * StorageImpl_LoadFileHeader
2818 * This method will read in the file header
2820 static HRESULT
StorageImpl_LoadFileHeader(
2824 BYTE headerBigBlock
[HEADER_SIZE
];
2826 ULARGE_INTEGER offset
;
2831 * Get a pointer to the big block of data containing the header.
2833 offset
.u
.HighPart
= 0;
2834 offset
.u
.LowPart
= 0;
2835 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2836 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2837 hr
= STG_E_FILENOTFOUND
;
2840 * Extract the information from the header.
2845 * Check for the "magic number" signature and return an error if it is not
2848 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2850 return STG_E_OLDFORMAT
;
2853 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2855 return STG_E_INVALIDHEADER
;
2858 StorageUtl_ReadWord(
2860 OFFSET_BIGBLOCKSIZEBITS
,
2861 &This
->bigBlockSizeBits
);
2863 StorageUtl_ReadWord(
2865 OFFSET_SMALLBLOCKSIZEBITS
,
2866 &This
->smallBlockSizeBits
);
2868 StorageUtl_ReadDWord(
2870 OFFSET_BBDEPOTCOUNT
,
2871 &This
->bigBlockDepotCount
);
2873 StorageUtl_ReadDWord(
2875 OFFSET_ROOTSTARTBLOCK
,
2876 &This
->rootStartBlock
);
2878 StorageUtl_ReadDWord(
2880 OFFSET_TRANSACTIONSIG
,
2881 &This
->transactionSig
);
2883 StorageUtl_ReadDWord(
2885 OFFSET_SMALLBLOCKLIMIT
,
2886 &This
->smallBlockLimit
);
2888 StorageUtl_ReadDWord(
2890 OFFSET_SBDEPOTSTART
,
2891 &This
->smallBlockDepotStart
);
2893 StorageUtl_ReadDWord(
2895 OFFSET_EXTBBDEPOTSTART
,
2896 &This
->extBigBlockDepotStart
);
2898 StorageUtl_ReadDWord(
2900 OFFSET_EXTBBDEPOTCOUNT
,
2901 &This
->extBigBlockDepotCount
);
2903 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2905 StorageUtl_ReadDWord(
2907 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2908 &(This
->bigBlockDepotStart
[index
]));
2912 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2914 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2915 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2918 * Right now, the code is making some assumptions about the size of the
2919 * blocks, just make sure they are what we're expecting.
2921 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2922 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2923 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2925 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2926 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
2927 hr
= STG_E_INVALIDHEADER
;
2936 /******************************************************************************
2937 * StorageImpl_SaveFileHeader
2939 * This method will save to the file the header
2941 static void StorageImpl_SaveFileHeader(
2944 BYTE headerBigBlock
[HEADER_SIZE
];
2947 ULARGE_INTEGER offset
;
2948 DWORD bytes_read
, bytes_written
;
2949 DWORD major_version
, dirsectorcount
;
2952 * Get a pointer to the big block of data containing the header.
2954 offset
.u
.HighPart
= 0;
2955 offset
.u
.LowPart
= 0;
2956 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2957 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2958 hr
= STG_E_FILENOTFOUND
;
2960 if (This
->bigBlockSizeBits
== 0x9)
2962 else if (This
->bigBlockSizeBits
== 0xc)
2966 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
2971 * If the block read failed, the file is probably new.
2976 * Initialize for all unknown fields.
2978 memset(headerBigBlock
, 0, HEADER_SIZE
);
2981 * Initialize the magic number.
2983 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2987 * Write the information to the header.
2989 StorageUtl_WriteWord(
2991 OFFSET_MINORVERSION
,
2994 StorageUtl_WriteWord(
2996 OFFSET_MAJORVERSION
,
2999 StorageUtl_WriteWord(
3001 OFFSET_BYTEORDERMARKER
,
3004 StorageUtl_WriteWord(
3006 OFFSET_BIGBLOCKSIZEBITS
,
3007 This
->bigBlockSizeBits
);
3009 StorageUtl_WriteWord(
3011 OFFSET_SMALLBLOCKSIZEBITS
,
3012 This
->smallBlockSizeBits
);
3014 if (major_version
>= 4)
3016 if (This
->rootBlockChain
)
3017 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3019 /* This file is being created, and it will start out with one block. */
3023 /* This field must be 0 in versions older than 4 */
3026 StorageUtl_WriteDWord(
3028 OFFSET_DIRSECTORCOUNT
,
3031 StorageUtl_WriteDWord(
3033 OFFSET_BBDEPOTCOUNT
,
3034 This
->bigBlockDepotCount
);
3036 StorageUtl_WriteDWord(
3038 OFFSET_ROOTSTARTBLOCK
,
3039 This
->rootStartBlock
);
3041 StorageUtl_WriteDWord(
3043 OFFSET_TRANSACTIONSIG
,
3044 This
->transactionSig
);
3046 StorageUtl_WriteDWord(
3048 OFFSET_SMALLBLOCKLIMIT
,
3049 This
->smallBlockLimit
);
3051 StorageUtl_WriteDWord(
3053 OFFSET_SBDEPOTSTART
,
3054 This
->smallBlockDepotStart
);
3056 StorageUtl_WriteDWord(
3058 OFFSET_SBDEPOTCOUNT
,
3059 This
->smallBlockDepotChain
?
3060 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3062 StorageUtl_WriteDWord(
3064 OFFSET_EXTBBDEPOTSTART
,
3065 This
->extBigBlockDepotStart
);
3067 StorageUtl_WriteDWord(
3069 OFFSET_EXTBBDEPOTCOUNT
,
3070 This
->extBigBlockDepotCount
);
3072 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3074 StorageUtl_WriteDWord(
3076 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3077 (This
->bigBlockDepotStart
[index
]));
3081 * Write the big block back to the file.
3083 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3087 /************************************************************************
3088 * StorageImpl implementation : DirEntry methods
3089 ***********************************************************************/
3091 /******************************************************************************
3092 * StorageImpl_ReadRawDirEntry
3094 * This method will read the raw data from a directory entry in the file.
3096 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3098 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3100 ULARGE_INTEGER offset
;
3104 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3106 hr
= BlockChainStream_ReadAt(
3107 This
->rootBlockChain
,
3113 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3114 return STG_E_READFAULT
;
3119 /******************************************************************************
3120 * StorageImpl_WriteRawDirEntry
3122 * This method will write the raw data from a directory entry in the file.
3124 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3126 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3128 ULARGE_INTEGER offset
;
3131 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3133 return BlockChainStream_WriteAt(
3134 This
->rootBlockChain
,
3141 /***************************************************************************
3145 * Mark a directory entry in the file as free.
3147 static HRESULT
StorageImpl_DestroyDirEntry(
3148 StorageBaseImpl
*base
,
3151 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3152 StorageImpl
*storage
= (StorageImpl
*)base
;
3154 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3156 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3159 /******************************************************************************
3162 * Update raw directory entry data from the fields in newData.
3164 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3166 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3168 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3171 buffer
+ OFFSET_PS_NAME
,
3173 DIRENTRY_NAME_BUFFER_LEN
);
3175 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3177 StorageUtl_WriteWord(
3179 OFFSET_PS_NAMELENGTH
,
3180 newData
->sizeOfNameString
);
3182 StorageUtl_WriteDWord(
3184 OFFSET_PS_LEFTCHILD
,
3185 newData
->leftChild
);
3187 StorageUtl_WriteDWord(
3189 OFFSET_PS_RIGHTCHILD
,
3190 newData
->rightChild
);
3192 StorageUtl_WriteDWord(
3195 newData
->dirRootEntry
);
3197 StorageUtl_WriteGUID(
3202 StorageUtl_WriteDWord(
3205 newData
->ctime
.dwLowDateTime
);
3207 StorageUtl_WriteDWord(
3209 OFFSET_PS_CTIMEHIGH
,
3210 newData
->ctime
.dwHighDateTime
);
3212 StorageUtl_WriteDWord(
3215 newData
->mtime
.dwLowDateTime
);
3217 StorageUtl_WriteDWord(
3219 OFFSET_PS_MTIMEHIGH
,
3220 newData
->ctime
.dwHighDateTime
);
3222 StorageUtl_WriteDWord(
3224 OFFSET_PS_STARTBLOCK
,
3225 newData
->startingBlock
);
3227 StorageUtl_WriteDWord(
3230 newData
->size
.u
.LowPart
);
3232 StorageUtl_WriteDWord(
3234 OFFSET_PS_SIZE_HIGH
,
3235 newData
->size
.u
.HighPart
);
3238 /***************************************************************************
3242 * Reserve a directory entry in the file and initialize it.
3244 static HRESULT
StorageImpl_CreateDirEntry(
3245 StorageBaseImpl
*base
,
3246 const DirEntry
*newData
,
3249 StorageImpl
*storage
= (StorageImpl
*)base
;
3250 ULONG currentEntryIndex
= 0;
3251 ULONG newEntryIndex
= DIRENTRY_NULL
;
3253 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3254 WORD sizeOfNameString
;
3258 hr
= StorageImpl_ReadRawDirEntry(storage
,
3264 StorageUtl_ReadWord(
3266 OFFSET_PS_NAMELENGTH
,
3269 if (sizeOfNameString
== 0)
3272 * The entry exists and is available, we found it.
3274 newEntryIndex
= currentEntryIndex
;
3280 * We exhausted the directory entries, we will create more space below
3282 newEntryIndex
= currentEntryIndex
;
3284 currentEntryIndex
++;
3286 } while (newEntryIndex
== DIRENTRY_NULL
);
3289 * grow the directory stream
3293 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3294 ULARGE_INTEGER newSize
;
3296 ULONG lastEntry
= 0;
3297 ULONG blockCount
= 0;
3300 * obtain the new count of blocks in the directory stream
3302 blockCount
= BlockChainStream_GetCount(
3303 storage
->rootBlockChain
)+1;
3306 * initialize the size used by the directory stream
3308 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3311 * add a block to the directory stream
3313 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3316 * memset the empty entry in order to initialize the unused newly
3319 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3324 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3327 entryIndex
= newEntryIndex
+ 1;
3328 entryIndex
< lastEntry
;
3331 StorageImpl_WriteRawDirEntry(
3337 StorageImpl_SaveFileHeader(storage
);
3340 UpdateRawDirEntry(currentData
, newData
);
3342 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3345 *index
= newEntryIndex
;
3350 /******************************************************************************
3351 * StorageImpl_ReadDirEntry
3353 * This method will read the specified directory entry.
3355 static HRESULT
StorageImpl_ReadDirEntry(
3360 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3363 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3365 if (SUCCEEDED(readRes
))
3367 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3370 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3371 DIRENTRY_NAME_BUFFER_LEN
);
3372 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3374 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3376 StorageUtl_ReadWord(
3378 OFFSET_PS_NAMELENGTH
,
3379 &buffer
->sizeOfNameString
);
3381 StorageUtl_ReadDWord(
3383 OFFSET_PS_LEFTCHILD
,
3384 &buffer
->leftChild
);
3386 StorageUtl_ReadDWord(
3388 OFFSET_PS_RIGHTCHILD
,
3389 &buffer
->rightChild
);
3391 StorageUtl_ReadDWord(
3394 &buffer
->dirRootEntry
);
3396 StorageUtl_ReadGUID(
3401 StorageUtl_ReadDWord(
3404 &buffer
->ctime
.dwLowDateTime
);
3406 StorageUtl_ReadDWord(
3408 OFFSET_PS_CTIMEHIGH
,
3409 &buffer
->ctime
.dwHighDateTime
);
3411 StorageUtl_ReadDWord(
3414 &buffer
->mtime
.dwLowDateTime
);
3416 StorageUtl_ReadDWord(
3418 OFFSET_PS_MTIMEHIGH
,
3419 &buffer
->mtime
.dwHighDateTime
);
3421 StorageUtl_ReadDWord(
3423 OFFSET_PS_STARTBLOCK
,
3424 &buffer
->startingBlock
);
3426 StorageUtl_ReadDWord(
3429 &buffer
->size
.u
.LowPart
);
3431 StorageUtl_ReadDWord(
3433 OFFSET_PS_SIZE_HIGH
,
3434 &buffer
->size
.u
.HighPart
);
3440 /*********************************************************************
3441 * Write the specified directory entry to the file
3443 static HRESULT
StorageImpl_WriteDirEntry(
3446 const DirEntry
* buffer
)
3448 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3450 UpdateRawDirEntry(currentEntry
, buffer
);
3452 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3456 /************************************************************************
3457 * StorageImpl implementation : Block methods
3458 ***********************************************************************/
3460 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3462 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3465 static HRESULT
StorageImpl_ReadBigBlock(
3471 ULARGE_INTEGER ulOffset
;
3475 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3477 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3479 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3481 /* File ends during this block; fill the rest with 0's. */
3482 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3485 if (out_read
) *out_read
= read
;
3490 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3496 ULARGE_INTEGER ulOffset
;
3500 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3501 ulOffset
.QuadPart
+= offset
;
3503 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3504 *value
= lendian32toh(tmp
);
3505 return (read
== sizeof(DWORD
));
3508 static BOOL
StorageImpl_WriteBigBlock(
3513 ULARGE_INTEGER ulOffset
;
3516 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3518 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3519 return (wrote
== This
->bigBlockSize
);
3522 static BOOL
StorageImpl_WriteDWordToBigBlock(
3528 ULARGE_INTEGER ulOffset
;
3531 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3532 ulOffset
.QuadPart
+= offset
;
3534 value
= htole32(value
);
3535 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3536 return (wrote
== sizeof(DWORD
));
3539 /******************************************************************************
3540 * Storage32Impl_SmallBlocksToBigBlocks
3542 * This method will convert a small block chain to a big block chain.
3543 * The small block chain will be destroyed.
3545 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3547 SmallBlockChainStream
** ppsbChain
)
3549 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3550 ULARGE_INTEGER size
, offset
;
3551 ULONG cbRead
, cbWritten
;
3552 ULARGE_INTEGER cbTotalRead
;
3553 DirRef streamEntryRef
;
3554 HRESULT resWrite
= S_OK
;
3556 DirEntry streamEntry
;
3558 BlockChainStream
*bbTempChain
= NULL
;
3559 BlockChainStream
*bigBlockChain
= NULL
;
3562 * Create a temporary big block chain that doesn't have
3563 * an associated directory entry. This temporary chain will be
3564 * used to copy data from small blocks to big blocks.
3566 bbTempChain
= BlockChainStream_Construct(This
,
3569 if(!bbTempChain
) return NULL
;
3571 * Grow the big block chain.
3573 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3574 BlockChainStream_SetSize(bbTempChain
, size
);
3577 * Copy the contents of the small block chain to the big block chain
3578 * by small block size increments.
3580 offset
.u
.LowPart
= 0;
3581 offset
.u
.HighPart
= 0;
3582 cbTotalRead
.QuadPart
= 0;
3584 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3587 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3589 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3592 if (FAILED(resRead
))
3597 cbTotalRead
.QuadPart
+= cbRead
;
3599 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3605 if (FAILED(resWrite
))
3608 offset
.u
.LowPart
+= cbRead
;
3612 resRead
= STG_E_READFAULT
;
3615 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3616 HeapFree(GetProcessHeap(),0,buffer
);
3618 size
.u
.HighPart
= 0;
3621 if (FAILED(resRead
) || FAILED(resWrite
))
3623 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3624 BlockChainStream_SetSize(bbTempChain
, size
);
3625 BlockChainStream_Destroy(bbTempChain
);
3630 * Destroy the small block chain.
3632 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3633 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3634 SmallBlockChainStream_Destroy(*ppsbChain
);
3638 * Change the directory entry. This chain is now a big block chain
3639 * and it doesn't reside in the small blocks chain anymore.
3641 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3643 streamEntry
.startingBlock
= bbHeadOfChain
;
3645 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3648 * Destroy the temporary entryless big block chain.
3649 * Create a new big block chain associated with this entry.
3651 BlockChainStream_Destroy(bbTempChain
);
3652 bigBlockChain
= BlockChainStream_Construct(This
,
3656 return bigBlockChain
;
3659 /******************************************************************************
3660 * Storage32Impl_BigBlocksToSmallBlocks
3662 * This method will convert a big block chain to a small block chain.
3663 * The big block chain will be destroyed on success.
3665 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3667 BlockChainStream
** ppbbChain
,
3668 ULARGE_INTEGER newSize
)
3670 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3671 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3672 DirRef streamEntryRef
;
3673 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3674 DirEntry streamEntry
;
3676 SmallBlockChainStream
* sbTempChain
;
3678 TRACE("%p %p\n", This
, ppbbChain
);
3680 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3686 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3687 size
= BlockChainStream_GetSize(*ppbbChain
);
3688 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3690 offset
.u
.HighPart
= 0;
3691 offset
.u
.LowPart
= 0;
3692 cbTotalRead
.QuadPart
= 0;
3693 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3694 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3696 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3697 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3705 cbTotalRead
.QuadPart
+= cbRead
;
3707 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3708 cbRead
, buffer
, &cbWritten
);
3710 if(FAILED(resWrite
))
3713 offset
.u
.LowPart
+= cbRead
;
3717 resRead
= STG_E_READFAULT
;
3721 HeapFree(GetProcessHeap(), 0, buffer
);
3723 size
.u
.HighPart
= 0;
3726 if(FAILED(resRead
) || FAILED(resWrite
))
3728 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3729 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3730 SmallBlockChainStream_Destroy(sbTempChain
);
3734 /* destroy the original big block chain */
3735 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3736 BlockChainStream_SetSize(*ppbbChain
, size
);
3737 BlockChainStream_Destroy(*ppbbChain
);
3740 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3741 streamEntry
.startingBlock
= sbHeadOfChain
;
3742 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3744 SmallBlockChainStream_Destroy(sbTempChain
);
3745 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3748 /******************************************************************************
3749 * Storage32Impl_AddBlockDepot
3751 * This will create a depot block, essentially it is a block initialized
3754 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3756 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3757 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3758 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3759 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3762 * Initialize blocks as free
3764 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3766 /* Reserve the range lock sector */
3767 if (depotIndex
== rangeLockDepot
)
3769 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3772 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3775 /******************************************************************************
3776 * Storage32Impl_GetExtDepotBlock
3778 * Returns the index of the block that corresponds to the specified depot
3779 * index. This method is only for depot indexes equal or greater than
3780 * COUNT_BBDEPOTINHEADER.
3782 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3784 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3785 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3786 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3787 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3788 ULONG blockIndex
= BLOCK_UNUSED
;
3789 ULONG extBlockIndex
;
3790 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3791 int index
, num_blocks
;
3793 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3795 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3796 return BLOCK_UNUSED
;
3798 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3800 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3802 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3804 num_blocks
= This
->bigBlockSize
/ 4;
3806 for (index
= 0; index
< num_blocks
; index
++)
3808 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3809 This
->extBlockDepotCached
[index
] = blockIndex
;
3812 This
->indexExtBlockDepotCached
= extBlockCount
;
3815 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3820 /******************************************************************************
3821 * Storage32Impl_SetExtDepotBlock
3823 * Associates the specified block index to the specified depot index.
3824 * This method is only for depot indexes equal or greater than
3825 * COUNT_BBDEPOTINHEADER.
3827 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3829 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3830 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3831 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3832 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3833 ULONG extBlockIndex
;
3835 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3837 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3839 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3841 if (extBlockIndex
!= BLOCK_UNUSED
)
3843 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3844 extBlockOffset
* sizeof(ULONG
),
3848 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3850 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3854 /******************************************************************************
3855 * Storage32Impl_AddExtBlockDepot
3857 * Creates an extended depot block.
3859 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3861 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3862 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3863 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3864 ULONG index
= BLOCK_UNUSED
;
3865 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3866 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3867 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3869 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3870 blocksPerDepotBlock
;
3872 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3875 * The first extended block.
3877 This
->extBigBlockDepotStart
= index
;
3882 * Find the last existing extended block.
3884 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3887 * Add the new extended block to the chain.
3889 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3894 * Initialize this block.
3896 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3897 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3899 /* Add the block to our cache. */
3900 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3902 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3903 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3905 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3906 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3908 This
->extBigBlockDepotLocations
= new_cache
;
3909 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3911 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3916 /************************************************************************
3917 * StorageImpl_GetNextBlockInChain
3919 * This method will retrieve the block index of the next big block in
3922 * Params: This - Pointer to the Storage object.
3923 * blockIndex - Index of the block to retrieve the chain
3925 * nextBlockIndex - receives the return value.
3927 * Returns: This method returns the index of the next block in the chain.
3928 * It will return the constants:
3929 * BLOCK_SPECIAL - If the block given was not part of a
3931 * BLOCK_END_OF_CHAIN - If the block given was the last in
3933 * BLOCK_UNUSED - If the block given was not past of a chain
3935 * BLOCK_EXTBBDEPOT - This block is part of the extended
3938 * See Windows documentation for more details on IStorage methods.
3940 static HRESULT
StorageImpl_GetNextBlockInChain(
3943 ULONG
* nextBlockIndex
)
3945 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3946 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3947 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3948 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3950 ULONG depotBlockIndexPos
;
3951 int index
, num_blocks
;
3953 *nextBlockIndex
= BLOCK_SPECIAL
;
3955 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3957 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3958 This
->bigBlockDepotCount
);
3959 return STG_E_READFAULT
;
3963 * Cache the currently accessed depot block.
3965 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3967 This
->indexBlockDepotCached
= depotBlockCount
;
3969 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3971 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3976 * We have to look in the extended depot.
3978 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3981 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3984 return STG_E_READFAULT
;
3986 num_blocks
= This
->bigBlockSize
/ 4;
3988 for (index
= 0; index
< num_blocks
; index
++)
3990 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3991 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3995 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4000 /******************************************************************************
4001 * Storage32Impl_GetNextExtendedBlock
4003 * Given an extended block this method will return the next extended block.
4006 * The last ULONG of an extended block is the block index of the next
4007 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4011 * - The index of the next extended block
4012 * - BLOCK_UNUSED: there is no next extended block.
4013 * - Any other return values denotes failure.
4015 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4017 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4018 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4020 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4023 return nextBlockIndex
;
4026 /******************************************************************************
4027 * StorageImpl_SetNextBlockInChain
4029 * This method will write the index of the specified block's next block
4030 * in the big block depot.
4032 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4035 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4036 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4037 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4040 static void StorageImpl_SetNextBlockInChain(
4045 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4046 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4047 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4048 ULONG depotBlockIndexPos
;
4050 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4051 assert(blockIndex
!= nextBlock
);
4053 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4054 /* This should never happen (storage file format spec forbids it), but
4055 * older versions of Wine may have generated broken files. We don't want to
4056 * assert and potentially lose data, but we do want to know if this ever
4057 * happens in a newly-created file. */
4058 ERR("Using range lock page\n");
4060 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4062 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4067 * We have to look in the extended depot.
4069 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4072 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4075 * Update the cached block depot, if necessary.
4077 if (depotBlockCount
== This
->indexBlockDepotCached
)
4079 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4083 /******************************************************************************
4084 * StorageImpl_GetNextFreeBigBlock
4086 * Returns the index of the next free big block.
4087 * If the big block depot is filled, this method will enlarge it.
4090 static ULONG
StorageImpl_GetNextFreeBigBlock(
4093 ULONG depotBlockIndexPos
;
4094 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4095 ULONG depotBlockOffset
;
4096 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4097 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4099 ULONG freeBlock
= BLOCK_UNUSED
;
4101 ULARGE_INTEGER neededSize
;
4104 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4105 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4108 * Scan the entire big block depot until we find a block marked free
4110 while (nextBlockIndex
!= BLOCK_UNUSED
)
4112 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4114 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4117 * Grow the primary depot.
4119 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4121 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4124 * Add a block depot.
4126 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4127 This
->bigBlockDepotCount
++;
4128 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4131 * Flag it as a block depot.
4133 StorageImpl_SetNextBlockInChain(This
,
4137 /* Save new header information.
4139 StorageImpl_SaveFileHeader(This
);
4144 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4146 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4149 * Grow the extended depot.
4151 ULONG extIndex
= BLOCK_UNUSED
;
4152 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4153 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4155 if (extBlockOffset
== 0)
4157 /* We need an extended block.
4159 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4160 This
->extBigBlockDepotCount
++;
4161 depotBlockIndexPos
= extIndex
+ 1;
4164 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4167 * Add a block depot and mark it in the extended block.
4169 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4170 This
->bigBlockDepotCount
++;
4171 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4173 /* Flag the block depot.
4175 StorageImpl_SetNextBlockInChain(This
,
4179 /* If necessary, flag the extended depot block.
4181 if (extIndex
!= BLOCK_UNUSED
)
4182 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4184 /* Save header information.
4186 StorageImpl_SaveFileHeader(This
);
4190 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4194 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4195 ( nextBlockIndex
!= BLOCK_UNUSED
))
4197 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4199 if (nextBlockIndex
== BLOCK_UNUSED
)
4201 freeBlock
= (depotIndex
* blocksPerDepot
) +
4202 (depotBlockOffset
/sizeof(ULONG
));
4205 depotBlockOffset
+= sizeof(ULONG
);
4210 depotBlockOffset
= 0;
4214 * make sure that the block physically exists before using it
4216 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
4218 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4220 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4221 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4223 This
->prevFreeBlock
= freeBlock
;
4228 /******************************************************************************
4229 * StorageImpl_FreeBigBlock
4231 * This method will flag the specified block as free in the big block depot.
4233 static void StorageImpl_FreeBigBlock(
4237 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4239 if (blockIndex
< This
->prevFreeBlock
)
4240 This
->prevFreeBlock
= blockIndex
;
4244 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4245 DirRef index
, const DirEntry
*data
)
4247 StorageImpl
*This
= (StorageImpl
*)base
;
4248 return StorageImpl_WriteDirEntry(This
, index
, data
);
4251 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4252 DirRef index
, DirEntry
*data
)
4254 StorageImpl
*This
= (StorageImpl
*)base
;
4255 return StorageImpl_ReadDirEntry(This
, index
, data
);
4258 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4262 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4264 if (!This
->blockChainCache
[i
])
4266 return &This
->blockChainCache
[i
];
4270 i
= This
->blockChainToEvict
;
4272 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4273 This
->blockChainCache
[i
] = NULL
;
4275 This
->blockChainToEvict
++;
4276 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4277 This
->blockChainToEvict
= 0;
4279 return &This
->blockChainCache
[i
];
4282 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4285 int i
, free_index
=-1;
4287 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4289 if (!This
->blockChainCache
[i
])
4291 if (free_index
== -1) free_index
= i
;
4293 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4295 return &This
->blockChainCache
[i
];
4299 if (free_index
== -1)
4301 free_index
= This
->blockChainToEvict
;
4303 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4304 This
->blockChainCache
[free_index
] = NULL
;
4306 This
->blockChainToEvict
++;
4307 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4308 This
->blockChainToEvict
= 0;
4311 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4312 return &This
->blockChainCache
[free_index
];
4315 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4319 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4321 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4323 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4324 This
->blockChainCache
[i
] = NULL
;
4330 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4331 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4333 StorageImpl
*This
= (StorageImpl
*)base
;
4338 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4339 if (FAILED(hr
)) return hr
;
4341 if (data
.size
.QuadPart
== 0)
4347 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4349 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4356 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4358 SmallBlockChainStream
*stream
;
4360 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4361 if (!stream
) return E_OUTOFMEMORY
;
4363 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4365 SmallBlockChainStream_Destroy(stream
);
4371 BlockChainStream
*stream
= NULL
;
4373 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4374 if (!stream
) return E_OUTOFMEMORY
;
4376 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4382 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4383 ULARGE_INTEGER newsize
)
4385 StorageImpl
*This
= (StorageImpl
*)base
;
4388 SmallBlockChainStream
*smallblock
=NULL
;
4389 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4391 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4392 if (FAILED(hr
)) return hr
;
4394 /* In simple mode keep the stream size above the small block limit */
4395 if (This
->base
.openFlags
& STGM_SIMPLE
)
4396 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4398 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4401 /* Create a block chain object of the appropriate type */
4402 if (data
.size
.QuadPart
== 0)
4404 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4406 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4407 if (!smallblock
) return E_OUTOFMEMORY
;
4411 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4412 bigblock
= *pbigblock
;
4413 if (!bigblock
) return E_OUTOFMEMORY
;
4416 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4418 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4419 if (!smallblock
) return E_OUTOFMEMORY
;
4423 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4424 bigblock
= *pbigblock
;
4425 if (!bigblock
) return E_OUTOFMEMORY
;
4428 /* Change the block chain type if necessary. */
4429 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4431 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4434 SmallBlockChainStream_Destroy(smallblock
);
4438 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4439 *pbigblock
= bigblock
;
4441 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4443 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4448 /* Set the size of the block chain. */
4451 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4452 SmallBlockChainStream_Destroy(smallblock
);
4456 BlockChainStream_SetSize(bigblock
, newsize
);
4459 /* Set the size in the directory entry. */
4460 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4463 data
.size
= newsize
;
4465 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4470 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4471 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4473 StorageImpl
*This
= (StorageImpl
*)base
;
4476 ULARGE_INTEGER newSize
;
4478 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4479 if (FAILED(hr
)) return hr
;
4481 /* Grow the stream if necessary */
4482 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4484 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4486 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4490 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4491 if (FAILED(hr
)) return hr
;
4494 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4496 SmallBlockChainStream
*stream
;
4498 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4499 if (!stream
) return E_OUTOFMEMORY
;
4501 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4503 SmallBlockChainStream_Destroy(stream
);
4509 BlockChainStream
*stream
;
4511 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4512 if (!stream
) return E_OUTOFMEMORY
;
4514 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4518 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4521 StorageImpl
*This
= (StorageImpl
*)base
;
4522 DirEntry dst_data
, src_data
;
4525 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4528 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4532 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4533 dst_data
.startingBlock
= src_data
.startingBlock
;
4534 dst_data
.size
= src_data
.size
;
4536 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4542 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4545 DirEntry currentEntry
;
4546 DirRef currentEntryRef
;
4547 BlockChainStream
*blockChainStream
;
4551 ULARGE_INTEGER size
;
4552 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4554 /* Discard any existing data. */
4556 ILockBytes_SetSize(This
->lockBytes
, size
);
4559 * Initialize all header variables:
4560 * - The big block depot consists of one block and it is at block 0
4561 * - The directory table starts at block 1
4562 * - There is no small block depot
4564 memset( This
->bigBlockDepotStart
,
4566 sizeof(This
->bigBlockDepotStart
));
4568 This
->bigBlockDepotCount
= 1;
4569 This
->bigBlockDepotStart
[0] = 0;
4570 This
->rootStartBlock
= 1;
4571 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4572 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4573 if (This
->bigBlockSize
== 4096)
4574 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4576 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4577 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4578 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4579 This
->extBigBlockDepotCount
= 0;
4581 StorageImpl_SaveFileHeader(This
);
4584 * Add one block for the big block depot and one block for the directory table
4586 size
.u
.HighPart
= 0;
4587 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4588 ILockBytes_SetSize(This
->lockBytes
, size
);
4591 * Initialize the big block depot
4593 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4594 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4595 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4596 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4601 * Load the header for the file.
4603 hr
= StorageImpl_LoadFileHeader(This
);
4612 * There is no block depot cached yet.
4614 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4615 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4618 * Start searching for free blocks with block 0.
4620 This
->prevFreeBlock
= 0;
4622 This
->firstFreeSmallBlock
= 0;
4624 /* Read the extended big block depot locations. */
4625 if (This
->extBigBlockDepotCount
!= 0)
4627 ULONG current_block
= This
->extBigBlockDepotStart
;
4628 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4631 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4632 if (!This
->extBigBlockDepotLocations
)
4634 return E_OUTOFMEMORY
;
4637 This
->extBigBlockDepotLocationsSize
= cache_size
;
4639 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4641 if (current_block
== BLOCK_END_OF_CHAIN
)
4643 WARN("File has too few extended big block depot blocks.\n");
4644 return STG_E_DOCFILECORRUPT
;
4646 This
->extBigBlockDepotLocations
[i
] = current_block
;
4647 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4652 This
->extBigBlockDepotLocations
= NULL
;
4653 This
->extBigBlockDepotLocationsSize
= 0;
4657 * Create the block chain abstractions.
4659 if(!(blockChainStream
=
4660 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4662 return STG_E_READFAULT
;
4665 BlockChainStream_Destroy(This
->rootBlockChain
);
4666 This
->rootBlockChain
= blockChainStream
;
4668 if(!(blockChainStream
=
4669 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4672 return STG_E_READFAULT
;
4675 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4676 This
->smallBlockDepotChain
= blockChainStream
;
4679 * Write the root storage entry (memory only)
4683 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4686 * Initialize the directory table
4688 memset(&rootEntry
, 0, sizeof(rootEntry
));
4689 strcpyW(rootEntry
.name
, rootentryW
);
4690 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
4691 rootEntry
.stgType
= STGTY_ROOT
;
4692 rootEntry
.leftChild
= DIRENTRY_NULL
;
4693 rootEntry
.rightChild
= DIRENTRY_NULL
;
4694 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4695 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4696 rootEntry
.size
.u
.HighPart
= 0;
4697 rootEntry
.size
.u
.LowPart
= 0;
4699 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4703 * Find the ID of the root storage.
4705 currentEntryRef
= 0;
4709 hr
= StorageImpl_ReadDirEntry(
4716 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4717 (currentEntry
.stgType
== STGTY_ROOT
) )
4719 This
->base
.storageDirEntry
= currentEntryRef
;
4725 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4729 return STG_E_READFAULT
;
4733 * Create the block chain abstraction for the small block root chain.
4735 if(!(blockChainStream
=
4736 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4738 return STG_E_READFAULT
;
4741 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4742 This
->smallBlockRootChain
= blockChainStream
;
4747 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4749 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4750 This
->blockChainCache
[i
] = NULL
;
4757 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4758 ULONG
* result
, BOOL refresh
)
4760 StorageImpl
*This
= (StorageImpl
*)base
;
4762 DWORD oldTransactionSig
= This
->transactionSig
;
4766 ULARGE_INTEGER offset
;
4770 offset
.u
.HighPart
= 0;
4771 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4772 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4776 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4778 if (oldTransactionSig
!= This
->transactionSig
)
4780 /* Someone else wrote to this, so toss all cached information. */
4781 TRACE("signature changed\n");
4783 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4787 This
->transactionSig
= oldTransactionSig
;
4791 *result
= This
->transactionSig
;
4796 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4799 StorageImpl
*This
= (StorageImpl
*)base
;
4801 This
->transactionSig
= value
;
4802 StorageImpl_SaveFileHeader(This
);
4807 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4808 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4810 if ((dwLockType
& This
->locks_supported
) == 0)
4812 if (supported
) *supported
= FALSE
;
4816 if (supported
) *supported
= TRUE
;
4817 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4820 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4821 ULARGE_INTEGER cb
, DWORD dwLockType
)
4823 if ((dwLockType
& This
->locks_supported
) == 0)
4826 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4829 /* Internal function */
4830 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4831 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4835 DWORD start_time
= GetTickCount();
4836 DWORD last_sanity_check
= start_time
;
4837 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4839 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4840 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4844 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4846 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4848 DWORD current_time
= GetTickCount();
4849 if (current_time
- start_time
>= 20000)
4854 if (current_time
- last_sanity_check
>= 500)
4856 /* Any storage implementation with the file open in a
4857 * shared mode should not lock these bytes for writing. However,
4858 * some programs (LibreOffice Writer) will keep ALL bytes locked
4859 * when opening in exclusive mode. We can use a read lock to
4860 * detect this case early, and not hang a full 20 seconds.
4862 * This can collide with another attempt to open the file in
4863 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4864 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4865 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4869 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4870 hr
= STG_E_ACCESSDENIED
;
4873 last_sanity_check
= current_time
;
4876 if (delay
< 150) delay
++;
4878 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4883 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4885 StorageImpl
*This
= (StorageImpl
*)base
;
4887 ULARGE_INTEGER offset
, cb
;
4891 /* Synchronous grab of second priority range, the commit lock, and the
4892 * lock-checking lock. */
4893 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4894 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4898 offset
.QuadPart
= RANGELOCK_COMMIT
;
4902 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4907 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4909 StorageImpl
*This
= (StorageImpl
*)base
;
4911 ULARGE_INTEGER offset
, cb
;
4915 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4916 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4920 offset
.QuadPart
= RANGELOCK_COMMIT
;
4924 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4929 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4931 StorageImpl
*This
= (StorageImpl
*) iface
;
4935 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
4937 *result
= statstg
.pwcsName
;
4942 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
4943 ULONG end
, HRESULT fail_hr
)
4946 ULARGE_INTEGER offset
, cb
;
4948 offset
.QuadPart
= start
;
4949 cb
.QuadPart
= 1 + end
- start
;
4951 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4952 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4960 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
4964 ULARGE_INTEGER offset
, cb
;
4968 for (i
=start
; i
<=end
; i
++)
4970 offset
.QuadPart
= i
;
4971 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4972 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
4978 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
4980 if (This
->locked_bytes
[j
] == 0)
4982 This
->locked_bytes
[j
] = i
;
4991 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
4994 ULARGE_INTEGER offset
;
4996 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
4999 if (openFlags
& STGM_NOSNAPSHOT
)
5001 /* STGM_NOSNAPSHOT implies deny write */
5002 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5003 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5006 /* Wrap all other locking inside a single lock so we can check ranges safely */
5007 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5009 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5011 /* If the ILockBytes doesn't support locking that's ok. */
5012 if (!supported
) return S_OK
;
5013 else if (FAILED(hr
)) return hr
;
5017 /* First check for any conflicting locks. */
5018 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5019 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5021 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5022 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5024 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5025 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5027 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5028 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5030 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5031 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5033 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5035 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5038 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5041 /* Then grab our locks. */
5042 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5044 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5046 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5049 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5050 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5052 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5053 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5055 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5056 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5058 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5059 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5061 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5062 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5064 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5066 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5071 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5073 StorageImpl
*This
= (StorageImpl
*)storage
;
5076 TRACE("(%p)\n", This
);
5078 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5081 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5084 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5086 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5087 if (This
->blockChainCache
[i
])
5088 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5091 hr
= ILockBytes_Flush(This
->lockBytes
);
5096 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5098 StorageImpl
*This
= (StorageImpl
*) iface
;
5100 StorageBaseImpl_DeleteAll(&This
->base
);
5102 This
->base
.reverted
= TRUE
;
5105 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5107 StorageImpl
*This
= (StorageImpl
*) iface
;
5109 TRACE("(%p)\n", This
);
5111 StorageImpl_Flush(iface
);
5113 StorageImpl_Invalidate(iface
);
5115 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5117 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5118 BlockChainStream_Destroy(This
->rootBlockChain
);
5119 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5121 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5122 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5124 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
5126 ULARGE_INTEGER offset
, cb
;
5128 if (This
->locked_bytes
[i
] != 0)
5130 offset
.QuadPart
= This
->locked_bytes
[i
];
5131 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5135 if (This
->lockBytes
)
5136 ILockBytes_Release(This
->lockBytes
);
5137 HeapFree(GetProcessHeap(), 0, This
);
5141 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5143 StorageImpl_Destroy
,
5144 StorageImpl_Invalidate
,
5146 StorageImpl_GetFilename
,
5147 StorageImpl_CreateDirEntry
,
5148 StorageImpl_BaseWriteDirEntry
,
5149 StorageImpl_BaseReadDirEntry
,
5150 StorageImpl_DestroyDirEntry
,
5151 StorageImpl_StreamReadAt
,
5152 StorageImpl_StreamWriteAt
,
5153 StorageImpl_StreamSetSize
,
5154 StorageImpl_StreamLink
,
5155 StorageImpl_GetTransactionSig
,
5156 StorageImpl_SetTransactionSig
,
5157 StorageImpl_LockTransaction
,
5158 StorageImpl_UnlockTransaction
5163 * Virtual function table for the IStorageBaseImpl class.
5165 static const IStorageVtbl StorageImpl_Vtbl
=
5167 StorageBaseImpl_QueryInterface
,
5168 StorageBaseImpl_AddRef
,
5169 StorageBaseImpl_Release
,
5170 StorageBaseImpl_CreateStream
,
5171 StorageBaseImpl_OpenStream
,
5172 StorageBaseImpl_CreateStorage
,
5173 StorageBaseImpl_OpenStorage
,
5174 StorageBaseImpl_CopyTo
,
5175 StorageBaseImpl_MoveElementTo
,
5176 StorageBaseImpl_Commit
,
5177 StorageBaseImpl_Revert
,
5178 StorageBaseImpl_EnumElements
,
5179 StorageBaseImpl_DestroyElement
,
5180 StorageBaseImpl_RenameElement
,
5181 StorageBaseImpl_SetElementTimes
,
5182 StorageBaseImpl_SetClass
,
5183 StorageBaseImpl_SetStateBits
,
5184 StorageBaseImpl_Stat
5187 static HRESULT
StorageImpl_Construct(
5195 StorageImpl
** result
)
5201 if ( FAILED( validateSTGM(openFlags
) ))
5202 return STG_E_INVALIDFLAG
;
5204 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5206 return E_OUTOFMEMORY
;
5208 memset(This
, 0, sizeof(StorageImpl
));
5210 list_init(&This
->base
.strmHead
);
5212 list_init(&This
->base
.storageHead
);
5214 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5215 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5216 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5217 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5218 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5220 This
->base
.create
= create
;
5222 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5223 This
->base
.lockingrole
= SWMR_Writer
;
5224 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5225 This
->base
.lockingrole
= SWMR_Reader
;
5227 This
->base
.lockingrole
= SWMR_None
;
5229 This
->base
.reverted
= FALSE
;
5232 * Initialize the big block cache.
5234 This
->bigBlockSize
= sector_size
;
5235 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5237 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5240 This
->lockBytes
= pLkbyt
;
5241 ILockBytes_AddRef(pLkbyt
);
5245 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5249 This
->locks_supported
= stat
.grfLocksSupported
;
5251 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5252 This
->locks_supported
&= ~WINE_LOCK_READ
;
5254 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5258 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5262 IStorage_Release(&This
->base
.IStorage_iface
);
5267 StorageImpl_Flush(&This
->base
);
5275 /************************************************************************
5276 * StorageInternalImpl implementation
5277 ***********************************************************************/
5279 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5281 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5283 if (!This
->base
.reverted
)
5285 TRACE("Storage invalidated (stg=%p)\n", This
);
5287 This
->base
.reverted
= TRUE
;
5289 This
->parentStorage
= NULL
;
5291 StorageBaseImpl_DeleteAll(&This
->base
);
5293 list_remove(&This
->ParentListEntry
);
5297 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5299 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5301 StorageInternalImpl_Invalidate(&This
->base
);
5303 HeapFree(GetProcessHeap(), 0, This
);
5306 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5308 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5310 return StorageBaseImpl_Flush(This
->parentStorage
);
5313 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5315 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5317 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5320 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5321 const DirEntry
*newData
, DirRef
*index
)
5323 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5325 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5329 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5330 DirRef index
, const DirEntry
*data
)
5332 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5334 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5338 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5339 DirRef index
, DirEntry
*data
)
5341 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5343 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5347 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5350 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5352 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5356 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5357 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5359 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5361 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5362 index
, offset
, size
, buffer
, bytesRead
);
5365 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5366 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5368 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5370 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5371 index
, offset
, size
, buffer
, bytesWritten
);
5374 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5375 DirRef index
, ULARGE_INTEGER newsize
)
5377 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5379 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5383 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5384 DirRef dst
, DirRef src
)
5386 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5388 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5392 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5393 ULONG
* result
, BOOL refresh
)
5398 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5404 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5409 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5414 /******************************************************************************
5416 ** StorageInternalImpl_Commit
5419 static HRESULT WINAPI
StorageInternalImpl_Commit(
5421 DWORD grfCommitFlags
) /* [in] */
5423 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5424 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5425 return StorageBaseImpl_Flush(This
);
5428 /******************************************************************************
5430 ** StorageInternalImpl_Revert
5433 static HRESULT WINAPI
StorageInternalImpl_Revert(
5436 FIXME("(%p): stub\n", iface
);
5441 * Virtual function table for the StorageInternalImpl class.
5443 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5445 StorageBaseImpl_QueryInterface
,
5446 StorageBaseImpl_AddRef
,
5447 StorageBaseImpl_Release
,
5448 StorageBaseImpl_CreateStream
,
5449 StorageBaseImpl_OpenStream
,
5450 StorageBaseImpl_CreateStorage
,
5451 StorageBaseImpl_OpenStorage
,
5452 StorageBaseImpl_CopyTo
,
5453 StorageBaseImpl_MoveElementTo
,
5454 StorageInternalImpl_Commit
,
5455 StorageInternalImpl_Revert
,
5456 StorageBaseImpl_EnumElements
,
5457 StorageBaseImpl_DestroyElement
,
5458 StorageBaseImpl_RenameElement
,
5459 StorageBaseImpl_SetElementTimes
,
5460 StorageBaseImpl_SetClass
,
5461 StorageBaseImpl_SetStateBits
,
5462 StorageBaseImpl_Stat
5465 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5467 StorageInternalImpl_Destroy
,
5468 StorageInternalImpl_Invalidate
,
5469 StorageInternalImpl_Flush
,
5470 StorageInternalImpl_GetFilename
,
5471 StorageInternalImpl_CreateDirEntry
,
5472 StorageInternalImpl_WriteDirEntry
,
5473 StorageInternalImpl_ReadDirEntry
,
5474 StorageInternalImpl_DestroyDirEntry
,
5475 StorageInternalImpl_StreamReadAt
,
5476 StorageInternalImpl_StreamWriteAt
,
5477 StorageInternalImpl_StreamSetSize
,
5478 StorageInternalImpl_StreamLink
,
5479 StorageInternalImpl_GetTransactionSig
,
5480 StorageInternalImpl_SetTransactionSig
,
5481 StorageInternalImpl_LockTransaction
,
5482 StorageInternalImpl_UnlockTransaction
5485 static StorageInternalImpl
* StorageInternalImpl_Construct(
5486 StorageBaseImpl
* parentStorage
,
5488 DirRef storageDirEntry
)
5490 StorageInternalImpl
* newStorage
;
5492 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5496 list_init(&newStorage
->base
.strmHead
);
5498 list_init(&newStorage
->base
.storageHead
);
5501 * Initialize the virtual function table.
5503 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5504 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5505 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5506 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5508 newStorage
->base
.reverted
= FALSE
;
5510 newStorage
->base
.ref
= 1;
5512 newStorage
->parentStorage
= parentStorage
;
5515 * Keep a reference to the directory entry of this storage
5517 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5519 newStorage
->base
.create
= FALSE
;
5528 /************************************************************************
5529 * TransactedSnapshotImpl implementation
5530 ***********************************************************************/
5532 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5534 DirRef result
=This
->firstFreeEntry
;
5536 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5539 if (result
== This
->entries_size
)
5541 ULONG new_size
= This
->entries_size
* 2;
5542 TransactedDirEntry
*new_entries
;
5544 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5545 if (!new_entries
) return DIRENTRY_NULL
;
5547 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5548 HeapFree(GetProcessHeap(), 0, This
->entries
);
5550 This
->entries
= new_entries
;
5551 This
->entries_size
= new_size
;
5554 This
->entries
[result
].inuse
= TRUE
;
5556 This
->firstFreeEntry
= result
+1;
5561 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5562 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5564 DirRef stubEntryRef
;
5565 TransactedDirEntry
*entry
;
5567 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5569 if (stubEntryRef
!= DIRENTRY_NULL
)
5571 entry
= &This
->entries
[stubEntryRef
];
5573 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5575 entry
->read
= FALSE
;
5578 return stubEntryRef
;
5581 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5582 TransactedSnapshotImpl
*This
, DirRef entry
)
5587 if (!This
->entries
[entry
].read
)
5589 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5590 This
->entries
[entry
].transactedParentEntry
,
5593 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5595 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5597 if (data
.leftChild
== DIRENTRY_NULL
)
5601 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5603 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5605 if (data
.rightChild
== DIRENTRY_NULL
)
5609 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5611 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5613 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5619 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5620 This
->entries
[entry
].read
= TRUE
;
5627 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5628 TransactedSnapshotImpl
*This
, DirRef entry
)
5632 if (!This
->entries
[entry
].stream_dirty
)
5634 DirEntry new_entrydata
;
5636 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5637 new_entrydata
.name
[0] = 'S';
5638 new_entrydata
.sizeOfNameString
= 1;
5639 new_entrydata
.stgType
= STGTY_STREAM
;
5640 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5641 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5642 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5643 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5645 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5646 &This
->entries
[entry
].stream_entry
);
5648 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5650 hr
= StorageBaseImpl_CopyStream(
5651 This
->scratch
, This
->entries
[entry
].stream_entry
,
5652 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5655 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5659 This
->entries
[entry
].stream_dirty
= TRUE
;
5661 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5663 /* Since this entry is modified, and we aren't using its stream data, we
5664 * no longer care about the original entry. */
5666 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5668 if (delete_ref
!= DIRENTRY_NULL
)
5669 This
->entries
[delete_ref
].deleted
= TRUE
;
5671 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5678 /* Find the first entry in a depth-first traversal. */
5679 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5680 TransactedSnapshotImpl
* This
, DirRef parent
)
5682 DirRef cursor
, prev
;
5683 TransactedDirEntry
*entry
;
5686 entry
= &This
->entries
[cursor
];
5689 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5692 cursor
= entry
->data
.leftChild
;
5693 entry
= &This
->entries
[cursor
];
5694 entry
->parent
= prev
;
5696 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5699 cursor
= entry
->data
.rightChild
;
5700 entry
= &This
->entries
[cursor
];
5701 entry
->parent
= prev
;
5703 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5706 cursor
= entry
->data
.dirRootEntry
;
5707 entry
= &This
->entries
[cursor
];
5708 entry
->parent
= prev
;
5717 /* Find the next entry in a depth-first traversal. */
5718 static DirRef
TransactedSnapshotImpl_FindNextChild(
5719 TransactedSnapshotImpl
* This
, DirRef current
)
5722 TransactedDirEntry
*parent_entry
;
5724 parent
= This
->entries
[current
].parent
;
5725 parent_entry
= &This
->entries
[parent
];
5727 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5729 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5731 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5732 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5735 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5737 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5738 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5745 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5746 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5747 TransactedSnapshotImpl
* This
, DirRef entry
)
5749 return entry
!= DIRENTRY_NULL
&&
5750 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5753 /* Destroy the entries created by CopyTree. */
5754 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5755 TransactedSnapshotImpl
* This
, DirRef stop
)
5758 TransactedDirEntry
*entry
;
5759 ULARGE_INTEGER zero
;
5763 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5766 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5768 if (cursor
== DIRENTRY_NULL
)
5771 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5773 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5775 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5777 entry
= &This
->entries
[cursor
];
5779 if (entry
->stream_dirty
)
5780 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5781 entry
->newTransactedParentEntry
, zero
);
5783 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5784 entry
->newTransactedParentEntry
);
5786 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5789 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5793 /* Make a copy of our edited tree that we can use in the parent. */
5794 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5797 TransactedDirEntry
*entry
;
5800 cursor
= This
->base
.storageDirEntry
;
5801 entry
= &This
->entries
[cursor
];
5802 entry
->parent
= DIRENTRY_NULL
;
5803 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5805 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5808 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5810 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5811 entry
= &This
->entries
[cursor
];
5813 while (cursor
!= DIRENTRY_NULL
)
5815 /* Make a copy of this entry in the transacted parent. */
5817 (!entry
->dirty
&& !entry
->stream_dirty
&&
5818 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5819 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5820 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5821 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5826 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5828 newData
.size
.QuadPart
= 0;
5829 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5831 if (newData
.leftChild
!= DIRENTRY_NULL
)
5832 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5834 if (newData
.rightChild
!= DIRENTRY_NULL
)
5835 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5837 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5838 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5840 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5841 &entry
->newTransactedParentEntry
);
5844 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5848 if (entry
->stream_dirty
)
5850 hr
= StorageBaseImpl_CopyStream(
5851 This
->transactedParent
, entry
->newTransactedParentEntry
,
5852 This
->scratch
, entry
->stream_entry
);
5854 else if (entry
->data
.size
.QuadPart
)
5856 hr
= StorageBaseImpl_StreamLink(
5857 This
->transactedParent
, entry
->newTransactedParentEntry
,
5858 entry
->transactedParentEntry
);
5863 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5864 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5869 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5870 entry
= &This
->entries
[cursor
];
5876 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5878 DWORD grfCommitFlags
) /* [in] */
5880 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5881 TransactedDirEntry
*root_entry
;
5882 DirRef i
, dir_root_ref
;
5884 ULARGE_INTEGER zero
;
5886 ULONG transactionSig
;
5890 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5892 /* Cannot commit a read-only transacted storage */
5893 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5894 return STG_E_ACCESSDENIED
;
5896 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5897 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5900 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5903 if (transactionSig
!= This
->lastTransactionSig
)
5905 ERR("file was externally modified\n");
5906 hr
= STG_E_NOTCURRENT
;
5911 This
->lastTransactionSig
= transactionSig
+1;
5912 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5915 else if (hr
== E_NOTIMPL
)
5918 if (FAILED(hr
)) goto end
;
5920 /* To prevent data loss, we create the new structure in the file before we
5921 * delete the old one, so that in case of errors the old data is intact. We
5922 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5923 * needed in the rare situation where we have just enough free disk space to
5924 * overwrite the existing data. */
5926 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5928 if (!root_entry
->read
)
5931 hr
= TransactedSnapshotImpl_CopyTree(This
);
5932 if (FAILED(hr
)) goto end
;
5934 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5935 dir_root_ref
= DIRENTRY_NULL
;
5937 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5939 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5941 /* Update the storage to use the new data in one step. */
5943 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5944 root_entry
->transactedParentEntry
, &data
);
5948 data
.dirRootEntry
= dir_root_ref
;
5949 data
.clsid
= root_entry
->data
.clsid
;
5950 data
.ctime
= root_entry
->data
.ctime
;
5951 data
.mtime
= root_entry
->data
.mtime
;
5953 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5954 root_entry
->transactedParentEntry
, &data
);
5957 /* Try to flush after updating the root storage, but if the flush fails, keep
5958 * going, on the theory that it'll either succeed later or the subsequent
5959 * writes will fail. */
5960 StorageBaseImpl_Flush(This
->transactedParent
);
5964 /* Destroy the old now-orphaned data. */
5965 for (i
=0; i
<This
->entries_size
; i
++)
5967 TransactedDirEntry
*entry
= &This
->entries
[i
];
5972 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5973 entry
->transactedParentEntry
, zero
);
5974 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5975 entry
->transactedParentEntry
);
5976 memset(entry
, 0, sizeof(TransactedDirEntry
));
5977 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5979 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5981 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5982 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5983 entry
->transactedParentEntry
);
5984 if (entry
->stream_dirty
)
5986 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5987 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5988 entry
->stream_dirty
= FALSE
;
5990 entry
->dirty
= FALSE
;
5991 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5998 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6002 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6004 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6010 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6013 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6014 ULARGE_INTEGER zero
;
6017 TRACE("(%p)\n", iface
);
6019 /* Destroy the open objects. */
6020 StorageBaseImpl_DeleteAll(&This
->base
);
6022 /* Clear out the scratch file. */
6024 for (i
=0; i
<This
->entries_size
; i
++)
6026 if (This
->entries
[i
].stream_dirty
)
6028 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6031 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6035 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6037 This
->firstFreeEntry
= 0;
6038 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6043 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6045 if (!This
->reverted
)
6047 TRACE("Storage invalidated (stg=%p)\n", This
);
6049 This
->reverted
= TRUE
;
6051 StorageBaseImpl_DeleteAll(This
);
6055 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6057 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6059 IStorage_Revert(&This
->base
.IStorage_iface
);
6060 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6061 IStorage_Release(&This
->scratch
->IStorage_iface
);
6062 HeapFree(GetProcessHeap(), 0, This
->entries
);
6063 HeapFree(GetProcessHeap(), 0, This
);
6066 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6068 /* We only need to flush when committing. */
6072 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6074 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6076 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6079 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6080 const DirEntry
*newData
, DirRef
*index
)
6082 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6084 TransactedDirEntry
*new_entry
;
6086 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6087 if (new_ref
== DIRENTRY_NULL
)
6088 return E_OUTOFMEMORY
;
6090 new_entry
= &This
->entries
[new_ref
];
6092 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6093 new_entry
->read
= TRUE
;
6094 new_entry
->dirty
= TRUE
;
6095 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6099 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6104 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6105 DirRef index
, const DirEntry
*data
)
6107 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6110 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6112 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6113 if (FAILED(hr
)) return hr
;
6115 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6117 if (index
!= This
->base
.storageDirEntry
)
6119 This
->entries
[index
].dirty
= TRUE
;
6121 if (data
->size
.QuadPart
== 0 &&
6122 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6124 /* Since this entry is modified, and we aren't using its stream data, we
6125 * no longer care about the original entry. */
6127 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6129 if (delete_ref
!= DIRENTRY_NULL
)
6130 This
->entries
[delete_ref
].deleted
= TRUE
;
6132 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6139 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6140 DirRef index
, DirEntry
*data
)
6142 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6145 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6146 if (FAILED(hr
)) return hr
;
6148 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6150 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6155 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6158 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6160 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6161 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6163 /* If we deleted this entry while it has stream data. We must have left the
6164 * data because some other entry is using it, and we need to leave the
6165 * original entry alone. */
6166 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6167 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6171 This
->entries
[index
].deleted
= TRUE
;
6177 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6178 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6180 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6182 if (This
->entries
[index
].stream_dirty
)
6184 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6185 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6187 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6189 /* This stream doesn't live in the parent, and we haven't allocated storage
6196 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6197 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6201 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6202 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6204 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6207 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6208 if (FAILED(hr
)) return hr
;
6210 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6211 if (FAILED(hr
)) return hr
;
6213 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6214 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6216 if (SUCCEEDED(hr
) && size
!= 0)
6217 This
->entries
[index
].data
.size
.QuadPart
= max(
6218 This
->entries
[index
].data
.size
.QuadPart
,
6219 offset
.QuadPart
+ size
);
6224 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6225 DirRef index
, ULARGE_INTEGER newsize
)
6227 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6230 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6231 if (FAILED(hr
)) return hr
;
6233 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6236 if (newsize
.QuadPart
== 0)
6238 /* Destroy any parent references or entries in the scratch file. */
6239 if (This
->entries
[index
].stream_dirty
)
6241 ULARGE_INTEGER zero
;
6243 StorageBaseImpl_StreamSetSize(This
->scratch
,
6244 This
->entries
[index
].stream_entry
, zero
);
6245 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6246 This
->entries
[index
].stream_entry
);
6247 This
->entries
[index
].stream_dirty
= FALSE
;
6249 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6252 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6254 if (delete_ref
!= DIRENTRY_NULL
)
6255 This
->entries
[delete_ref
].deleted
= TRUE
;
6257 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6262 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6263 if (FAILED(hr
)) return hr
;
6265 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6266 This
->entries
[index
].stream_entry
, newsize
);
6270 This
->entries
[index
].data
.size
= newsize
;
6275 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6276 DirRef dst
, DirRef src
)
6278 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6280 TransactedDirEntry
*dst_entry
, *src_entry
;
6282 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6283 if (FAILED(hr
)) return hr
;
6285 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6286 if (FAILED(hr
)) return hr
;
6288 dst_entry
= &This
->entries
[dst
];
6289 src_entry
= &This
->entries
[src
];
6291 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6292 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6293 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6294 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6295 dst_entry
->data
.size
= src_entry
->data
.size
;
6300 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6301 ULONG
* result
, BOOL refresh
)
6306 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6312 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6317 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6322 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6324 StorageBaseImpl_QueryInterface
,
6325 StorageBaseImpl_AddRef
,
6326 StorageBaseImpl_Release
,
6327 StorageBaseImpl_CreateStream
,
6328 StorageBaseImpl_OpenStream
,
6329 StorageBaseImpl_CreateStorage
,
6330 StorageBaseImpl_OpenStorage
,
6331 StorageBaseImpl_CopyTo
,
6332 StorageBaseImpl_MoveElementTo
,
6333 TransactedSnapshotImpl_Commit
,
6334 TransactedSnapshotImpl_Revert
,
6335 StorageBaseImpl_EnumElements
,
6336 StorageBaseImpl_DestroyElement
,
6337 StorageBaseImpl_RenameElement
,
6338 StorageBaseImpl_SetElementTimes
,
6339 StorageBaseImpl_SetClass
,
6340 StorageBaseImpl_SetStateBits
,
6341 StorageBaseImpl_Stat
6344 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6346 TransactedSnapshotImpl_Destroy
,
6347 TransactedSnapshotImpl_Invalidate
,
6348 TransactedSnapshotImpl_Flush
,
6349 TransactedSnapshotImpl_GetFilename
,
6350 TransactedSnapshotImpl_CreateDirEntry
,
6351 TransactedSnapshotImpl_WriteDirEntry
,
6352 TransactedSnapshotImpl_ReadDirEntry
,
6353 TransactedSnapshotImpl_DestroyDirEntry
,
6354 TransactedSnapshotImpl_StreamReadAt
,
6355 TransactedSnapshotImpl_StreamWriteAt
,
6356 TransactedSnapshotImpl_StreamSetSize
,
6357 TransactedSnapshotImpl_StreamLink
,
6358 TransactedSnapshotImpl_GetTransactionSig
,
6359 TransactedSnapshotImpl_SetTransactionSig
,
6360 TransactedSnapshotImpl_LockTransaction
,
6361 TransactedSnapshotImpl_UnlockTransaction
6364 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6365 TransactedSnapshotImpl
** result
)
6369 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6374 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6376 /* This is OK because the property set storage functions use the IStorage functions. */
6377 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6378 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6380 list_init(&(*result
)->base
.strmHead
);
6382 list_init(&(*result
)->base
.storageHead
);
6384 (*result
)->base
.ref
= 1;
6386 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6388 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6389 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6391 /* Create a new temporary storage to act as the scratch file. */
6392 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6394 (*result
)->scratch
= impl_from_IStorage(scratch
);
6398 ULONG num_entries
= 20;
6400 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6401 (*result
)->entries_size
= num_entries
;
6402 (*result
)->firstFreeEntry
= 0;
6404 if ((*result
)->entries
)
6406 /* parentStorage already has 1 reference, which we take over here. */
6407 (*result
)->transactedParent
= parentStorage
;
6409 parentStorage
->transactedChild
= &(*result
)->base
;
6411 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6415 IStorage_Release(scratch
);
6421 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6426 return E_OUTOFMEMORY
;
6430 /************************************************************************
6431 * TransactedSharedImpl implementation
6432 ***********************************************************************/
6434 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6436 if (!This
->reverted
)
6438 TRACE("Storage invalidated (stg=%p)\n", This
);
6440 This
->reverted
= TRUE
;
6442 StorageBaseImpl_DeleteAll(This
);
6446 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6448 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6450 TransactedSharedImpl_Invalidate(&This
->base
);
6451 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6452 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6453 HeapFree(GetProcessHeap(), 0, This
);
6456 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6458 /* We only need to flush when committing. */
6462 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6464 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6466 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6469 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6470 const DirEntry
*newData
, DirRef
*index
)
6472 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6474 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6478 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6479 DirRef index
, const DirEntry
*data
)
6481 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6483 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6487 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6488 DirRef index
, DirEntry
*data
)
6490 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6492 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6496 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6499 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6501 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6505 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6506 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6508 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6510 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6511 index
, offset
, size
, buffer
, bytesRead
);
6514 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6515 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6517 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6519 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6520 index
, offset
, size
, buffer
, bytesWritten
);
6523 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6524 DirRef index
, ULARGE_INTEGER newsize
)
6526 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6528 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6532 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6533 DirRef dst
, DirRef src
)
6535 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6537 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6541 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6542 ULONG
* result
, BOOL refresh
)
6547 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6553 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6558 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6563 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6565 DWORD grfCommitFlags
) /* [in] */
6567 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6568 DirRef new_storage_ref
, prev_storage_ref
;
6569 DirEntry src_data
, dst_data
;
6571 ULONG transactionSig
;
6573 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6575 /* Cannot commit a read-only transacted storage */
6576 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6577 return STG_E_ACCESSDENIED
;
6579 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6580 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6583 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6586 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6587 hr
= STG_E_NOTCURRENT
;
6590 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6592 else if (hr
== E_NOTIMPL
)
6596 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6598 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6600 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6603 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6606 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6610 prev_storage_ref
= dst_data
.dirRootEntry
;
6611 dst_data
.dirRootEntry
= new_storage_ref
;
6612 dst_data
.clsid
= src_data
.clsid
;
6613 dst_data
.ctime
= src_data
.ctime
;
6614 dst_data
.mtime
= src_data
.mtime
;
6615 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6620 /* Try to flush after updating the root storage, but if the flush fails, keep
6621 * going, on the theory that it'll either succeed later or the subsequent
6622 * writes will fail. */
6623 StorageBaseImpl_Flush(This
->transactedParent
);
6625 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6629 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6631 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6634 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6638 This
->lastTransactionSig
= transactionSig
+1;
6645 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6648 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6650 TRACE("(%p)\n", iface
);
6652 /* Destroy the open objects. */
6653 StorageBaseImpl_DeleteAll(&This
->base
);
6655 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6658 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6660 StorageBaseImpl_QueryInterface
,
6661 StorageBaseImpl_AddRef
,
6662 StorageBaseImpl_Release
,
6663 StorageBaseImpl_CreateStream
,
6664 StorageBaseImpl_OpenStream
,
6665 StorageBaseImpl_CreateStorage
,
6666 StorageBaseImpl_OpenStorage
,
6667 StorageBaseImpl_CopyTo
,
6668 StorageBaseImpl_MoveElementTo
,
6669 TransactedSharedImpl_Commit
,
6670 TransactedSharedImpl_Revert
,
6671 StorageBaseImpl_EnumElements
,
6672 StorageBaseImpl_DestroyElement
,
6673 StorageBaseImpl_RenameElement
,
6674 StorageBaseImpl_SetElementTimes
,
6675 StorageBaseImpl_SetClass
,
6676 StorageBaseImpl_SetStateBits
,
6677 StorageBaseImpl_Stat
6680 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6682 TransactedSharedImpl_Destroy
,
6683 TransactedSharedImpl_Invalidate
,
6684 TransactedSharedImpl_Flush
,
6685 TransactedSharedImpl_GetFilename
,
6686 TransactedSharedImpl_CreateDirEntry
,
6687 TransactedSharedImpl_WriteDirEntry
,
6688 TransactedSharedImpl_ReadDirEntry
,
6689 TransactedSharedImpl_DestroyDirEntry
,
6690 TransactedSharedImpl_StreamReadAt
,
6691 TransactedSharedImpl_StreamWriteAt
,
6692 TransactedSharedImpl_StreamSetSize
,
6693 TransactedSharedImpl_StreamLink
,
6694 TransactedSharedImpl_GetTransactionSig
,
6695 TransactedSharedImpl_SetTransactionSig
,
6696 TransactedSharedImpl_LockTransaction
,
6697 TransactedSharedImpl_UnlockTransaction
6700 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6701 TransactedSharedImpl
** result
)
6705 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6710 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6712 /* This is OK because the property set storage functions use the IStorage functions. */
6713 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6714 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6716 list_init(&(*result
)->base
.strmHead
);
6718 list_init(&(*result
)->base
.storageHead
);
6720 (*result
)->base
.ref
= 1;
6722 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6724 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6730 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6731 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6735 stgo
.ulSectorSize
= 4096;
6736 stgo
.pwcsTemplateFile
= NULL
;
6738 /* Create a new temporary storage to act as the scratch file. */
6739 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6740 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6741 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6745 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6746 parentStorage
, parentStorage
->storageDirEntry
);
6750 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6752 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6753 (*result
)->transactedParent
= parentStorage
;
6757 IStorage_Release(scratch
);
6760 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6763 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6768 return E_OUTOFMEMORY
;
6771 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6772 BOOL toplevel
, StorageBaseImpl
** result
)
6774 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6776 if (parentStorage
->openFlags
& fixme_flags
)
6778 fixme_flags
&= ~parentStorage
->openFlags
;
6779 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6782 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6783 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6784 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6786 /* Need to create a temp file for the snapshot */
6787 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6790 return TransactedSnapshotImpl_Construct(parentStorage
,
6791 (TransactedSnapshotImpl
**)result
);
6794 static HRESULT
Storage_Construct(
6802 StorageBaseImpl
** result
)
6804 StorageImpl
*newStorage
;
6805 StorageBaseImpl
*newTransactedStorage
;
6808 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6809 if (FAILED(hr
)) goto end
;
6811 if (openFlags
& STGM_TRANSACTED
)
6813 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6815 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6817 *result
= newTransactedStorage
;
6820 *result
= &newStorage
->base
;
6827 /************************************************************************
6828 * StorageUtl helper functions
6829 ***********************************************************************/
6831 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6835 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6836 *value
= lendian16toh(tmp
);
6839 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6841 value
= htole16(value
);
6842 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6845 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6849 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6850 *value
= lendian32toh(tmp
);
6853 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6855 value
= htole32(value
);
6856 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6859 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6860 ULARGE_INTEGER
* value
)
6862 #ifdef WORDS_BIGENDIAN
6865 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6866 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6867 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6869 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6873 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6874 const ULARGE_INTEGER
*value
)
6876 #ifdef WORDS_BIGENDIAN
6879 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6880 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6881 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6883 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6887 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6889 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6890 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6891 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6893 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6896 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6898 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6899 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6900 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6902 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6905 void StorageUtl_CopyDirEntryToSTATSTG(
6906 StorageBaseImpl
* storage
,
6907 STATSTG
* destination
,
6908 const DirEntry
* source
,
6912 * The copy of the string occurs only when the flag is not set
6914 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6916 /* Use the filename for the root storage. */
6917 destination
->pwcsName
= 0;
6918 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6920 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6921 (source
->name
[0] == 0) )
6923 destination
->pwcsName
= 0;
6927 destination
->pwcsName
=
6928 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6930 strcpyW(destination
->pwcsName
, source
->name
);
6933 switch (source
->stgType
)
6937 destination
->type
= STGTY_STORAGE
;
6940 destination
->type
= STGTY_STREAM
;
6943 destination
->type
= STGTY_STREAM
;
6947 destination
->cbSize
= source
->size
;
6949 currentReturnStruct->mtime = {0}; TODO
6950 currentReturnStruct->ctime = {0};
6951 currentReturnStruct->atime = {0};
6953 destination
->grfMode
= 0;
6954 destination
->grfLocksSupported
= 0;
6955 destination
->clsid
= source
->clsid
;
6956 destination
->grfStateBits
= 0;
6957 destination
->reserved
= 0;
6961 /************************************************************************
6962 * BlockChainStream implementation
6963 ***********************************************************************/
6965 /******************************************************************************
6966 * BlockChainStream_GetHeadOfChain
6968 * Returns the head of this stream chain.
6969 * Some special chains don't have directory entries, their heads are kept in
6970 * This->headOfStreamPlaceHolder.
6973 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6975 DirEntry chainEntry
;
6978 if (This
->headOfStreamPlaceHolder
!= 0)
6979 return *(This
->headOfStreamPlaceHolder
);
6981 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6983 hr
= StorageImpl_ReadDirEntry(
6984 This
->parentStorage
,
6985 This
->ownerDirEntry
,
6988 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
6989 return chainEntry
.startingBlock
;
6992 return BLOCK_END_OF_CHAIN
;
6995 /* Read and save the index of all blocks in this stream. */
6996 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6998 ULONG next_sector
, next_offset
;
7000 struct BlockChainRun
*last_run
;
7002 if (This
->indexCacheLen
== 0)
7006 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7010 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7011 next_offset
= last_run
->lastOffset
+1;
7012 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7013 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7015 if (FAILED(hr
)) return hr
;
7018 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7020 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7022 /* Add the current block to the cache. */
7023 if (This
->indexCacheSize
== 0)
7025 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7026 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7027 This
->indexCacheSize
= 16;
7029 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7031 struct BlockChainRun
*new_cache
;
7034 new_size
= This
->indexCacheSize
* 2;
7035 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7036 if (!new_cache
) return E_OUTOFMEMORY
;
7037 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7039 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7040 This
->indexCache
= new_cache
;
7041 This
->indexCacheSize
= new_size
;
7044 This
->indexCacheLen
++;
7045 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7046 last_run
->firstSector
= next_sector
;
7047 last_run
->firstOffset
= next_offset
;
7050 last_run
->lastOffset
= next_offset
;
7052 /* Find the next block. */
7054 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7055 if (FAILED(hr
)) return hr
;
7058 if (This
->indexCacheLen
)
7060 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7061 This
->numBlocks
= last_run
->lastOffset
+1;
7065 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7066 This
->numBlocks
= 0;
7072 /* Locate the nth block in this stream. */
7073 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7075 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7076 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7078 if (offset
>= This
->numBlocks
)
7079 return BLOCK_END_OF_CHAIN
;
7081 while (min_run
< max_run
)
7083 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7084 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7086 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7087 max_run
= run_to_check
-1;
7089 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7091 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7092 min_run
= run_to_check
+1;
7095 /* Block is in this run. */
7096 min_run
= max_run
= run_to_check
;
7099 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7102 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7103 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7105 BlockChainBlock
*result
=NULL
;
7109 if (This
->cachedBlocks
[i
].index
== index
)
7111 *sector
= This
->cachedBlocks
[i
].sector
;
7112 *block
= &This
->cachedBlocks
[i
];
7116 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7117 if (*sector
== BLOCK_END_OF_CHAIN
)
7118 return STG_E_DOCFILECORRUPT
;
7122 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7123 result
= &This
->cachedBlocks
[0];
7124 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7125 result
= &This
->cachedBlocks
[1];
7128 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7129 if (This
->blockToEvict
== 2)
7130 This
->blockToEvict
= 0;
7135 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7136 return STG_E_WRITEFAULT
;
7137 result
->dirty
= FALSE
;
7140 result
->read
= FALSE
;
7141 result
->index
= index
;
7142 result
->sector
= *sector
;
7149 BlockChainStream
* BlockChainStream_Construct(
7150 StorageImpl
* parentStorage
,
7151 ULONG
* headOfStreamPlaceHolder
,
7154 BlockChainStream
* newStream
;
7156 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7158 newStream
->parentStorage
= parentStorage
;
7159 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7160 newStream
->ownerDirEntry
= dirEntry
;
7161 newStream
->indexCache
= NULL
;
7162 newStream
->indexCacheLen
= 0;
7163 newStream
->indexCacheSize
= 0;
7164 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7165 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7166 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7167 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7168 newStream
->blockToEvict
= 0;
7170 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7172 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7173 HeapFree(GetProcessHeap(), 0, newStream
);
7180 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7183 if (!This
) return S_OK
;
7186 if (This
->cachedBlocks
[i
].dirty
)
7188 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7189 This
->cachedBlocks
[i
].dirty
= FALSE
;
7191 return STG_E_WRITEFAULT
;
7197 void BlockChainStream_Destroy(BlockChainStream
* This
)
7201 BlockChainStream_Flush(This
);
7202 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7204 HeapFree(GetProcessHeap(), 0, This
);
7207 /******************************************************************************
7208 * BlockChainStream_Shrink
7210 * Shrinks this chain in the big block depot.
7212 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7213 ULARGE_INTEGER newSize
)
7220 * Figure out how many blocks are needed to contain the new size
7222 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7224 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7230 * Go to the new end of chain
7232 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7234 /* Mark the new end of chain */
7235 StorageImpl_SetNextBlockInChain(
7236 This
->parentStorage
,
7238 BLOCK_END_OF_CHAIN
);
7240 This
->tailIndex
= blockIndex
;
7244 if (This
->headOfStreamPlaceHolder
!= 0)
7246 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7250 DirEntry chainEntry
;
7251 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7253 StorageImpl_ReadDirEntry(
7254 This
->parentStorage
,
7255 This
->ownerDirEntry
,
7258 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7260 StorageImpl_WriteDirEntry(
7261 This
->parentStorage
,
7262 This
->ownerDirEntry
,
7266 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7269 This
->numBlocks
= numBlocks
;
7272 * Mark the extra blocks as free
7274 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7276 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7277 StorageImpl_FreeBigBlock(This
->parentStorage
,
7278 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7279 if (last_run
->lastOffset
== last_run
->firstOffset
)
7280 This
->indexCacheLen
--;
7282 last_run
->lastOffset
--;
7286 * Reset the last accessed block cache.
7290 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7292 This
->cachedBlocks
[i
].index
= 0xffffffff;
7293 This
->cachedBlocks
[i
].dirty
= FALSE
;
7300 /******************************************************************************
7301 * BlockChainStream_Enlarge
7303 * Grows this chain in the big block depot.
7305 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7306 ULARGE_INTEGER newSize
)
7308 ULONG blockIndex
, currentBlock
;
7310 ULONG oldNumBlocks
= 0;
7312 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7315 * Empty chain. Create the head.
7317 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7319 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7320 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7322 BLOCK_END_OF_CHAIN
);
7324 if (This
->headOfStreamPlaceHolder
!= 0)
7326 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7330 DirEntry chainEntry
;
7331 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7333 StorageImpl_ReadDirEntry(
7334 This
->parentStorage
,
7335 This
->ownerDirEntry
,
7338 chainEntry
.startingBlock
= blockIndex
;
7340 StorageImpl_WriteDirEntry(
7341 This
->parentStorage
,
7342 This
->ownerDirEntry
,
7346 This
->tailIndex
= blockIndex
;
7347 This
->numBlocks
= 1;
7351 * Figure out how many blocks are needed to contain this stream
7353 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7355 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7359 * Go to the current end of chain
7361 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7363 currentBlock
= blockIndex
;
7365 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7368 currentBlock
= blockIndex
;
7370 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7375 This
->tailIndex
= currentBlock
;
7378 currentBlock
= This
->tailIndex
;
7379 oldNumBlocks
= This
->numBlocks
;
7382 * Add new blocks to the chain
7384 if (oldNumBlocks
< newNumBlocks
)
7386 while (oldNumBlocks
< newNumBlocks
)
7388 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7390 StorageImpl_SetNextBlockInChain(
7391 This
->parentStorage
,
7395 StorageImpl_SetNextBlockInChain(
7396 This
->parentStorage
,
7398 BLOCK_END_OF_CHAIN
);
7400 currentBlock
= blockIndex
;
7404 This
->tailIndex
= blockIndex
;
7405 This
->numBlocks
= newNumBlocks
;
7408 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7415 /******************************************************************************
7416 * BlockChainStream_GetSize
7418 * Returns the size of this chain.
7419 * Will return the block count if this chain doesn't have a directory entry.
7421 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7423 DirEntry chainEntry
;
7425 if(This
->headOfStreamPlaceHolder
== NULL
)
7428 * This chain has a directory entry so use the size value from there.
7430 StorageImpl_ReadDirEntry(
7431 This
->parentStorage
,
7432 This
->ownerDirEntry
,
7435 return chainEntry
.size
;
7440 * this chain is a chain that does not have a directory entry, figure out the
7441 * size by making the product number of used blocks times the
7444 ULARGE_INTEGER result
;
7446 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7447 This
->parentStorage
->bigBlockSize
;
7453 /******************************************************************************
7454 * BlockChainStream_SetSize
7456 * Sets the size of this stream. The big block depot will be updated.
7457 * The file will grow if we grow the chain.
7459 * TODO: Free the actual blocks in the file when we shrink the chain.
7460 * Currently, the blocks are still in the file. So the file size
7461 * doesn't shrink even if we shrink streams.
7463 BOOL
BlockChainStream_SetSize(
7464 BlockChainStream
* This
,
7465 ULARGE_INTEGER newSize
)
7467 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7469 if (newSize
.QuadPart
== size
.QuadPart
)
7472 if (newSize
.QuadPart
< size
.QuadPart
)
7474 BlockChainStream_Shrink(This
, newSize
);
7478 BlockChainStream_Enlarge(This
, newSize
);
7484 /******************************************************************************
7485 * BlockChainStream_ReadAt
7487 * Reads a specified number of bytes from this chain at the specified offset.
7488 * bytesRead may be NULL.
7489 * Failure will be returned if the specified number of bytes has not been read.
7491 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7492 ULARGE_INTEGER offset
,
7497 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7498 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7499 ULONG bytesToReadInBuffer
;
7502 ULARGE_INTEGER stream_size
;
7504 BlockChainBlock
*cachedBlock
;
7506 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7509 * Find the first block in the stream that contains part of the buffer.
7511 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7515 stream_size
= BlockChainStream_GetSize(This
);
7516 if (stream_size
.QuadPart
> offset
.QuadPart
)
7517 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7522 * Start reading the buffer.
7524 bufferWalker
= buffer
;
7528 ULARGE_INTEGER ulOffset
;
7532 * Calculate how many bytes we can copy from this big block.
7534 bytesToReadInBuffer
=
7535 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7537 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7544 /* Not in cache, and we're going to read past the end of the block. */
7545 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7548 StorageImpl_ReadAt(This
->parentStorage
,
7551 bytesToReadInBuffer
,
7556 if (!cachedBlock
->read
)
7559 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7560 return STG_E_READFAULT
;
7562 cachedBlock
->read
= TRUE
;
7565 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7566 bytesReadAt
= bytesToReadInBuffer
;
7569 blockNoInSequence
++;
7570 bufferWalker
+= bytesReadAt
;
7571 size
-= bytesReadAt
;
7572 *bytesRead
+= bytesReadAt
;
7573 offsetInBlock
= 0; /* There is no offset on the next block */
7575 if (bytesToReadInBuffer
!= bytesReadAt
)
7582 /******************************************************************************
7583 * BlockChainStream_WriteAt
7585 * Writes the specified number of bytes to this chain at the specified offset.
7586 * Will fail if not all specified number of bytes have been written.
7588 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7589 ULARGE_INTEGER offset
,
7592 ULONG
* bytesWritten
)
7594 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7595 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7598 const BYTE
* bufferWalker
;
7600 BlockChainBlock
*cachedBlock
;
7603 bufferWalker
= buffer
;
7607 ULARGE_INTEGER ulOffset
;
7608 DWORD bytesWrittenAt
;
7611 * Calculate how many bytes we can copy to this big block.
7614 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7616 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7618 /* BlockChainStream_SetSize should have already been called to ensure we have
7619 * enough blocks in the chain to write into */
7622 ERR("not enough blocks in chain to write data\n");
7628 /* Not in cache, and we're going to write past the end of the block. */
7629 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7632 StorageImpl_WriteAt(This
->parentStorage
,
7640 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7643 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7644 return STG_E_READFAULT
;
7647 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7648 bytesWrittenAt
= bytesToWrite
;
7649 cachedBlock
->read
= TRUE
;
7650 cachedBlock
->dirty
= TRUE
;
7653 blockNoInSequence
++;
7654 bufferWalker
+= bytesWrittenAt
;
7655 size
-= bytesWrittenAt
;
7656 *bytesWritten
+= bytesWrittenAt
;
7657 offsetInBlock
= 0; /* There is no offset on the next block */
7659 if (bytesWrittenAt
!= bytesToWrite
)
7663 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7667 /************************************************************************
7668 * SmallBlockChainStream implementation
7669 ***********************************************************************/
7671 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7672 StorageImpl
* parentStorage
,
7673 ULONG
* headOfStreamPlaceHolder
,
7676 SmallBlockChainStream
* newStream
;
7678 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7680 newStream
->parentStorage
= parentStorage
;
7681 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7682 newStream
->ownerDirEntry
= dirEntry
;
7687 void SmallBlockChainStream_Destroy(
7688 SmallBlockChainStream
* This
)
7690 HeapFree(GetProcessHeap(), 0, This
);
7693 /******************************************************************************
7694 * SmallBlockChainStream_GetHeadOfChain
7696 * Returns the head of this chain of small blocks.
7698 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7699 SmallBlockChainStream
* This
)
7701 DirEntry chainEntry
;
7704 if (This
->headOfStreamPlaceHolder
!= NULL
)
7705 return *(This
->headOfStreamPlaceHolder
);
7707 if (This
->ownerDirEntry
)
7709 hr
= StorageImpl_ReadDirEntry(
7710 This
->parentStorage
,
7711 This
->ownerDirEntry
,
7714 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7715 return chainEntry
.startingBlock
;
7718 return BLOCK_END_OF_CHAIN
;
7721 /******************************************************************************
7722 * SmallBlockChainStream_GetNextBlockInChain
7724 * Returns the index of the next small block in this chain.
7727 * - BLOCK_END_OF_CHAIN: end of this chain
7728 * - BLOCK_UNUSED: small block 'blockIndex' is free
7730 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7731 SmallBlockChainStream
* This
,
7733 ULONG
* nextBlockInChain
)
7735 ULARGE_INTEGER offsetOfBlockInDepot
;
7740 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7742 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7745 * Read those bytes in the buffer from the small block file.
7747 res
= BlockChainStream_ReadAt(
7748 This
->parentStorage
->smallBlockDepotChain
,
7749 offsetOfBlockInDepot
,
7754 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7755 res
= STG_E_READFAULT
;
7759 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7766 /******************************************************************************
7767 * SmallBlockChainStream_SetNextBlockInChain
7769 * Writes the index of the next block of the specified block in the small
7771 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7772 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7774 static void SmallBlockChainStream_SetNextBlockInChain(
7775 SmallBlockChainStream
* This
,
7779 ULARGE_INTEGER offsetOfBlockInDepot
;
7783 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7785 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7788 * Read those bytes in the buffer from the small block file.
7790 BlockChainStream_WriteAt(
7791 This
->parentStorage
->smallBlockDepotChain
,
7792 offsetOfBlockInDepot
,
7798 /******************************************************************************
7799 * SmallBlockChainStream_FreeBlock
7801 * Flag small block 'blockIndex' as free in the small block depot.
7803 static void SmallBlockChainStream_FreeBlock(
7804 SmallBlockChainStream
* This
,
7807 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7810 /******************************************************************************
7811 * SmallBlockChainStream_GetNextFreeBlock
7813 * Returns the index of a free small block. The small block depot will be
7814 * enlarged if necessary. The small block chain will also be enlarged if
7817 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7818 SmallBlockChainStream
* This
)
7820 ULARGE_INTEGER offsetOfBlockInDepot
;
7823 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7824 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7826 ULONG smallBlocksPerBigBlock
;
7828 ULONG blocksRequired
;
7829 ULARGE_INTEGER old_size
, size_required
;
7831 offsetOfBlockInDepot
.u
.HighPart
= 0;
7834 * Scan the small block depot for a free block
7836 while (nextBlockIndex
!= BLOCK_UNUSED
)
7838 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7840 res
= BlockChainStream_ReadAt(
7841 This
->parentStorage
->smallBlockDepotChain
,
7842 offsetOfBlockInDepot
,
7848 * If we run out of space for the small block depot, enlarge it
7850 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7852 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7854 if (nextBlockIndex
!= BLOCK_UNUSED
)
7860 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7862 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7863 ULARGE_INTEGER newSize
, offset
;
7866 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7867 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7870 * Initialize all the small blocks to free
7872 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7873 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7874 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7875 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7877 StorageImpl_SaveFileHeader(This
->parentStorage
);
7881 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7883 smallBlocksPerBigBlock
=
7884 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7887 * Verify if we have to allocate big blocks to contain small blocks
7889 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7891 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7893 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7895 if (size_required
.QuadPart
> old_size
.QuadPart
)
7897 BlockChainStream_SetSize(
7898 This
->parentStorage
->smallBlockRootChain
,
7901 StorageImpl_ReadDirEntry(
7902 This
->parentStorage
,
7903 This
->parentStorage
->base
.storageDirEntry
,
7906 rootEntry
.size
= size_required
;
7908 StorageImpl_WriteDirEntry(
7909 This
->parentStorage
,
7910 This
->parentStorage
->base
.storageDirEntry
,
7917 /******************************************************************************
7918 * SmallBlockChainStream_ReadAt
7920 * Reads a specified number of bytes from this chain at the specified offset.
7921 * bytesRead may be NULL.
7922 * Failure will be returned if the specified number of bytes has not been read.
7924 HRESULT
SmallBlockChainStream_ReadAt(
7925 SmallBlockChainStream
* This
,
7926 ULARGE_INTEGER offset
,
7932 ULARGE_INTEGER offsetInBigBlockFile
;
7933 ULONG blockNoInSequence
=
7934 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7936 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7937 ULONG bytesToReadInBuffer
;
7939 ULONG bytesReadFromBigBlockFile
;
7941 ULARGE_INTEGER stream_size
;
7944 * This should never happen on a small block file.
7946 assert(offset
.u
.HighPart
==0);
7950 stream_size
= SmallBlockChainStream_GetSize(This
);
7951 if (stream_size
.QuadPart
> offset
.QuadPart
)
7952 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7957 * Find the first block in the stream that contains part of the buffer.
7959 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7961 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7963 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7966 blockNoInSequence
--;
7970 * Start reading the buffer.
7972 bufferWalker
= buffer
;
7974 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7977 * Calculate how many bytes we can copy from this small block.
7979 bytesToReadInBuffer
=
7980 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7983 * Calculate the offset of the small block in the small block file.
7985 offsetInBigBlockFile
.QuadPart
=
7986 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7988 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7991 * Read those bytes in the buffer from the small block file.
7992 * The small block has already been identified so it shouldn't fail
7993 * unless the file is corrupt.
7995 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7996 offsetInBigBlockFile
,
7997 bytesToReadInBuffer
,
7999 &bytesReadFromBigBlockFile
);
8004 if (!bytesReadFromBigBlockFile
)
8005 return STG_E_DOCFILECORRUPT
;
8008 * Step to the next big block.
8010 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8012 return STG_E_DOCFILECORRUPT
;
8014 bufferWalker
+= bytesReadFromBigBlockFile
;
8015 size
-= bytesReadFromBigBlockFile
;
8016 *bytesRead
+= bytesReadFromBigBlockFile
;
8017 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8023 /******************************************************************************
8024 * SmallBlockChainStream_WriteAt
8026 * Writes the specified number of bytes to this chain at the specified offset.
8027 * Will fail if not all specified number of bytes have been written.
8029 HRESULT
SmallBlockChainStream_WriteAt(
8030 SmallBlockChainStream
* This
,
8031 ULARGE_INTEGER offset
,
8034 ULONG
* bytesWritten
)
8036 ULARGE_INTEGER offsetInBigBlockFile
;
8037 ULONG blockNoInSequence
=
8038 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8040 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8041 ULONG bytesToWriteInBuffer
;
8043 ULONG bytesWrittenToBigBlockFile
;
8044 const BYTE
* bufferWalker
;
8048 * This should never happen on a small block file.
8050 assert(offset
.u
.HighPart
==0);
8053 * Find the first block in the stream that contains part of the buffer.
8055 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8057 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8059 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8060 return STG_E_DOCFILECORRUPT
;
8061 blockNoInSequence
--;
8065 * Start writing the buffer.
8068 bufferWalker
= buffer
;
8069 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8072 * Calculate how many bytes we can copy to this small block.
8074 bytesToWriteInBuffer
=
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 * Write those bytes in the buffer to the small block file.
8088 res
= BlockChainStream_WriteAt(
8089 This
->parentStorage
->smallBlockRootChain
,
8090 offsetInBigBlockFile
,
8091 bytesToWriteInBuffer
,
8093 &bytesWrittenToBigBlockFile
);
8098 * Step to the next big block.
8100 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8103 bufferWalker
+= bytesWrittenToBigBlockFile
;
8104 size
-= bytesWrittenToBigBlockFile
;
8105 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8106 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8109 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8112 /******************************************************************************
8113 * SmallBlockChainStream_Shrink
8115 * Shrinks this chain in the small block depot.
8117 static BOOL
SmallBlockChainStream_Shrink(
8118 SmallBlockChainStream
* This
,
8119 ULARGE_INTEGER newSize
)
8121 ULONG blockIndex
, extraBlock
;
8125 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8127 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8130 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8133 * Go to the new end of chain
8135 while (count
< numBlocks
)
8137 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8144 * If the count is 0, we have a special case, the head of the chain was
8149 DirEntry chainEntry
;
8151 StorageImpl_ReadDirEntry(This
->parentStorage
,
8152 This
->ownerDirEntry
,
8155 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8157 StorageImpl_WriteDirEntry(This
->parentStorage
,
8158 This
->ownerDirEntry
,
8162 * We start freeing the chain at the head block.
8164 extraBlock
= blockIndex
;
8168 /* Get the next block before marking the new end */
8169 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8173 /* Mark the new end of chain */
8174 SmallBlockChainStream_SetNextBlockInChain(
8177 BLOCK_END_OF_CHAIN
);
8181 * Mark the extra blocks as free
8183 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8185 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8188 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8189 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8190 extraBlock
= blockIndex
;
8196 /******************************************************************************
8197 * SmallBlockChainStream_Enlarge
8199 * Grows this chain in the small block depot.
8201 static BOOL
SmallBlockChainStream_Enlarge(
8202 SmallBlockChainStream
* This
,
8203 ULARGE_INTEGER newSize
)
8205 ULONG blockIndex
, currentBlock
;
8207 ULONG oldNumBlocks
= 0;
8209 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8212 * Empty chain. Create the head.
8214 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8216 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8217 SmallBlockChainStream_SetNextBlockInChain(
8220 BLOCK_END_OF_CHAIN
);
8222 if (This
->headOfStreamPlaceHolder
!= NULL
)
8224 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8228 DirEntry chainEntry
;
8230 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8233 chainEntry
.startingBlock
= blockIndex
;
8235 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8240 currentBlock
= blockIndex
;
8243 * Figure out how many blocks are needed to contain this stream
8245 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8247 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8251 * Go to the current end of chain
8253 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8256 currentBlock
= blockIndex
;
8257 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8262 * Add new blocks to the chain
8264 while (oldNumBlocks
< newNumBlocks
)
8266 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8267 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8269 SmallBlockChainStream_SetNextBlockInChain(
8272 BLOCK_END_OF_CHAIN
);
8274 currentBlock
= blockIndex
;
8281 /******************************************************************************
8282 * SmallBlockChainStream_SetSize
8284 * Sets the size of this stream.
8285 * The file will grow if we grow the chain.
8287 * TODO: Free the actual blocks in the file when we shrink the chain.
8288 * Currently, the blocks are still in the file. So the file size
8289 * doesn't shrink even if we shrink streams.
8291 BOOL
SmallBlockChainStream_SetSize(
8292 SmallBlockChainStream
* This
,
8293 ULARGE_INTEGER newSize
)
8295 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8297 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8300 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8302 SmallBlockChainStream_Shrink(This
, newSize
);
8306 SmallBlockChainStream_Enlarge(This
, newSize
);
8312 /******************************************************************************
8313 * SmallBlockChainStream_GetCount
8315 * Returns the number of small blocks that comprises this chain.
8316 * This is not the size of the stream as the last block may not be full!
8319 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8324 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8326 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8330 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8331 blockIndex
, &blockIndex
)))
8338 /******************************************************************************
8339 * SmallBlockChainStream_GetSize
8341 * Returns the size of this chain.
8343 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8345 DirEntry chainEntry
;
8347 if(This
->headOfStreamPlaceHolder
!= NULL
)
8349 ULARGE_INTEGER result
;
8350 result
.u
.HighPart
= 0;
8352 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8353 This
->parentStorage
->smallBlockSize
;
8358 StorageImpl_ReadDirEntry(
8359 This
->parentStorage
,
8360 This
->ownerDirEntry
,
8363 return chainEntry
.size
;
8367 /************************************************************************
8368 * Miscellaneous storage functions
8369 ***********************************************************************/
8371 static HRESULT
create_storagefile(
8375 STGOPTIONS
* pStgOptions
,
8379 StorageBaseImpl
* newStorage
= 0;
8380 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8381 HRESULT hr
= STG_E_INVALIDFLAG
;
8385 DWORD fileAttributes
;
8386 WCHAR tempFileName
[MAX_PATH
];
8389 return STG_E_INVALIDPOINTER
;
8391 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8392 return STG_E_INVALIDPARAMETER
;
8394 /* if no share mode given then DENY_NONE is the default */
8395 if (STGM_SHARE_MODE(grfMode
) == 0)
8396 grfMode
|= STGM_SHARE_DENY_NONE
;
8398 if ( FAILED( validateSTGM(grfMode
) ))
8401 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8402 switch(STGM_ACCESS_MODE(grfMode
))
8405 case STGM_READWRITE
:
8411 /* in direct mode, can only use SHARE_EXCLUSIVE */
8412 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8415 /* but in transacted mode, any share mode is valid */
8418 * Generate a unique name.
8422 WCHAR tempPath
[MAX_PATH
];
8423 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8425 memset(tempPath
, 0, sizeof(tempPath
));
8426 memset(tempFileName
, 0, sizeof(tempFileName
));
8428 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8431 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8432 pwcsName
= tempFileName
;
8435 hr
= STG_E_INSUFFICIENTMEMORY
;
8439 creationMode
= TRUNCATE_EXISTING
;
8443 creationMode
= GetCreationModeFromSTGM(grfMode
);
8447 * Interpret the STGM value grfMode
8449 shareMode
= GetShareModeFromSTGM(grfMode
);
8450 accessMode
= GetAccessModeFromSTGM(grfMode
);
8452 if (grfMode
& STGM_DELETEONRELEASE
)
8453 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8455 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8459 hFile
= CreateFileW(pwcsName
,
8467 if (hFile
== INVALID_HANDLE_VALUE
)
8469 if(GetLastError() == ERROR_FILE_EXISTS
)
8470 hr
= STG_E_FILEALREADYEXISTS
;
8477 * Allocate and initialize the new IStorage object.
8479 hr
= Storage_Construct(
8486 pStgOptions
->ulSectorSize
,
8494 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8495 IStorage_Release(&newStorage
->IStorage_iface
);
8498 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8503 /******************************************************************************
8504 * StgCreateDocfile [OLE32.@]
8505 * Creates a new compound file storage object
8508 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8509 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8510 * reserved [ ?] unused?, usually 0
8511 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8514 * S_OK if the file was successfully created
8515 * some STG_E_ value if error
8517 * if pwcsName is NULL, create file with new unique name
8518 * the function can returns
8519 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8522 HRESULT WINAPI
StgCreateDocfile(
8526 IStorage
**ppstgOpen
)
8528 STGOPTIONS stgoptions
= {1, 0, 512};
8530 TRACE("(%s, %x, %d, %p)\n",
8531 debugstr_w(pwcsName
), grfMode
,
8532 reserved
, ppstgOpen
);
8535 return STG_E_INVALIDPOINTER
;
8537 return STG_E_INVALIDPARAMETER
;
8539 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8542 /******************************************************************************
8543 * StgCreateStorageEx [OLE32.@]
8545 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8547 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8548 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8550 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8552 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8553 return STG_E_INVALIDPARAMETER
;
8556 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8558 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8559 return STG_E_INVALIDPARAMETER
;
8562 if (stgfmt
== STGFMT_FILE
)
8564 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8565 return STG_E_INVALIDPARAMETER
;
8568 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8570 STGOPTIONS defaultOptions
= {1, 0, 512};
8572 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8573 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8577 ERR("Invalid stgfmt argument\n");
8578 return STG_E_INVALIDPARAMETER
;
8581 /******************************************************************************
8582 * StgCreatePropSetStg [OLE32.@]
8584 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8585 IPropertySetStorage
**propset
)
8587 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8589 return STG_E_INVALIDPARAMETER
;
8591 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8594 /******************************************************************************
8595 * StgOpenStorageEx [OLE32.@]
8597 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8599 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8600 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8602 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8604 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8605 return STG_E_INVALIDPARAMETER
;
8611 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8612 return STG_E_INVALIDPARAMETER
;
8614 case STGFMT_STORAGE
:
8617 case STGFMT_DOCFILE
:
8618 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8620 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8621 return STG_E_INVALIDPARAMETER
;
8623 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8627 WARN("STGFMT_ANY assuming storage\n");
8631 return STG_E_INVALIDPARAMETER
;
8634 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8638 /******************************************************************************
8639 * StgOpenStorage [OLE32.@]
8641 HRESULT WINAPI
StgOpenStorage(
8642 const OLECHAR
*pwcsName
,
8643 IStorage
*pstgPriority
,
8647 IStorage
**ppstgOpen
)
8649 StorageBaseImpl
* newStorage
= 0;
8654 LPWSTR temp_name
= NULL
;
8656 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8657 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8658 snbExclude
, reserved
, ppstgOpen
);
8662 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8663 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8664 if (FAILED(hr
)) goto end
;
8665 pwcsName
= temp_name
;
8666 TRACE("using filename %s\n", debugstr_w(temp_name
));
8671 hr
= STG_E_INVALIDNAME
;
8677 hr
= STG_E_INVALIDPOINTER
;
8683 hr
= STG_E_INVALIDPARAMETER
;
8687 if (grfMode
& STGM_PRIORITY
)
8689 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8690 return STG_E_INVALIDFLAG
;
8691 if (grfMode
& STGM_DELETEONRELEASE
)
8692 return STG_E_INVALIDFUNCTION
;
8693 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8694 return STG_E_INVALIDFLAG
;
8695 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8696 grfMode
|= STGM_SHARE_DENY_NONE
;
8700 * Validate the sharing mode
8702 if (grfMode
& STGM_DIRECT_SWMR
)
8704 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8705 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8707 hr
= STG_E_INVALIDFLAG
;
8711 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8712 switch(STGM_SHARE_MODE(grfMode
))
8714 case STGM_SHARE_EXCLUSIVE
:
8715 case STGM_SHARE_DENY_WRITE
:
8718 hr
= STG_E_INVALIDFLAG
;
8722 if ( FAILED( validateSTGM(grfMode
) ) ||
8723 (grfMode
&STGM_CREATE
))
8725 hr
= STG_E_INVALIDFLAG
;
8729 /* shared reading requires transacted or single writer mode */
8730 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8731 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8732 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8734 hr
= STG_E_INVALIDFLAG
;
8739 * Interpret the STGM value grfMode
8741 shareMode
= GetShareModeFromSTGM(grfMode
);
8742 accessMode
= GetAccessModeFromSTGM(grfMode
);
8746 hFile
= CreateFileW( pwcsName
,
8751 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8754 if (hFile
==INVALID_HANDLE_VALUE
)
8756 DWORD last_error
= GetLastError();
8762 case ERROR_FILE_NOT_FOUND
:
8763 hr
= STG_E_FILENOTFOUND
;
8766 case ERROR_PATH_NOT_FOUND
:
8767 hr
= STG_E_PATHNOTFOUND
;
8770 case ERROR_ACCESS_DENIED
:
8771 case ERROR_WRITE_PROTECT
:
8772 hr
= STG_E_ACCESSDENIED
;
8775 case ERROR_SHARING_VIOLATION
:
8776 hr
= STG_E_SHAREVIOLATION
;
8787 * Refuse to open the file if it's too small to be a structured storage file
8788 * FIXME: verify the file when reading instead of here
8790 if (GetFileSize(hFile
, NULL
) < 0x100)
8793 hr
= STG_E_FILEALREADYEXISTS
;
8798 * Allocate and initialize the new IStorage object.
8800 hr
= Storage_Construct(
8813 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8815 if(hr
== STG_E_INVALIDHEADER
)
8816 hr
= STG_E_FILEALREADYEXISTS
;
8820 *ppstgOpen
= &newStorage
->IStorage_iface
;
8823 CoTaskMemFree(temp_name
);
8824 if (pstgPriority
) IStorage_Release(pstgPriority
);
8825 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8829 /******************************************************************************
8830 * StgCreateDocfileOnILockBytes [OLE32.@]
8832 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8836 IStorage
** ppstgOpen
)
8838 StorageBaseImpl
* newStorage
= 0;
8841 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8842 return STG_E_INVALIDPOINTER
;
8845 * Allocate and initialize the new IStorage object.
8847 hr
= Storage_Construct(
8862 *ppstgOpen
= &newStorage
->IStorage_iface
;
8867 /******************************************************************************
8868 * StgOpenStorageOnILockBytes [OLE32.@]
8870 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8872 IStorage
*pstgPriority
,
8876 IStorage
**ppstgOpen
)
8878 StorageBaseImpl
* newStorage
= 0;
8881 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8882 return STG_E_INVALIDPOINTER
;
8884 if ( FAILED( validateSTGM(grfMode
) ))
8885 return STG_E_INVALIDFLAG
;
8890 * Allocate and initialize the new IStorage object.
8892 hr
= Storage_Construct(
8907 *ppstgOpen
= &newStorage
->IStorage_iface
;
8912 /******************************************************************************
8913 * StgSetTimes [ole32.@]
8914 * StgSetTimes [OLE32.@]
8918 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8919 FILETIME
const *patime
, FILETIME
const *pmtime
)
8921 IStorage
*stg
= NULL
;
8924 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8926 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8930 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8931 IStorage_Release(stg
);
8937 /******************************************************************************
8938 * StgIsStorageILockBytes [OLE32.@]
8940 * Determines if the ILockBytes contains a storage object.
8942 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8944 BYTE sig
[sizeof(STORAGE_magic
)];
8945 ULARGE_INTEGER offset
;
8948 offset
.u
.HighPart
= 0;
8949 offset
.u
.LowPart
= 0;
8951 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8953 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8959 /******************************************************************************
8960 * WriteClassStg [OLE32.@]
8962 * This method will store the specified CLSID in the specified storage object
8964 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8967 return E_INVALIDARG
;
8970 return STG_E_INVALIDPOINTER
;
8972 return IStorage_SetClass(pStg
, rclsid
);
8975 /***********************************************************************
8976 * ReadClassStg (OLE32.@)
8978 * This method reads the CLSID previously written to a storage object with
8979 * the WriteClassStg.
8982 * pstg [I] IStorage pointer
8983 * pclsid [O] Pointer to where the CLSID is written
8987 * Failure: HRESULT code.
8989 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8994 TRACE("(%p, %p)\n", pstg
, pclsid
);
8996 if(!pstg
|| !pclsid
)
8997 return E_INVALIDARG
;
9000 * read a STATSTG structure (contains the clsid) from the storage
9002 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9005 *pclsid
=pstatstg
.clsid
;
9010 /***********************************************************************
9011 * OleLoadFromStream (OLE32.@)
9013 * This function loads an object from stream
9015 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9019 LPPERSISTSTREAM xstm
;
9021 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9023 res
=ReadClassStm(pStm
,&clsid
);
9026 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9029 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9031 IUnknown_Release((IUnknown
*)*ppvObj
);
9034 res
=IPersistStream_Load(xstm
,pStm
);
9035 IPersistStream_Release(xstm
);
9036 /* FIXME: all refcounts ok at this point? I think they should be:
9039 * xstm : 0 (released)
9044 /***********************************************************************
9045 * OleSaveToStream (OLE32.@)
9047 * This function saves an object with the IPersistStream interface on it
9048 * to the specified stream.
9050 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9056 TRACE("(%p,%p)\n",pPStm
,pStm
);
9058 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9060 if (SUCCEEDED(res
)){
9062 res
=WriteClassStm(pStm
,&clsid
);
9066 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9069 TRACE("Finished Save\n");
9073 /*************************************************************************
9074 * STORAGE_CreateOleStream [Internal]
9076 * Creates the "\001OLE" stream in the IStorage if necessary.
9079 * storage [I] Dest storage to create the stream in
9080 * flags [I] flags to be set for newly created stream
9083 * HRESULT return value
9087 * This stream is still unknown, MS Word seems to have extra data
9088 * but since the data is stored in the OLESTREAM there should be
9089 * no need to recreate the stream. If the stream is manually
9090 * deleted it will create it with this default data.
9093 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9095 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9096 static const DWORD version_magic
= 0x02000001;
9100 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9103 struct empty_1ole_stream
{
9104 DWORD version_magic
;
9106 DWORD update_options
;
9108 DWORD mon_stream_size
;
9110 struct empty_1ole_stream stream_data
;
9112 stream_data
.version_magic
= version_magic
;
9113 stream_data
.flags
= flags
;
9114 stream_data
.update_options
= 0;
9115 stream_data
.reserved
= 0;
9116 stream_data
.mon_stream_size
= 0;
9118 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9119 IStream_Release(stream
);
9125 /* write a string to a stream, preceded by its length */
9126 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9133 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9134 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9139 str
= CoTaskMemAlloc( len
);
9140 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9141 r
= IStream_Write( stm
, str
, len
, NULL
);
9142 CoTaskMemFree( str
);
9146 /* read a string preceded by its length from a stream */
9147 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9150 DWORD len
, count
= 0;
9154 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9157 if( count
!= sizeof(len
) )
9158 return E_OUTOFMEMORY
;
9160 TRACE("%d bytes\n",len
);
9162 str
= CoTaskMemAlloc( len
);
9164 return E_OUTOFMEMORY
;
9166 r
= IStream_Read( stm
, str
, len
, &count
);
9171 CoTaskMemFree( str
);
9172 return E_OUTOFMEMORY
;
9175 TRACE("Read string %s\n",debugstr_an(str
,len
));
9177 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9178 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9181 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9184 CoTaskMemFree( str
);
9192 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9193 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9197 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9199 static const BYTE unknown1
[12] =
9200 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9201 0xFF, 0xFF, 0xFF, 0xFF};
9202 static const BYTE unknown2
[16] =
9203 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9206 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9207 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9208 debugstr_w(szProgIDName
));
9210 /* Create a CompObj stream */
9211 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9212 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9216 /* Write CompObj Structure to stream */
9217 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9219 if( SUCCEEDED( r
) )
9220 r
= WriteClassStm( pstm
, clsid
);
9222 if( SUCCEEDED( r
) )
9223 r
= STREAM_WriteString( pstm
, lpszUserType
);
9224 if( SUCCEEDED( r
) )
9225 r
= STREAM_WriteString( pstm
, szClipName
);
9226 if( SUCCEEDED( r
) )
9227 r
= STREAM_WriteString( pstm
, szProgIDName
);
9228 if( SUCCEEDED( r
) )
9229 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9231 IStream_Release( pstm
);
9236 /***********************************************************************
9237 * WriteFmtUserTypeStg (OLE32.@)
9239 HRESULT WINAPI
WriteFmtUserTypeStg(
9240 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9244 WCHAR szwClipName
[0x40];
9246 LPWSTR wstrProgID
= NULL
;
9249 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9251 /* get the clipboard format name */
9254 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9255 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9259 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9261 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9267 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9269 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9271 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9272 cf
? szwClipName
: NULL
, wstrProgID
);
9274 CoTaskMemFree(wstrProgID
);
9280 /******************************************************************************
9281 * ReadFmtUserTypeStg [OLE32.@]
9283 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9287 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9288 unsigned char unknown1
[12];
9289 unsigned char unknown2
[16];
9291 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9294 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9296 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9297 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9300 WARN("Failed to open stream r = %08x\n", r
);
9304 /* read the various parts of the structure */
9305 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9306 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9308 r
= ReadClassStm( stm
, &clsid
);
9312 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9316 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9320 r
= STREAM_ReadString( stm
, &szProgIDName
);
9324 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9325 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9328 /* ok, success... now we just need to store what we found */
9330 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9332 if( lplpszUserType
)
9334 *lplpszUserType
= szCLSIDName
;
9339 CoTaskMemFree( szCLSIDName
);
9340 CoTaskMemFree( szOleTypeName
);
9341 CoTaskMemFree( szProgIDName
);
9342 IStream_Release( stm
);
9347 /******************************************************************************
9348 * StgIsStorageFile [OLE32.@]
9349 * Verify if the file contains a storage object
9355 * S_OK if file has magic bytes as a storage object
9356 * S_FALSE if file is not storage
9359 StgIsStorageFile(LPCOLESTR fn
)
9365 TRACE("%s\n", debugstr_w(fn
));
9366 hf
= CreateFileW(fn
, GENERIC_READ
,
9367 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9368 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9370 if (hf
== INVALID_HANDLE_VALUE
)
9371 return STG_E_FILENOTFOUND
;
9373 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9375 WARN(" unable to read file\n");
9382 if (bytes_read
!= 8) {
9383 TRACE(" too short\n");
9387 if (!memcmp(magic
,STORAGE_magic
,8)) {
9392 TRACE(" -> Invalid header.\n");
9396 /***********************************************************************
9397 * WriteClassStm (OLE32.@)
9399 * Writes a CLSID to a stream.
9402 * pStm [I] Stream to write to.
9403 * rclsid [I] CLSID to write.
9407 * Failure: HRESULT code.
9409 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9411 TRACE("(%p,%p)\n",pStm
,rclsid
);
9413 if (!pStm
|| !rclsid
)
9414 return E_INVALIDARG
;
9416 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9419 /***********************************************************************
9420 * ReadClassStm (OLE32.@)
9422 * Reads a CLSID from a stream.
9425 * pStm [I] Stream to read from.
9426 * rclsid [O] CLSID to read.
9430 * Failure: HRESULT code.
9432 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9437 TRACE("(%p,%p)\n",pStm
,pclsid
);
9439 if (!pStm
|| !pclsid
)
9440 return E_INVALIDARG
;
9442 /* clear the output args */
9443 *pclsid
= CLSID_NULL
;
9445 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9450 if (nbByte
!= sizeof(CLSID
))
9451 return STG_E_READFAULT
;
9457 /************************************************************************
9458 * OleConvert Functions
9459 ***********************************************************************/
9461 #define OLESTREAM_ID 0x501
9462 #define OLESTREAM_MAX_STR_LEN 255
9464 /* OLESTREAM memory structure to use for Get and Put Routines */
9469 DWORD dwOleTypeNameLength
;
9470 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9471 CHAR
*pstrOleObjFileName
;
9472 DWORD dwOleObjFileNameLength
;
9473 DWORD dwMetaFileWidth
;
9474 DWORD dwMetaFileHeight
;
9475 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9478 } OLECONVERT_OLESTREAM_DATA
;
9480 /* CompObj Stream structure */
9483 BYTE byUnknown1
[12];
9485 DWORD dwCLSIDNameLength
;
9486 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9487 DWORD dwOleTypeNameLength
;
9488 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9489 DWORD dwProgIDNameLength
;
9490 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9491 BYTE byUnknown2
[16];
9492 } OLECONVERT_ISTORAGE_COMPOBJ
;
9494 /* Ole Presentation Stream structure */
9497 BYTE byUnknown1
[28];
9502 } OLECONVERT_ISTORAGE_OLEPRES
;
9505 /*************************************************************************
9506 * OLECONVERT_LoadOLE10 [Internal]
9508 * Loads the OLE10 STREAM to memory
9511 * pOleStream [I] The OLESTREAM
9512 * pData [I] Data Structure for the OLESTREAM Data
9516 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9517 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9520 * This function is used by OleConvertOLESTREAMToIStorage only.
9522 * Memory allocated for pData must be freed by the caller
9524 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9527 HRESULT hRes
= S_OK
;
9531 pData
->pData
= NULL
;
9532 pData
->pstrOleObjFileName
= NULL
;
9534 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9537 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9538 if(dwSize
!= sizeof(pData
->dwOleID
))
9540 hRes
= CONVERT10_E_OLESTREAM_GET
;
9542 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9544 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9555 /* Get the TypeID... more info needed for this field */
9556 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9557 if(dwSize
!= sizeof(pData
->dwTypeID
))
9559 hRes
= CONVERT10_E_OLESTREAM_GET
;
9564 if(pData
->dwTypeID
!= 0)
9566 /* Get the length of the OleTypeName */
9567 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9568 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9570 hRes
= CONVERT10_E_OLESTREAM_GET
;
9575 if(pData
->dwOleTypeNameLength
> 0)
9577 /* Get the OleTypeName */
9578 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9579 if(dwSize
!= pData
->dwOleTypeNameLength
)
9581 hRes
= CONVERT10_E_OLESTREAM_GET
;
9587 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9588 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9590 hRes
= CONVERT10_E_OLESTREAM_GET
;
9594 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9595 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9596 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9597 if(pData
->pstrOleObjFileName
)
9599 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9600 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9602 hRes
= CONVERT10_E_OLESTREAM_GET
;
9606 hRes
= CONVERT10_E_OLESTREAM_GET
;
9611 /* Get the Width of the Metafile */
9612 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9613 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9615 hRes
= CONVERT10_E_OLESTREAM_GET
;
9619 /* Get the Height of the Metafile */
9620 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9621 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9623 hRes
= CONVERT10_E_OLESTREAM_GET
;
9629 /* Get the Length of the Data */
9630 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9631 if(dwSize
!= sizeof(pData
->dwDataLength
))
9633 hRes
= CONVERT10_E_OLESTREAM_GET
;
9637 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9639 if(!bStrem1
) /* if it is a second OLE stream data */
9641 pData
->dwDataLength
-= 8;
9642 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9643 if(dwSize
!= sizeof(pData
->strUnknown
))
9645 hRes
= CONVERT10_E_OLESTREAM_GET
;
9651 if(pData
->dwDataLength
> 0)
9653 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9655 /* Get Data (ex. IStorage, Metafile, or BMP) */
9658 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9659 if(dwSize
!= pData
->dwDataLength
)
9661 hRes
= CONVERT10_E_OLESTREAM_GET
;
9666 hRes
= CONVERT10_E_OLESTREAM_GET
;
9675 /*************************************************************************
9676 * OLECONVERT_SaveOLE10 [Internal]
9678 * Saves the OLE10 STREAM From memory
9681 * pData [I] Data Structure for the OLESTREAM Data
9682 * pOleStream [I] The OLESTREAM to save
9686 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9689 * This function is used by OleConvertIStorageToOLESTREAM only.
9692 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9695 HRESULT hRes
= S_OK
;
9699 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9700 if(dwSize
!= sizeof(pData
->dwOleID
))
9702 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9707 /* Set the TypeID */
9708 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9709 if(dwSize
!= sizeof(pData
->dwTypeID
))
9711 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9715 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9717 /* Set the Length of the OleTypeName */
9718 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9719 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9721 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9726 if(pData
->dwOleTypeNameLength
> 0)
9728 /* Set the OleTypeName */
9729 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9730 if(dwSize
!= pData
->dwOleTypeNameLength
)
9732 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9739 /* Set the width of the Metafile */
9740 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9741 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9743 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9749 /* Set the height of the Metafile */
9750 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9751 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9753 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9759 /* Set the length of the Data */
9760 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9761 if(dwSize
!= sizeof(pData
->dwDataLength
))
9763 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9769 if(pData
->dwDataLength
> 0)
9771 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9772 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9773 if(dwSize
!= pData
->dwDataLength
)
9775 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9783 /*************************************************************************
9784 * OLECONVERT_GetOLE20FromOLE10[Internal]
9786 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9787 * opens it, and copies the content to the dest IStorage for
9788 * OleConvertOLESTREAMToIStorage
9792 * pDestStorage [I] The IStorage to copy the data to
9793 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9794 * nBufferLength [I] The size of the buffer
9803 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9807 IStorage
*pTempStorage
;
9808 DWORD dwNumOfBytesWritten
;
9809 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9810 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9812 /* Create a temp File */
9813 GetTempPathW(MAX_PATH
, wstrTempDir
);
9814 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9815 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9817 if(hFile
!= INVALID_HANDLE_VALUE
)
9819 /* Write IStorage Data to File */
9820 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9823 /* Open and copy temp storage to the Dest Storage */
9824 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9827 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9828 IStorage_Release(pTempStorage
);
9830 DeleteFileW(wstrTempFile
);
9835 /*************************************************************************
9836 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9838 * Saves the OLE10 STREAM From memory
9841 * pStorage [I] The Src IStorage to copy
9842 * pData [I] The Dest Memory to write to.
9845 * The size in bytes allocated for pData
9848 * Memory allocated for pData must be freed by the caller
9850 * Used by OleConvertIStorageToOLESTREAM only.
9853 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9857 DWORD nDataLength
= 0;
9858 IStorage
*pTempStorage
;
9859 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9860 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9864 /* Create temp Storage */
9865 GetTempPathW(MAX_PATH
, wstrTempDir
);
9866 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9867 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9871 /* Copy Src Storage to the Temp Storage */
9872 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9873 IStorage_Release(pTempStorage
);
9875 /* Open Temp Storage as a file and copy to memory */
9876 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9877 if(hFile
!= INVALID_HANDLE_VALUE
)
9879 nDataLength
= GetFileSize(hFile
, NULL
);
9880 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9881 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9884 DeleteFileW(wstrTempFile
);
9889 /*************************************************************************
9890 * OLECONVERT_CreateCompObjStream [Internal]
9892 * Creates a "\001CompObj" is the destination IStorage if necessary.
9895 * pStorage [I] The dest IStorage to create the CompObj Stream
9897 * strOleTypeName [I] The ProgID
9901 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9904 * This function is used by OleConvertOLESTREAMToIStorage only.
9906 * The stream data is stored in the OLESTREAM and there should be
9907 * no need to recreate the stream. If the stream is manually
9908 * deleted it will attempt to create it by querying the registry.
9912 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9915 HRESULT hStorageRes
, hRes
= S_OK
;
9916 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9917 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9918 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9920 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9921 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9923 /* Initialize the CompObj structure */
9924 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9925 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9926 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9929 /* Create a CompObj stream if it doesn't exist */
9930 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9931 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9932 if(hStorageRes
== S_OK
)
9934 /* copy the OleTypeName to the compobj struct */
9935 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9936 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9938 /* copy the OleTypeName to the compobj struct */
9939 /* Note: in the test made, these were Identical */
9940 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9941 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9944 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9945 bufferW
, OLESTREAM_MAX_STR_LEN
);
9946 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9952 /* Get the CLSID Default Name from the Registry */
9953 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9954 if(hErr
== ERROR_SUCCESS
)
9956 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9957 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9958 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9959 if(hErr
== ERROR_SUCCESS
)
9961 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9967 /* Write CompObj Structure to stream */
9968 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9970 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9972 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9973 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9975 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9977 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9978 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9980 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9982 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9983 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9985 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9987 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9988 IStream_Release(pStream
);
9994 /*************************************************************************
9995 * OLECONVERT_CreateOlePresStream[Internal]
9997 * Creates the "\002OlePres000" Stream with the Metafile data
10000 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10001 * dwExtentX [I] Width of the Metafile
10002 * dwExtentY [I] Height of the Metafile
10003 * pData [I] Metafile data
10004 * dwDataLength [I] Size of the Metafile data
10008 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10011 * This function is used by OleConvertOLESTREAMToIStorage only.
10014 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10018 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10019 BYTE pOlePresStreamHeader
[] =
10021 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10022 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10023 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10024 0x00, 0x00, 0x00, 0x00
10027 BYTE pOlePresStreamHeaderEmpty
[] =
10029 0x00, 0x00, 0x00, 0x00,
10030 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10031 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10032 0x00, 0x00, 0x00, 0x00
10035 /* Create the OlePres000 Stream */
10036 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10037 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10042 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10044 memset(&OlePres
, 0, sizeof(OlePres
));
10045 /* Do we have any metafile data to save */
10046 if(dwDataLength
> 0)
10048 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10049 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10053 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10054 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10056 /* Set width and height of the metafile */
10057 OlePres
.dwExtentX
= dwExtentX
;
10058 OlePres
.dwExtentY
= -dwExtentY
;
10060 /* Set Data and Length */
10061 if(dwDataLength
> sizeof(METAFILEPICT16
))
10063 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10064 OlePres
.pData
= &(pData
[8]);
10066 /* Save OlePres000 Data to Stream */
10067 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10068 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10069 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10070 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10071 if(OlePres
.dwSize
> 0)
10073 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10075 IStream_Release(pStream
);
10079 /*************************************************************************
10080 * OLECONVERT_CreateOle10NativeStream [Internal]
10082 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10085 * pStorage [I] Dest storage to create the stream in
10086 * pData [I] Ole10 Native Data (ex. bmp)
10087 * dwDataLength [I] Size of the Ole10 Native Data
10093 * This function is used by OleConvertOLESTREAMToIStorage only.
10095 * Might need to verify the data and return appropriate error message
10098 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10102 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10104 /* Create the Ole10Native Stream */
10105 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10106 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10110 /* Write info to stream */
10111 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10112 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10113 IStream_Release(pStream
);
10118 /*************************************************************************
10119 * OLECONVERT_GetOLE10ProgID [Internal]
10121 * Finds the ProgID (or OleTypeID) from the IStorage
10124 * pStorage [I] The Src IStorage to get the ProgID
10125 * strProgID [I] the ProgID string to get
10126 * dwSize [I] the size of the string
10130 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10133 * This function is used by OleConvertIStorageToOLESTREAM only.
10137 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10141 LARGE_INTEGER iSeekPos
;
10142 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10143 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10145 /* Open the CompObj Stream */
10146 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10147 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10151 /*Get the OleType from the CompObj Stream */
10152 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10153 iSeekPos
.u
.HighPart
= 0;
10155 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10156 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10157 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10158 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10159 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10160 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10161 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10163 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10166 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10168 IStream_Release(pStream
);
10173 LPOLESTR wstrProgID
;
10175 /* Get the OleType from the registry */
10176 REFCLSID clsid
= &(stat
.clsid
);
10177 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10178 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10181 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10182 CoTaskMemFree(wstrProgID
);
10189 /*************************************************************************
10190 * OLECONVERT_GetOle10PresData [Internal]
10192 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10195 * pStorage [I] Src IStroage
10196 * pOleStream [I] Dest OleStream Mem Struct
10202 * This function is used by OleConvertIStorageToOLESTREAM only.
10204 * Memory allocated for pData must be freed by the caller
10208 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10213 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10215 /* Initialize Default data for OLESTREAM */
10216 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10217 pOleStreamData
[0].dwTypeID
= 2;
10218 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10219 pOleStreamData
[1].dwTypeID
= 0;
10220 pOleStreamData
[0].dwMetaFileWidth
= 0;
10221 pOleStreamData
[0].dwMetaFileHeight
= 0;
10222 pOleStreamData
[0].pData
= NULL
;
10223 pOleStreamData
[1].pData
= NULL
;
10225 /* Open Ole10Native Stream */
10226 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10227 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10231 /* Read Size and Data */
10232 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10233 if(pOleStreamData
->dwDataLength
> 0)
10235 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10236 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10238 IStream_Release(pStream
);
10244 /*************************************************************************
10245 * OLECONVERT_GetOle20PresData[Internal]
10247 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10250 * pStorage [I] Src IStroage
10251 * pOleStreamData [I] Dest OleStream Mem Struct
10257 * This function is used by OleConvertIStorageToOLESTREAM only.
10259 * Memory allocated for pData must be freed by the caller
10261 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10265 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10266 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10268 /* Initialize Default data for OLESTREAM */
10269 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10270 pOleStreamData
[0].dwTypeID
= 2;
10271 pOleStreamData
[0].dwMetaFileWidth
= 0;
10272 pOleStreamData
[0].dwMetaFileHeight
= 0;
10273 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10274 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10275 pOleStreamData
[1].dwTypeID
= 0;
10276 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10277 pOleStreamData
[1].strOleTypeName
[0] = 0;
10278 pOleStreamData
[1].dwMetaFileWidth
= 0;
10279 pOleStreamData
[1].dwMetaFileHeight
= 0;
10280 pOleStreamData
[1].pData
= NULL
;
10281 pOleStreamData
[1].dwDataLength
= 0;
10284 /* Open OlePress000 stream */
10285 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10286 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10289 LARGE_INTEGER iSeekPos
;
10290 METAFILEPICT16 MetaFilePict
;
10291 static const char strMetafilePictName
[] = "METAFILEPICT";
10293 /* Set the TypeID for a Metafile */
10294 pOleStreamData
[1].dwTypeID
= 5;
10296 /* Set the OleTypeName to Metafile */
10297 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10298 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10300 iSeekPos
.u
.HighPart
= 0;
10301 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10303 /* Get Presentation Data */
10304 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10305 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10306 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10307 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10309 /*Set width and Height */
10310 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10311 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10312 if(olePress
.dwSize
> 0)
10315 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10317 /* Set MetaFilePict struct */
10318 MetaFilePict
.mm
= 8;
10319 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10320 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10321 MetaFilePict
.hMF
= 0;
10323 /* Get Metafile Data */
10324 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10325 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10326 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10328 IStream_Release(pStream
);
10332 /*************************************************************************
10333 * OleConvertOLESTREAMToIStorage [OLE32.@]
10335 * Read info on MSDN
10338 * DVTARGETDEVICE parameter is not handled
10339 * Still unsure of some mem fields for OLE 10 Stream
10340 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10341 * and "\001OLE" streams
10344 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10345 LPOLESTREAM pOleStream
,
10347 const DVTARGETDEVICE
* ptd
)
10351 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10353 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10355 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10359 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10362 if(pstg
== NULL
|| pOleStream
== NULL
)
10364 hRes
= E_INVALIDARG
;
10369 /* Load the OLESTREAM to Memory */
10370 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10375 /* Load the OLESTREAM to Memory (part 2)*/
10376 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10382 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10384 /* Do we have the IStorage Data in the OLESTREAM */
10385 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10387 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10388 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10392 /* It must be an original OLE 1.0 source */
10393 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10398 /* It must be an original OLE 1.0 source */
10399 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10402 /* Create CompObj Stream if necessary */
10403 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10406 /*Create the Ole Stream if necessary */
10407 STORAGE_CreateOleStream(pstg
, 0);
10412 /* Free allocated memory */
10413 for(i
=0; i
< 2; i
++)
10415 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10416 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10417 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10422 /*************************************************************************
10423 * OleConvertIStorageToOLESTREAM [OLE32.@]
10425 * Read info on MSDN
10427 * Read info on MSDN
10430 * Still unsure of some mem fields for OLE 10 Stream
10431 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10432 * and "\001OLE" streams.
10435 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10437 LPOLESTREAM pOleStream
)
10440 HRESULT hRes
= S_OK
;
10442 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10443 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10445 TRACE("%p %p\n", pstg
, pOleStream
);
10447 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10449 if(pstg
== NULL
|| pOleStream
== NULL
)
10451 hRes
= E_INVALIDARG
;
10455 /* Get the ProgID */
10456 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10457 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10461 /* Was it originally Ole10 */
10462 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10465 IStream_Release(pStream
);
10466 /* Get Presentation Data for Ole10Native */
10467 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10471 /* Get Presentation Data (OLE20) */
10472 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10475 /* Save OLESTREAM */
10476 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10479 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10484 /* Free allocated memory */
10485 for(i
=0; i
< 2; i
++)
10487 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10493 enum stream_1ole_flags
{
10494 OleStream_LinkedObject
= 0x00000001,
10495 OleStream_Convert
= 0x00000004
10498 /***********************************************************************
10499 * GetConvertStg (OLE32.@)
10501 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10503 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10504 static const DWORD version_magic
= 0x02000001;
10509 TRACE("%p\n", stg
);
10511 if (!stg
) return E_INVALIDARG
;
10513 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10514 if (FAILED(hr
)) return hr
;
10516 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10517 IStream_Release(stream
);
10518 if (FAILED(hr
)) return hr
;
10520 if (header
[0] != version_magic
)
10522 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10526 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10529 /***********************************************************************
10530 * SetConvertStg (OLE32.@)
10532 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10534 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10535 DWORD flags
= convert
? OleStream_Convert
: 0;
10540 TRACE("(%p, %d)\n", storage
, convert
);
10542 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10545 if (hr
!= STG_E_FILENOTFOUND
)
10548 return STORAGE_CreateOleStream(storage
, flags
);
10551 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10554 IStream_Release(stream
);
10558 /* update flag if differs */
10559 if ((header
[1] ^ flags
) & OleStream_Convert
)
10561 LARGE_INTEGER pos
= {{0}};
10563 if (header
[1] & OleStream_Convert
)
10564 flags
= header
[1] & ~OleStream_Convert
;
10566 flags
= header
[1] | OleStream_Convert
;
10568 pos
.QuadPart
= sizeof(DWORD
);
10569 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10572 IStream_Release(stream
);
10576 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10579 IStream_Release(stream
);