2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
46 #include "wine/debug.h"
48 #include "storage32.h"
49 #include "ole2.h" /* For Write/ReadClassStm */
52 #include "wine/wingdi16.h"
53 #include "compobj_private.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
59 * These are signatures to detect the type of Document file.
61 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
62 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
64 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
67 /****************************************************************************
68 * StorageInternalImpl definitions.
70 * Definition of the implementation structure for the IStorage interface.
71 * This one implements the IStorage interface for storage that are
72 * inside another storage.
74 typedef struct StorageInternalImpl
76 struct StorageBaseImpl base
;
79 * Entry in the parent's stream tracking list
81 struct list ParentListEntry
;
83 StorageBaseImpl
*parentStorage
;
84 } StorageInternalImpl
;
86 static const IStorageVtbl StorageInternalImpl_Vtbl
;
87 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
*,DWORD
,DirRef
);
89 typedef struct TransactedDirEntry
91 /* If applicable, a reference to the original DirEntry in the transacted
92 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
93 DirRef transactedParentEntry
;
95 /* True if this entry is being used. */
98 /* True if data is up to date. */
101 /* True if this entry has been modified. */
104 /* True if this entry's stream has been modified. */
107 /* True if this entry has been deleted in the transacted storage, but the
108 * delete has not yet been committed. */
111 /* If this entry's stream has been modified, a reference to where the stream
112 * is stored in the snapshot file. */
115 /* This directory entry's data, including any changes that have been made. */
118 /* A reference to the parent of this node. This is only valid while we are
119 * committing changes. */
122 /* A reference to a newly-created entry in the transacted parent. This is
123 * always equal to transactedParentEntry except when committing changes. */
124 DirRef newTransactedParentEntry
;
125 } TransactedDirEntry
;
128 /****************************************************************************
129 * Transacted storage object.
131 typedef struct TransactedSnapshotImpl
133 struct StorageBaseImpl base
;
136 * Modified streams are temporarily saved to the scratch file.
138 StorageBaseImpl
*scratch
;
140 /* The directory structure is kept here, so that we can track how these
141 * entries relate to those in the parent storage. */
142 TransactedDirEntry
*entries
;
144 ULONG firstFreeEntry
;
147 * Changes are committed to the transacted parent.
149 StorageBaseImpl
*transactedParent
;
151 /* The transaction signature from when we last committed */
152 ULONG lastTransactionSig
;
153 } TransactedSnapshotImpl
;
155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
156 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*,BOOL
,StorageBaseImpl
**);
158 typedef struct TransactedSharedImpl
160 struct StorageBaseImpl base
;
163 * Snapshot and uncommitted changes go here.
165 TransactedSnapshotImpl
*scratch
;
168 * Changes are committed to the transacted parent.
170 StorageBaseImpl
*transactedParent
;
172 /* The transaction signature from when we last committed */
173 ULONG lastTransactionSig
;
174 } TransactedSharedImpl
;
177 /****************************************************************************
178 * BlockChainStream definitions.
180 * The BlockChainStream class is a utility class that is used to create an
181 * abstraction of the big block chains in the storage file.
186 /* This represents a range of blocks that happen reside in consecutive sectors. */
192 typedef struct BlockChainBlock
198 BYTE data
[MAX_BIG_BLOCK_SIZE
];
201 struct BlockChainStream
203 StorageImpl
* parentStorage
;
204 ULONG
* headOfStreamPlaceHolder
;
205 DirRef ownerDirEntry
;
206 struct BlockChainRun
* indexCache
;
208 ULONG indexCacheSize
;
209 BlockChainBlock cachedBlocks
[2];
215 /* Returns the number of blocks that comprises this chain.
216 * This is not the size of the stream as the last block may not be full!
218 static inline ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
220 return This
->numBlocks
;
223 static BlockChainStream
* BlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
224 static void BlockChainStream_Destroy(BlockChainStream
*);
225 static HRESULT
BlockChainStream_ReadAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
226 static HRESULT
BlockChainStream_WriteAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
227 static HRESULT
BlockChainStream_Flush(BlockChainStream
*);
228 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
*);
229 static BOOL
BlockChainStream_SetSize(BlockChainStream
*,ULARGE_INTEGER
);
232 /****************************************************************************
233 * SmallBlockChainStream definitions.
235 * The SmallBlockChainStream class is a utility class that is used to create an
236 * abstraction of the small block chains in the storage file.
239 struct SmallBlockChainStream
241 StorageImpl
* parentStorage
;
242 DirRef ownerDirEntry
;
243 ULONG
* headOfStreamPlaceHolder
;
246 static SmallBlockChainStream
* SmallBlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
247 static void SmallBlockChainStream_Destroy(SmallBlockChainStream
*);
248 static HRESULT
SmallBlockChainStream_ReadAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
249 static HRESULT
SmallBlockChainStream_WriteAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
250 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
*);
251 static BOOL
SmallBlockChainStream_SetSize(SmallBlockChainStream
*,ULARGE_INTEGER
);
254 /************************************************************************
256 ***********************************************************************/
258 /************************************************************************
259 * This method validates an STGM parameter that can contain the values below
261 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
262 * The stgm values contained in 0xffff0000 are bitmasks.
264 * STGM_DIRECT 0x00000000
265 * STGM_TRANSACTED 0x00010000
266 * STGM_SIMPLE 0x08000000
268 * STGM_READ 0x00000000
269 * STGM_WRITE 0x00000001
270 * STGM_READWRITE 0x00000002
272 * STGM_SHARE_DENY_NONE 0x00000040
273 * STGM_SHARE_DENY_READ 0x00000030
274 * STGM_SHARE_DENY_WRITE 0x00000020
275 * STGM_SHARE_EXCLUSIVE 0x00000010
277 * STGM_PRIORITY 0x00040000
278 * STGM_DELETEONRELEASE 0x04000000
280 * STGM_CREATE 0x00001000
281 * STGM_CONVERT 0x00020000
282 * STGM_FAILIFTHERE 0x00000000
284 * STGM_NOSCRATCH 0x00100000
285 * STGM_NOSNAPSHOT 0x00200000
287 static HRESULT
validateSTGM(DWORD stgm
)
289 DWORD access
= STGM_ACCESS_MODE(stgm
);
290 DWORD share
= STGM_SHARE_MODE(stgm
);
291 DWORD create
= STGM_CREATE_MODE(stgm
);
293 if (stgm
&~STGM_KNOWN_FLAGS
)
295 ERR("unknown flags %#lx\n", stgm
);
311 case STGM_SHARE_DENY_NONE
:
312 case STGM_SHARE_DENY_READ
:
313 case STGM_SHARE_DENY_WRITE
:
314 case STGM_SHARE_EXCLUSIVE
:
317 if (!(stgm
& STGM_TRANSACTED
))
327 case STGM_FAILIFTHERE
:
334 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
336 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
340 * STGM_CREATE | STGM_CONVERT
341 * if both are false, STGM_FAILIFTHERE is set to TRUE
343 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
347 * STGM_NOSCRATCH requires STGM_TRANSACTED
349 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
353 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
354 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
356 if ( (stgm
& STGM_NOSNAPSHOT
) &&
357 (!(stgm
& STGM_TRANSACTED
) ||
358 share
== STGM_SHARE_EXCLUSIVE
||
359 share
== STGM_SHARE_DENY_WRITE
) )
365 /************************************************************************
366 * GetShareModeFromSTGM
368 * This method will return a share mode flag from a STGM value.
369 * The STGM value is assumed valid.
371 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
373 switch (STGM_SHARE_MODE(stgm
))
376 assert(stgm
& STGM_TRANSACTED
);
378 case STGM_SHARE_DENY_NONE
:
379 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
380 case STGM_SHARE_DENY_READ
:
381 return FILE_SHARE_WRITE
;
382 case STGM_SHARE_DENY_WRITE
:
383 case STGM_SHARE_EXCLUSIVE
:
384 return FILE_SHARE_READ
;
386 ERR("Invalid share mode!\n");
391 /************************************************************************
392 * GetAccessModeFromSTGM
394 * This method will return an access mode flag from a STGM value.
395 * The STGM value is assumed valid.
397 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
399 switch (STGM_ACCESS_MODE(stgm
))
405 return GENERIC_READ
| GENERIC_WRITE
;
407 ERR("Invalid access mode!\n");
412 /************************************************************************
413 * GetCreationModeFromSTGM
415 * This method will return a creation mode flag from a STGM value.
416 * The STGM value is assumed valid.
418 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
420 switch(STGM_CREATE_MODE(stgm
))
423 return CREATE_ALWAYS
;
425 FIXME("STGM_CONVERT not implemented!\n");
427 case STGM_FAILIFTHERE
:
430 ERR("Invalid create mode!\n");
436 /************************************************************************
437 * IDirectWriterLock implementation
438 ***********************************************************************/
440 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
442 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
445 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
447 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
448 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
451 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
453 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
454 return IStorage_AddRef(&This
->IStorage_iface
);
457 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
459 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
460 return IStorage_Release(&This
->IStorage_iface
);
463 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
465 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
466 FIXME("%p, %ld: stub\n", This
, timeout
);
470 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
472 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
473 FIXME("(%p): stub\n", This
);
477 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
479 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
480 FIXME("(%p): stub\n", This
);
484 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
486 directwriterlock_QueryInterface
,
487 directwriterlock_AddRef
,
488 directwriterlock_Release
,
489 directwriterlock_WaitForWriteAccess
,
490 directwriterlock_ReleaseWriteAccess
,
491 directwriterlock_HaveWriteAccess
495 /************************************************************************
496 * StorageBaseImpl implementation : Tree helper functions
497 ***********************************************************************/
499 /****************************************************************************
503 * Case insensitive comparison of DirEntry.name by first considering
506 * Returns <0 when name1 < name2
507 * >0 when name1 > name2
508 * 0 when name1 == name2
510 static LONG
entryNameCmp(
511 const OLECHAR
*name1
,
512 const OLECHAR
*name2
)
514 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
516 while (diff
== 0 && *name1
!= 0)
519 * We compare the string themselves only when they are of the same length
521 diff
= towupper(*name1
++) - towupper(*name2
++);
527 /****************************************************************************
531 * Find and read the element of a storage with the given name.
533 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
534 const OLECHAR
*name
, DirEntry
*data
)
538 /* Read the storage entry to find the root of the tree. */
539 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
541 currentEntry
= data
->dirRootEntry
;
543 while (currentEntry
!= DIRENTRY_NULL
)
547 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
549 cmp
= entryNameCmp(name
, data
->name
);
556 currentEntry
= data
->leftChild
;
559 currentEntry
= data
->rightChild
;
565 /****************************************************************************
569 * Find and read the binary tree parent of the element with the given name.
571 * If there is no such element, find a place where it could be inserted and
572 * return STG_E_FILENOTFOUND.
574 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
575 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
581 /* Read the storage entry to find the root of the tree. */
582 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
584 *parentEntry
= storageEntry
;
585 *relation
= DIRENTRY_RELATION_DIR
;
587 childEntry
= parentData
->dirRootEntry
;
589 while (childEntry
!= DIRENTRY_NULL
)
593 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
595 cmp
= entryNameCmp(childName
, childData
.name
);
603 *parentData
= childData
;
604 *parentEntry
= childEntry
;
605 *relation
= DIRENTRY_RELATION_PREVIOUS
;
607 childEntry
= parentData
->leftChild
;
612 *parentData
= childData
;
613 *parentEntry
= childEntry
;
614 *relation
= DIRENTRY_RELATION_NEXT
;
616 childEntry
= parentData
->rightChild
;
620 if (childEntry
== DIRENTRY_NULL
)
621 return STG_E_FILENOTFOUND
;
626 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
630 case DIRENTRY_RELATION_PREVIOUS
:
631 entry
->leftChild
= new_target
;
633 case DIRENTRY_RELATION_NEXT
:
634 entry
->rightChild
= new_target
;
636 case DIRENTRY_RELATION_DIR
:
637 entry
->dirRootEntry
= new_target
;
644 /****************************************************************************
648 * Add a directory entry to a storage
650 static HRESULT
insertIntoTree(
651 StorageBaseImpl
*This
,
652 DirRef parentStorageIndex
,
653 DirRef newEntryIndex
)
655 DirEntry currentEntry
;
659 * Read the inserted entry
661 StorageBaseImpl_ReadDirEntry(This
,
666 * Read the storage entry
668 StorageBaseImpl_ReadDirEntry(This
,
672 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
675 * The root storage contains some element, therefore, start the research
676 * for the appropriate location.
679 DirRef current
, next
, previous
, currentEntryId
;
682 * Keep a reference to the root of the storage's element tree
684 currentEntryId
= currentEntry
.dirRootEntry
;
689 StorageBaseImpl_ReadDirEntry(This
,
690 currentEntry
.dirRootEntry
,
693 previous
= currentEntry
.leftChild
;
694 next
= currentEntry
.rightChild
;
695 current
= currentEntryId
;
699 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
703 if (previous
!= DIRENTRY_NULL
)
705 StorageBaseImpl_ReadDirEntry(This
,
712 currentEntry
.leftChild
= newEntryIndex
;
713 StorageBaseImpl_WriteDirEntry(This
,
721 if (next
!= DIRENTRY_NULL
)
723 StorageBaseImpl_ReadDirEntry(This
,
730 currentEntry
.rightChild
= newEntryIndex
;
731 StorageBaseImpl_WriteDirEntry(This
,
740 * Trying to insert an item with the same name in the
743 return STG_E_FILEALREADYEXISTS
;
746 previous
= currentEntry
.leftChild
;
747 next
= currentEntry
.rightChild
;
753 * The storage is empty, make the new entry the root of its element tree
755 currentEntry
.dirRootEntry
= newEntryIndex
;
756 StorageBaseImpl_WriteDirEntry(This
,
764 /*************************************************************************
768 * This method removes a directory entry from its parent storage tree without
769 * freeing any resources attached to it.
771 static HRESULT
removeFromTree(
772 StorageBaseImpl
*This
,
773 DirRef parentStorageIndex
,
776 DirEntry entryToDelete
;
777 DirEntry parentEntry
;
778 DirRef parentEntryRef
;
779 ULONG typeOfRelation
;
782 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
788 * Find the element that links to the one we want to delete.
790 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
791 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
796 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
799 * Replace the deleted entry with its left child
801 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
803 hr
= StorageBaseImpl_WriteDirEntry(
812 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
815 * We need to reinsert the right child somewhere. We already know it and
816 * its children are greater than everything in the left tree, so we
817 * insert it at the rightmost point in the left tree.
819 DirRef newRightChildParent
= entryToDelete
.leftChild
;
820 DirEntry newRightChildParentEntry
;
824 hr
= StorageBaseImpl_ReadDirEntry(
827 &newRightChildParentEntry
);
833 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
834 newRightChildParent
= newRightChildParentEntry
.rightChild
;
835 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
837 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
839 hr
= StorageBaseImpl_WriteDirEntry(
842 &newRightChildParentEntry
);
852 * Replace the deleted entry with its right child
854 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
856 hr
= StorageBaseImpl_WriteDirEntry(
870 /************************************************************************
871 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
872 ***********************************************************************/
875 * IEnumSTATSTGImpl definitions.
877 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
878 * This class allows iterating through the content of a storage and finding
879 * specific items inside it.
881 struct IEnumSTATSTGImpl
883 IEnumSTATSTG IEnumSTATSTG_iface
;
885 LONG ref
; /* Reference count */
886 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
887 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
889 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
892 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
894 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
897 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
899 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
900 HeapFree(GetProcessHeap(), 0, This
);
903 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
908 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
910 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
917 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
918 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
920 *ppvObject
= &This
->IEnumSTATSTG_iface
;
921 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
922 TRACE("<-- %p\n", *ppvObject
);
926 TRACE("<-- E_NOINTERFACE\n");
927 return E_NOINTERFACE
;
930 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
933 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
934 return InterlockedIncrement(&This
->ref
);
937 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
940 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
944 newRef
= InterlockedDecrement(&This
->ref
);
948 IEnumSTATSTGImpl_Destroy(This
);
954 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
955 IEnumSTATSTGImpl
* This
,
958 DirRef result
= DIRENTRY_NULL
;
962 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
964 TRACE("%p,%p\n", This
, ref
);
966 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
967 This
->parentStorage
->storageDirEntry
, &entry
);
968 searchNode
= entry
.dirRootEntry
;
970 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
972 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
976 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
980 searchNode
= entry
.rightChild
;
985 memcpy(result_name
, entry
.name
, sizeof(result_name
));
986 searchNode
= entry
.leftChild
;
994 if (result
!= DIRENTRY_NULL
)
995 memcpy(This
->name
, result_name
, sizeof(result_name
));
998 TRACE("<-- %#lx\n", hr
);
1002 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
1003 IEnumSTATSTG
* iface
,
1006 ULONG
* pceltFetched
)
1008 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1010 DirEntry currentEntry
;
1011 STATSTG
* currentReturnStruct
= rgelt
;
1012 ULONG objectFetched
= 0;
1013 DirRef currentSearchNode
;
1016 TRACE("%p, %lu, %p, %p.\n", iface
, celt
, rgelt
, pceltFetched
);
1018 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
1019 return E_INVALIDARG
;
1021 if (This
->parentStorage
->reverted
)
1023 TRACE("<-- STG_E_REVERTED\n");
1024 return STG_E_REVERTED
;
1028 * To avoid the special case, get another pointer to a ULONG value if
1029 * the caller didn't supply one.
1031 if (pceltFetched
==0)
1032 pceltFetched
= &objectFetched
;
1035 * Start the iteration, we will iterate until we hit the end of the
1036 * linked list or until we hit the number of items to iterate through
1040 while ( *pceltFetched
< celt
)
1042 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1044 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1046 memset(currentReturnStruct
, 0, sizeof(*currentReturnStruct
));
1051 * Read the entry from the storage.
1053 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
1056 if (FAILED(hr
)) break;
1059 * Copy the information to the return buffer.
1061 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
1062 currentReturnStruct
,
1067 * Step to the next item in the iteration
1070 currentReturnStruct
++;
1073 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
1076 TRACE("<-- %#lx (asked %lu, got %lu)\n", hr
, celt
, *pceltFetched
);
1081 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
1082 IEnumSTATSTG
* iface
,
1085 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1087 ULONG objectFetched
= 0;
1088 DirRef currentSearchNode
;
1091 TRACE("%p, %lu.\n", iface
, celt
);
1093 if (This
->parentStorage
->reverted
)
1095 TRACE("<-- STG_E_REVERTED\n");
1096 return STG_E_REVERTED
;
1099 while ( (objectFetched
< celt
) )
1101 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1103 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1109 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
1112 TRACE("<-- %#lx\n", hr
);
1116 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
1117 IEnumSTATSTG
* iface
)
1119 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1121 TRACE("%p\n", iface
);
1123 if (This
->parentStorage
->reverted
)
1125 TRACE("<-- STG_E_REVERTED\n");
1126 return STG_E_REVERTED
;
1134 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
*,DirRef
);
1136 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
1137 IEnumSTATSTG
* iface
,
1138 IEnumSTATSTG
** ppenum
)
1140 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1141 IEnumSTATSTGImpl
* newClone
;
1143 TRACE("%p,%p\n", iface
, ppenum
);
1145 if (This
->parentStorage
->reverted
)
1147 TRACE("<-- STG_E_REVERTED\n");
1148 return STG_E_REVERTED
;
1152 return E_INVALIDARG
;
1154 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
1155 This
->storageDirEntry
);
1159 return E_OUTOFMEMORY
;
1163 * The new clone enumeration must point to the same current node as
1166 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
1168 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
1174 * Virtual function table for the IEnumSTATSTGImpl class.
1176 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
1178 IEnumSTATSTGImpl_QueryInterface
,
1179 IEnumSTATSTGImpl_AddRef
,
1180 IEnumSTATSTGImpl_Release
,
1181 IEnumSTATSTGImpl_Next
,
1182 IEnumSTATSTGImpl_Skip
,
1183 IEnumSTATSTGImpl_Reset
,
1184 IEnumSTATSTGImpl_Clone
1187 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
1188 StorageBaseImpl
* parentStorage
,
1189 DirRef storageDirEntry
)
1191 IEnumSTATSTGImpl
* newEnumeration
;
1193 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
1197 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
1198 newEnumeration
->ref
= 1;
1199 newEnumeration
->name
[0] = 0;
1202 * We want to nail-down the reference to the storage in case the
1203 * enumeration out-lives the storage in the client application.
1205 newEnumeration
->parentStorage
= parentStorage
;
1206 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
1208 newEnumeration
->storageDirEntry
= storageDirEntry
;
1211 return newEnumeration
;
1215 /************************************************************************
1216 * StorageBaseImpl implementation
1217 ***********************************************************************/
1219 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
1221 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
1224 /************************************************************************
1225 * StorageBaseImpl_QueryInterface (IUnknown)
1227 * This method implements the common QueryInterface for all IStorage
1228 * implementations contained in this file.
1230 * See Windows documentation for more details on IUnknown methods.
1232 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
1237 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1239 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
1242 return E_INVALIDARG
;
1246 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
1247 IsEqualGUID(&IID_IStorage
, riid
))
1249 *ppvObject
= &This
->IStorage_iface
;
1251 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
1253 *ppvObject
= &This
->IPropertySetStorage_iface
;
1255 /* locking interface is reported for writer only */
1256 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
1258 *ppvObject
= &This
->IDirectWriterLock_iface
;
1262 TRACE("<-- E_NOINTERFACE\n");
1263 return E_NOINTERFACE
;
1266 IStorage_AddRef(iface
);
1267 TRACE("<-- %p\n", *ppvObject
);
1271 /************************************************************************
1272 * StorageBaseImpl_AddRef (IUnknown)
1274 * This method implements the common AddRef for all IStorage
1275 * implementations contained in this file.
1277 * See Windows documentation for more details on IUnknown methods.
1279 static ULONG WINAPI
StorageBaseImpl_AddRef(
1282 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1283 ULONG ref
= InterlockedIncrement(&This
->ref
);
1285 TRACE("%p, refcount %lu.\n", iface
, ref
);
1290 /************************************************************************
1291 * StorageBaseImpl_Release (IUnknown)
1293 * This method implements the common Release for all IStorage
1294 * implementations contained in this file.
1296 * See Windows documentation for more details on IUnknown methods.
1298 static ULONG WINAPI
StorageBaseImpl_Release(
1301 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1303 ULONG ref
= InterlockedDecrement(&This
->ref
);
1305 TRACE("%p, refcount %lu.\n", iface
, ref
);
1310 * Since we are using a system of base-classes, we want to call the
1311 * destructor of the appropriate derived class. To do this, we are
1312 * using virtual functions to implement the destructor.
1314 StorageBaseImpl_Destroy(This
);
1320 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1321 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1322 SNB snbExclude
, IStorage
*pstgDest
);
1324 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1325 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1326 SNB snbExclude
, IStorage
*pstgDest
)
1332 IStream
*pstrChild
, *pstrTmp
;
1335 if (srcEntry
== DIRENTRY_NULL
)
1338 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1345 WCHAR
**snb
= snbExclude
;
1347 while ( *snb
!= NULL
&& !skip
)
1349 if ( wcscmp(data
.name
, *snb
) == 0 )
1357 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1360 * create a new storage in destination storage
1362 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1363 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1368 * if it already exist, don't create a new one use this one
1370 if (hr
== STG_E_FILEALREADYEXISTS
)
1372 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1373 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1374 NULL
, 0, &pstgTmp
);
1379 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1380 skip_stream
, NULL
, pstgTmp
);
1382 IStorage_Release(pstgTmp
);
1385 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1388 * create a new stream in destination storage. If the stream already
1389 * exist, it will be deleted and a new one will be created.
1391 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1392 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1396 * open child stream storage. This operation must succeed even if the
1397 * stream is already open, so we use internal functions to do it.
1401 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1405 pstrChild
= &streamimpl
->IStream_iface
;
1407 IStream_AddRef(pstrChild
);
1419 * Get the size of the source stream
1421 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1424 * Set the size of the destination stream.
1426 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1431 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1434 IStream_Release( pstrChild
);
1437 IStream_Release( pstrTmp
);
1443 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1444 skip_stream
, snbExclude
, pstgDest
);
1447 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1448 skip_stream
, snbExclude
, pstgDest
);
1450 TRACE("<-- %#lx\n", hr
);
1454 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1456 StgStreamImpl
*strm
;
1458 TRACE("%p, %ld.\n", stg
, streamEntry
);
1460 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1462 if (strm
->dirEntry
== streamEntry
)
1471 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1473 StorageInternalImpl
*childstg
;
1475 TRACE("%p, %ld.\n", stg
, storageEntry
);
1477 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1479 if (childstg
->base
.storageDirEntry
== storageEntry
)
1488 /************************************************************************
1489 * StorageBaseImpl_OpenStream (IStorage)
1491 * This method will open the specified stream object from the current storage.
1493 * See Windows documentation for more details on IStorage methods.
1495 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
1497 const OLECHAR
* pwcsName
, /* [string][in] */
1498 void* reserved1
, /* [unique][in] */
1499 DWORD grfMode
, /* [in] */
1500 DWORD reserved2
, /* [in] */
1501 IStream
** ppstm
) /* [out] */
1503 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1504 StgStreamImpl
* newStream
;
1505 DirEntry currentEntry
;
1506 DirRef streamEntryRef
;
1507 HRESULT res
= STG_E_UNKNOWN
;
1509 TRACE("%p, %s, %p, %#lx, %ld, %p.\n", iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1511 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1519 if ( FAILED( validateSTGM(grfMode
) ) ||
1520 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1522 res
= STG_E_INVALIDFLAG
;
1529 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1531 res
= STG_E_INVALIDFUNCTION
;
1537 res
= STG_E_REVERTED
;
1542 * Check that we're compatible with the parent's storage mode, but
1543 * only if we are not in transacted mode
1545 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1546 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1548 res
= STG_E_INVALIDFLAG
;
1554 * Search for the element with the given name
1556 streamEntryRef
= findElement(
1558 This
->storageDirEntry
,
1563 * If it was found, construct the stream object and return a pointer to it.
1565 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1566 (currentEntry
.stgType
==STGTY_STREAM
) )
1568 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1570 /* A single stream cannot be opened a second time. */
1571 res
= STG_E_ACCESSDENIED
;
1575 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1579 newStream
->grfMode
= grfMode
;
1580 *ppstm
= &newStream
->IStream_iface
;
1582 IStream_AddRef(*ppstm
);
1588 res
= E_OUTOFMEMORY
;
1592 res
= STG_E_FILENOTFOUND
;
1596 TRACE("<-- IStream %p\n", *ppstm
);
1597 TRACE("<-- %#lx\n", res
);
1601 /************************************************************************
1602 * StorageBaseImpl_OpenStorage (IStorage)
1604 * This method will open a new storage object from the current storage.
1606 * See Windows documentation for more details on IStorage methods.
1608 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1610 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1611 IStorage
* pstgPriority
, /* [unique][in] */
1612 DWORD grfMode
, /* [in] */
1613 SNB snbExclude
, /* [unique][in] */
1614 DWORD reserved
, /* [in] */
1615 IStorage
** ppstg
) /* [out] */
1617 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1618 StorageInternalImpl
* newStorage
;
1619 StorageBaseImpl
* newTransactedStorage
;
1620 DirEntry currentEntry
;
1621 DirRef storageEntryRef
;
1622 HRESULT res
= STG_E_UNKNOWN
;
1624 TRACE("%p, %s, %p, %#lx, %p, %ld, %p.\n", iface
, debugstr_w(pwcsName
), pstgPriority
,
1625 grfMode
, snbExclude
, reserved
, ppstg
);
1627 if ((pwcsName
==NULL
) || (ppstg
==0) )
1633 if (This
->openFlags
& STGM_SIMPLE
)
1635 res
= STG_E_INVALIDFUNCTION
;
1640 if (snbExclude
!= NULL
)
1642 res
= STG_E_INVALIDPARAMETER
;
1646 if ( FAILED( validateSTGM(grfMode
) ))
1648 res
= STG_E_INVALIDFLAG
;
1655 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1656 (grfMode
& STGM_DELETEONRELEASE
) ||
1657 (grfMode
& STGM_PRIORITY
) )
1659 res
= STG_E_INVALIDFUNCTION
;
1664 return STG_E_REVERTED
;
1667 * Check that we're compatible with the parent's storage mode,
1668 * but only if we are not transacted
1670 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1671 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1673 res
= STG_E_ACCESSDENIED
;
1680 storageEntryRef
= findElement(
1682 This
->storageDirEntry
,
1686 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1687 (currentEntry
.stgType
==STGTY_STORAGE
) )
1689 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1691 /* A single storage cannot be opened a second time. */
1692 res
= STG_E_ACCESSDENIED
;
1696 newStorage
= StorageInternalImpl_Construct(
1701 if (newStorage
!= 0)
1703 if (grfMode
& STGM_TRANSACTED
)
1705 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1709 HeapFree(GetProcessHeap(), 0, newStorage
);
1713 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1717 *ppstg
= &newStorage
->base
.IStorage_iface
;
1720 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1726 res
= STG_E_INSUFFICIENTMEMORY
;
1730 res
= STG_E_FILENOTFOUND
;
1733 TRACE("<-- %#lx\n", res
);
1737 /************************************************************************
1738 * StorageBaseImpl_EnumElements (IStorage)
1740 * This method will create an enumerator object that can be used to
1741 * retrieve information about all the elements in the storage object.
1743 * See Windows documentation for more details on IStorage methods.
1745 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1747 DWORD reserved1
, /* [in] */
1748 void* reserved2
, /* [size_is][unique][in] */
1749 DWORD reserved3
, /* [in] */
1750 IEnumSTATSTG
** ppenum
) /* [out] */
1752 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1753 IEnumSTATSTGImpl
* newEnum
;
1755 TRACE("%p, %ld, %p, %ld, %p.\n", iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1758 return E_INVALIDARG
;
1761 return STG_E_REVERTED
;
1763 newEnum
= IEnumSTATSTGImpl_Construct(
1765 This
->storageDirEntry
);
1769 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1773 return E_OUTOFMEMORY
;
1776 /************************************************************************
1777 * StorageBaseImpl_Stat (IStorage)
1779 * This method will retrieve information about this storage object.
1781 * See Windows documentation for more details on IStorage methods.
1783 static HRESULT WINAPI
StorageBaseImpl_Stat(
1785 STATSTG
* pstatstg
, /* [out] */
1786 DWORD grfStatFlag
) /* [in] */
1788 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1789 DirEntry currentEntry
;
1790 HRESULT res
= STG_E_UNKNOWN
;
1792 TRACE("%p, %p, %#lx.\n", iface
, pstatstg
, grfStatFlag
);
1802 res
= STG_E_REVERTED
;
1806 res
= StorageBaseImpl_ReadDirEntry(
1808 This
->storageDirEntry
,
1813 StorageUtl_CopyDirEntryToSTATSTG(
1819 pstatstg
->grfMode
= This
->openFlags
;
1820 pstatstg
->grfStateBits
= This
->stateBits
;
1826 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
1828 TRACE("<-- %#lx\n", res
);
1832 /************************************************************************
1833 * StorageBaseImpl_RenameElement (IStorage)
1835 * This method will rename the specified element.
1837 * See Windows documentation for more details on IStorage methods.
1839 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1841 const OLECHAR
* pwcsOldName
, /* [in] */
1842 const OLECHAR
* pwcsNewName
) /* [in] */
1844 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1845 DirEntry currentEntry
;
1846 DirRef currentEntryRef
;
1848 TRACE("(%p, %s, %s)\n",
1849 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1852 return STG_E_REVERTED
;
1854 currentEntryRef
= findElement(This
,
1855 This
->storageDirEntry
,
1859 if (currentEntryRef
!= DIRENTRY_NULL
)
1862 * There is already an element with the new name
1864 return STG_E_FILEALREADYEXISTS
;
1868 * Search for the old element name
1870 currentEntryRef
= findElement(This
,
1871 This
->storageDirEntry
,
1875 if (currentEntryRef
!= DIRENTRY_NULL
)
1877 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1878 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1880 WARN("Element is already open; cannot rename.\n");
1881 return STG_E_ACCESSDENIED
;
1884 /* Remove the element from its current position in the tree */
1885 removeFromTree(This
, This
->storageDirEntry
,
1888 /* Change the name of the element */
1889 lstrcpyW(currentEntry
.name
, pwcsNewName
);
1891 /* Delete any sibling links */
1892 currentEntry
.leftChild
= DIRENTRY_NULL
;
1893 currentEntry
.rightChild
= DIRENTRY_NULL
;
1895 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1898 /* Insert the element in a new position in the tree */
1899 insertIntoTree(This
, This
->storageDirEntry
,
1905 * There is no element with the old name
1907 return STG_E_FILENOTFOUND
;
1910 return StorageBaseImpl_Flush(This
);
1913 /************************************************************************
1914 * StorageBaseImpl_CreateStream (IStorage)
1916 * This method will create a stream object within this storage
1918 * See Windows documentation for more details on IStorage methods.
1920 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1922 const OLECHAR
* pwcsName
, /* [string][in] */
1923 DWORD grfMode
, /* [in] */
1924 DWORD reserved1
, /* [in] */
1925 DWORD reserved2
, /* [in] */
1926 IStream
** ppstm
) /* [out] */
1928 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1929 StgStreamImpl
* newStream
;
1930 DirEntry currentEntry
, newStreamEntry
;
1931 DirRef currentEntryRef
, newStreamEntryRef
;
1934 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface
, debugstr_w(pwcsName
), grfMode
, reserved1
, reserved2
, ppstm
);
1937 return STG_E_INVALIDPOINTER
;
1940 return STG_E_INVALIDNAME
;
1942 if (reserved1
|| reserved2
)
1943 return STG_E_INVALIDPARAMETER
;
1945 if ( FAILED( validateSTGM(grfMode
) ))
1946 return STG_E_INVALIDFLAG
;
1948 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1949 return STG_E_INVALIDFLAG
;
1952 return STG_E_REVERTED
;
1957 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1958 (grfMode
& STGM_TRANSACTED
))
1959 return STG_E_INVALIDFUNCTION
;
1962 * Don't worry about permissions in transacted mode, as we can always write
1963 * changes; we just can't always commit them.
1965 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1966 /* Can't create a stream on read-only storage */
1967 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1968 return STG_E_ACCESSDENIED
;
1970 /* Can't create a stream with greater access than the parent. */
1971 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1972 return STG_E_ACCESSDENIED
;
1975 if(This
->openFlags
& STGM_SIMPLE
)
1976 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1980 currentEntryRef
= findElement(This
,
1981 This
->storageDirEntry
,
1985 if (currentEntryRef
!= DIRENTRY_NULL
)
1988 * An element with this name already exists
1990 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1992 IStorage_DestroyElement(iface
, pwcsName
);
1995 return STG_E_FILEALREADYEXISTS
;
1999 * memset the empty entry
2001 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
2003 newStreamEntry
.sizeOfNameString
=
2004 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
2006 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2007 return STG_E_INVALIDNAME
;
2009 lstrcpyW(newStreamEntry
.name
, pwcsName
);
2011 newStreamEntry
.stgType
= STGTY_STREAM
;
2012 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2013 newStreamEntry
.size
.u
.LowPart
= 0;
2014 newStreamEntry
.size
.u
.HighPart
= 0;
2016 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
2017 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
2018 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
2020 /* call CoFileTime to get the current time
2021 newStreamEntry.ctime
2022 newStreamEntry.mtime
2025 /* newStreamEntry.clsid */
2028 * Create an entry with the new data
2030 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
2035 * Insert the new entry in the parent storage's tree.
2037 hr
= insertIntoTree(
2039 This
->storageDirEntry
,
2043 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
2048 * Open the stream to return it.
2050 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2054 *ppstm
= &newStream
->IStream_iface
;
2055 IStream_AddRef(*ppstm
);
2059 return STG_E_INSUFFICIENTMEMORY
;
2062 return StorageBaseImpl_Flush(This
);
2065 /************************************************************************
2066 * StorageBaseImpl_SetClass (IStorage)
2068 * This method will write the specified CLSID in the directory entry of this
2071 * See Windows documentation for more details on IStorage methods.
2073 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2075 REFCLSID clsid
) /* [in] */
2077 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2079 DirEntry currentEntry
;
2081 TRACE("(%p, %s)\n", iface
, wine_dbgstr_guid(clsid
));
2084 return STG_E_REVERTED
;
2086 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2087 This
->storageDirEntry
,
2089 if (SUCCEEDED(hRes
))
2091 currentEntry
.clsid
= *clsid
;
2093 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2094 This
->storageDirEntry
,
2098 if (SUCCEEDED(hRes
))
2099 hRes
= StorageBaseImpl_Flush(This
);
2104 /************************************************************************
2105 * StorageBaseImpl_CreateStorage (IStorage)
2107 * This method will create the storage object within the provided storage.
2109 * See Windows documentation for more details on IStorage methods.
2111 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2113 const OLECHAR
*pwcsName
, /* [string][in] */
2114 DWORD grfMode
, /* [in] */
2115 DWORD reserved1
, /* [in] */
2116 DWORD reserved2
, /* [in] */
2117 IStorage
**ppstg
) /* [out] */
2119 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2121 DirEntry currentEntry
;
2123 DirRef currentEntryRef
;
2127 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface
, debugstr_w(pwcsName
), grfMode
,
2128 reserved1
, reserved2
, ppstg
);
2131 return STG_E_INVALIDPOINTER
;
2133 if (This
->openFlags
& STGM_SIMPLE
)
2135 return STG_E_INVALIDFUNCTION
;
2139 return STG_E_INVALIDNAME
;
2143 if ( FAILED( validateSTGM(grfMode
) ) ||
2144 (grfMode
& STGM_DELETEONRELEASE
) )
2146 WARN("bad grfMode: %#lx\n", grfMode
);
2147 return STG_E_INVALIDFLAG
;
2151 return STG_E_REVERTED
;
2154 * Check that we're compatible with the parent's storage mode
2156 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2157 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2159 WARN("access denied\n");
2160 return STG_E_ACCESSDENIED
;
2163 currentEntryRef
= findElement(This
,
2164 This
->storageDirEntry
,
2168 if (currentEntryRef
!= DIRENTRY_NULL
)
2171 * An element with this name already exists
2173 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2174 ((This
->openFlags
& STGM_TRANSACTED
) ||
2175 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2177 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2183 WARN("file already exists\n");
2184 return STG_E_FILEALREADYEXISTS
;
2187 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2188 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2190 WARN("read-only storage\n");
2191 return STG_E_ACCESSDENIED
;
2194 memset(&newEntry
, 0, sizeof(DirEntry
));
2196 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2198 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2200 FIXME("name too long\n");
2201 return STG_E_INVALIDNAME
;
2204 lstrcpyW(newEntry
.name
, pwcsName
);
2206 newEntry
.stgType
= STGTY_STORAGE
;
2207 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2208 newEntry
.size
.u
.LowPart
= 0;
2209 newEntry
.size
.u
.HighPart
= 0;
2211 newEntry
.leftChild
= DIRENTRY_NULL
;
2212 newEntry
.rightChild
= DIRENTRY_NULL
;
2213 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2215 /* call CoFileTime to get the current time
2220 /* newEntry.clsid */
2223 * Create a new directory entry for the storage
2225 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2230 * Insert the new directory entry into the parent storage's tree
2232 hr
= insertIntoTree(
2234 This
->storageDirEntry
,
2238 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2243 * Open it to get a pointer to return.
2245 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2247 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2253 hr
= StorageBaseImpl_Flush(This
);
2258 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2259 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2260 SNB snbExclude
, IStorage
*pstgDest
)
2265 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2268 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2271 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2272 skip_stream
, snbExclude
, pstgDest
);
2274 TRACE("<-- %#lx\n", hr
);
2278 /*************************************************************************
2281 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2283 DWORD ciidExclude
, /* [in] */
2284 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2285 SNB snbExclude
, /* [unique][in] */
2286 IStorage
* pstgDest
) /* [unique][in] */
2288 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2290 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2293 TRACE("%p, %ld, %p, %p, %p.\n", iface
, ciidExclude
, rgiidExclude
, snbExclude
, pstgDest
);
2295 if ( pstgDest
== 0 )
2296 return STG_E_INVALIDPOINTER
;
2298 for(i
= 0; i
< ciidExclude
; ++i
)
2300 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2301 skip_storage
= TRUE
;
2302 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2305 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2310 /* Give up early if it looks like this would be infinitely recursive.
2311 * Oddly enough, this includes some cases that aren't really recursive, like
2312 * copying to a transacted child. */
2313 IStorage
*pstgDestAncestor
= pstgDest
;
2314 IStorage
*pstgDestAncestorChild
= NULL
;
2316 /* Go up the chain from the destination until we find the source storage. */
2317 while (pstgDestAncestor
!= iface
) {
2318 pstgDestAncestorChild
= pstgDest
;
2320 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2322 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2324 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2326 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2328 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2330 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2336 if (pstgDestAncestor
== iface
)
2340 if (pstgDestAncestorChild
&& snbExclude
)
2342 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2344 WCHAR
**snb
= snbExclude
;
2346 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2348 while ( *snb
!= NULL
&& fail
)
2350 if ( wcscmp(data
.name
, *snb
) == 0 )
2357 return STG_E_ACCESSDENIED
;
2361 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2362 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2365 /*************************************************************************
2366 * MoveElementTo (IStorage)
2368 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2370 const OLECHAR
*pwcsName
, /* [string][in] */
2371 IStorage
*pstgDest
, /* [unique][in] */
2372 const OLECHAR
*pwcsNewName
,/* [string][in] */
2373 DWORD grfFlags
) /* [in] */
2375 FIXME("%p, %s, %p, %s, %#lx: stub\n", iface
, debugstr_w(pwcsName
), pstgDest
,
2376 debugstr_w(pwcsNewName
), grfFlags
);
2380 /*************************************************************************
2383 * Ensures that any changes made to a storage object open in transacted mode
2384 * are reflected in the parent storage
2386 * In a non-transacted mode, this ensures all cached writes are completed.
2388 static HRESULT WINAPI
StorageBaseImpl_Commit(
2390 DWORD grfCommitFlags
)/* [in] */
2392 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2393 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
2394 return StorageBaseImpl_Flush(This
);
2397 /*************************************************************************
2400 * Discard all changes that have been made since the last commit operation
2402 static HRESULT WINAPI
StorageBaseImpl_Revert(
2405 TRACE("(%p)\n", iface
);
2409 /*********************************************************************
2411 * Internal helper function for StorageBaseImpl_DestroyElement()
2413 * Delete the contents of a storage entry.
2416 static HRESULT
deleteStorageContents(
2417 StorageBaseImpl
*parentStorage
,
2418 DirRef indexToDelete
,
2419 DirEntry entryDataToDelete
)
2421 IEnumSTATSTG
*elements
= 0;
2422 IStorage
*childStorage
= 0;
2423 STATSTG currentElement
;
2425 HRESULT destroyHr
= S_OK
;
2426 StorageInternalImpl
*stg
, *stg2
;
2428 TRACE("%p, %ld.\n", parentStorage
, indexToDelete
);
2430 /* Invalidate any open storage objects. */
2431 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2433 if (stg
->base
.storageDirEntry
== indexToDelete
)
2435 StorageBaseImpl_Invalidate(&stg
->base
);
2440 * Open the storage and enumerate it
2442 hr
= IStorage_OpenStorage(
2443 &parentStorage
->IStorage_iface
,
2444 entryDataToDelete
.name
,
2446 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2453 TRACE("<-- %#lx\n", hr
);
2458 * Enumerate the elements
2460 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2463 IStorage_Release(childStorage
);
2464 TRACE("<-- %#lx\n", hr
);
2471 * Obtain the next element
2473 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2476 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2478 CoTaskMemFree(currentElement
.pwcsName
);
2482 * We need to Reset the enumeration every time because we delete elements
2483 * and the enumeration could be invalid
2485 IEnumSTATSTG_Reset(elements
);
2487 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2489 IStorage_Release(childStorage
);
2490 IEnumSTATSTG_Release(elements
);
2492 TRACE("%#lx\n", hr
);
2496 /*********************************************************************
2498 * Internal helper function for StorageBaseImpl_DestroyElement()
2500 * Perform the deletion of a stream's data
2503 static HRESULT
deleteStreamContents(
2504 StorageBaseImpl
*parentStorage
,
2505 DirRef indexToDelete
,
2506 DirEntry entryDataToDelete
)
2510 ULARGE_INTEGER size
;
2511 StgStreamImpl
*strm
, *strm2
;
2513 /* Invalidate any open stream objects. */
2514 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2516 if (strm
->dirEntry
== indexToDelete
)
2518 TRACE("Stream deleted %p\n", strm
);
2519 strm
->parentStorage
= NULL
;
2520 list_remove(&strm
->StrmListEntry
);
2524 size
.u
.HighPart
= 0;
2527 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2528 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2532 TRACE("<-- %#lx\n", hr
);
2539 hr
= IStream_SetSize(pis
, size
);
2543 TRACE("<-- %#lx\n", hr
);
2548 * Release the stream object.
2550 IStream_Release(pis
);
2551 TRACE("<-- %#lx\n", hr
);
2555 /*************************************************************************
2556 * DestroyElement (IStorage)
2558 * Strategy: This implementation is built this way for simplicity not for speed.
2559 * I always delete the topmost element of the enumeration and adjust
2560 * the deleted element pointer all the time. This takes longer to
2561 * do but allows reinvoking DestroyElement whenever we encounter a
2562 * storage object. The optimisation resides in the usage of another
2563 * enumeration strategy that would give all the leaves of a storage
2564 * first. (postfix order)
2566 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2568 const OLECHAR
*pwcsName
)/* [string][in] */
2570 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2573 DirEntry entryToDelete
;
2574 DirRef entryToDeleteRef
;
2577 iface
, debugstr_w(pwcsName
));
2580 return STG_E_INVALIDPOINTER
;
2583 return STG_E_REVERTED
;
2585 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2586 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2587 return STG_E_ACCESSDENIED
;
2589 entryToDeleteRef
= findElement(
2591 This
->storageDirEntry
,
2595 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2597 TRACE("<-- STG_E_FILENOTFOUND\n");
2598 return STG_E_FILENOTFOUND
;
2601 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2603 hr
= deleteStorageContents(
2608 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2610 hr
= deleteStreamContents(
2618 TRACE("<-- %#lx\n", hr
);
2623 * Remove the entry from its parent storage
2625 hr
= removeFromTree(
2627 This
->storageDirEntry
,
2631 * Invalidate the entry
2634 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2637 hr
= StorageBaseImpl_Flush(This
);
2639 TRACE("<-- %#lx\n", hr
);
2643 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2645 struct list
*cur
, *cur2
;
2646 StgStreamImpl
*strm
=NULL
;
2647 StorageInternalImpl
*childstg
=NULL
;
2649 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2650 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2651 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2652 strm
->parentStorage
= NULL
;
2656 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2657 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2658 StorageBaseImpl_Invalidate( &childstg
->base
);
2661 if (stg
->transactedChild
)
2663 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2665 stg
->transactedChild
= NULL
;
2669 /******************************************************************************
2670 * SetElementTimes (IStorage)
2672 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2674 const OLECHAR
*pwcsName
,/* [string][in] */
2675 const FILETIME
*pctime
, /* [in] */
2676 const FILETIME
*patime
, /* [in] */
2677 const FILETIME
*pmtime
) /* [in] */
2679 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2683 /******************************************************************************
2684 * SetStateBits (IStorage)
2686 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2688 DWORD grfStateBits
,/* [in] */
2689 DWORD grfMask
) /* [in] */
2691 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2694 return STG_E_REVERTED
;
2696 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2700 /******************************************************************************
2701 * Internal stream list handlers
2704 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2706 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2707 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2710 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2712 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2713 list_remove(&(strm
->StrmListEntry
));
2716 static HRESULT
StorageBaseImpl_CopyStream(
2717 StorageBaseImpl
*dst
, DirRef dst_entry
,
2718 StorageBaseImpl
*src
, DirRef src_entry
)
2723 ULARGE_INTEGER bytes_copied
;
2724 ULONG bytestocopy
, bytesread
, byteswritten
;
2726 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2730 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2732 bytes_copied
.QuadPart
= 0;
2733 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2735 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2737 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2739 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2742 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2743 data
, &byteswritten
);
2746 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2747 bytes_copied
.QuadPart
+= byteswritten
;
2755 static HRESULT
StorageBaseImpl_DupStorageTree(
2756 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2757 StorageBaseImpl
*src
, DirRef src_entry
)
2761 BOOL has_stream
=FALSE
;
2763 if (src_entry
== DIRENTRY_NULL
)
2765 *dst_entry
= DIRENTRY_NULL
;
2769 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2772 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2773 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2774 data
.size
.QuadPart
= 0;
2776 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2780 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2783 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2786 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2788 if (SUCCEEDED(hr
) && has_stream
)
2789 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2794 static HRESULT
StorageBaseImpl_CopyStorageTree(
2795 StorageBaseImpl
*dst
, DirRef dst_entry
,
2796 StorageBaseImpl
*src
, DirRef src_entry
)
2799 DirEntry src_data
, dst_data
;
2800 DirRef new_root_entry
;
2802 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2806 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2811 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2812 dst_data
.clsid
= src_data
.clsid
;
2813 dst_data
.ctime
= src_data
.ctime
;
2814 dst_data
.mtime
= src_data
.mtime
;
2815 dst_data
.dirRootEntry
= new_root_entry
;
2819 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2824 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2828 ULARGE_INTEGER zero
;
2830 if (entry
== DIRENTRY_NULL
)
2835 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2837 if (SUCCEEDED(hr
) && include_siblings
)
2838 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2840 if (SUCCEEDED(hr
) && include_siblings
)
2841 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2844 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2846 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2847 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2850 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2856 /************************************************************************
2857 * StorageImpl implementation
2858 ***********************************************************************/
2860 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2861 ULARGE_INTEGER offset
,
2866 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2869 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2870 ULARGE_INTEGER offset
,
2873 ULONG
* bytesWritten
)
2875 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2878 /******************************************************************************
2879 * StorageImpl_LoadFileHeader
2881 * This method will read in the file header
2883 static HRESULT
StorageImpl_LoadFileHeader(
2887 BYTE headerBigBlock
[HEADER_SIZE
];
2889 ULARGE_INTEGER offset
;
2894 * Get a pointer to the big block of data containing the header.
2896 offset
.u
.HighPart
= 0;
2897 offset
.u
.LowPart
= 0;
2898 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2899 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2900 hr
= STG_E_FILENOTFOUND
;
2903 * Extract the information from the header.
2908 * Check for the "magic number" signature and return an error if it is not
2911 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2913 return STG_E_OLDFORMAT
;
2916 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2918 return STG_E_INVALIDHEADER
;
2921 StorageUtl_ReadWord(
2923 OFFSET_BIGBLOCKSIZEBITS
,
2924 &This
->bigBlockSizeBits
);
2926 StorageUtl_ReadWord(
2928 OFFSET_SMALLBLOCKSIZEBITS
,
2929 &This
->smallBlockSizeBits
);
2931 StorageUtl_ReadDWord(
2933 OFFSET_BBDEPOTCOUNT
,
2934 &This
->bigBlockDepotCount
);
2936 StorageUtl_ReadDWord(
2938 OFFSET_ROOTSTARTBLOCK
,
2939 &This
->rootStartBlock
);
2941 StorageUtl_ReadDWord(
2943 OFFSET_TRANSACTIONSIG
,
2944 &This
->transactionSig
);
2946 StorageUtl_ReadDWord(
2948 OFFSET_SMALLBLOCKLIMIT
,
2949 &This
->smallBlockLimit
);
2951 StorageUtl_ReadDWord(
2953 OFFSET_SBDEPOTSTART
,
2954 &This
->smallBlockDepotStart
);
2956 StorageUtl_ReadDWord(
2958 OFFSET_EXTBBDEPOTSTART
,
2959 &This
->extBigBlockDepotStart
);
2961 StorageUtl_ReadDWord(
2963 OFFSET_EXTBBDEPOTCOUNT
,
2964 &This
->extBigBlockDepotCount
);
2966 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2968 StorageUtl_ReadDWord(
2970 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2971 &(This
->bigBlockDepotStart
[index
]));
2975 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2977 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2978 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2981 * Right now, the code is making some assumptions about the size of the
2982 * blocks, just make sure they are what we're expecting.
2984 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2985 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2986 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2988 FIXME("Broken OLE storage file? bigblock=%#lx, smallblock=%#lx, sblimit=%#lx\n",
2989 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
2990 hr
= STG_E_INVALIDHEADER
;
2999 /******************************************************************************
3000 * StorageImpl_SaveFileHeader
3002 * This method will save to the file the header
3004 static void StorageImpl_SaveFileHeader(
3007 BYTE headerBigBlock
[HEADER_SIZE
];
3009 ULARGE_INTEGER offset
;
3010 DWORD bytes_written
;
3011 DWORD major_version
, dirsectorcount
;
3013 if (This
->bigBlockSizeBits
== 0x9)
3015 else if (This
->bigBlockSizeBits
== 0xc)
3019 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3023 memset(headerBigBlock
, 0, HEADER_SIZE
);
3024 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3027 * Write the information to the header.
3029 StorageUtl_WriteWord(
3031 OFFSET_MINORVERSION
,
3034 StorageUtl_WriteWord(
3036 OFFSET_MAJORVERSION
,
3039 StorageUtl_WriteWord(
3041 OFFSET_BYTEORDERMARKER
,
3044 StorageUtl_WriteWord(
3046 OFFSET_BIGBLOCKSIZEBITS
,
3047 This
->bigBlockSizeBits
);
3049 StorageUtl_WriteWord(
3051 OFFSET_SMALLBLOCKSIZEBITS
,
3052 This
->smallBlockSizeBits
);
3054 if (major_version
>= 4)
3056 if (This
->rootBlockChain
)
3057 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3059 /* This file is being created, and it will start out with one block. */
3063 /* This field must be 0 in versions older than 4 */
3066 StorageUtl_WriteDWord(
3068 OFFSET_DIRSECTORCOUNT
,
3071 StorageUtl_WriteDWord(
3073 OFFSET_BBDEPOTCOUNT
,
3074 This
->bigBlockDepotCount
);
3076 StorageUtl_WriteDWord(
3078 OFFSET_ROOTSTARTBLOCK
,
3079 This
->rootStartBlock
);
3081 StorageUtl_WriteDWord(
3083 OFFSET_TRANSACTIONSIG
,
3084 This
->transactionSig
);
3086 StorageUtl_WriteDWord(
3088 OFFSET_SMALLBLOCKLIMIT
,
3089 This
->smallBlockLimit
);
3091 StorageUtl_WriteDWord(
3093 OFFSET_SBDEPOTSTART
,
3094 This
->smallBlockDepotStart
);
3096 StorageUtl_WriteDWord(
3098 OFFSET_SBDEPOTCOUNT
,
3099 This
->smallBlockDepotChain
?
3100 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3102 StorageUtl_WriteDWord(
3104 OFFSET_EXTBBDEPOTSTART
,
3105 This
->extBigBlockDepotStart
);
3107 StorageUtl_WriteDWord(
3109 OFFSET_EXTBBDEPOTCOUNT
,
3110 This
->extBigBlockDepotCount
);
3112 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3114 StorageUtl_WriteDWord(
3116 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3117 (This
->bigBlockDepotStart
[index
]));
3120 offset
.QuadPart
= 0;
3121 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3125 /************************************************************************
3126 * StorageImpl implementation : DirEntry methods
3127 ***********************************************************************/
3129 /******************************************************************************
3130 * StorageImpl_ReadRawDirEntry
3132 * This method will read the raw data from a directory entry in the file.
3134 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3136 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3138 ULARGE_INTEGER offset
;
3142 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3144 hr
= BlockChainStream_ReadAt(
3145 This
->rootBlockChain
,
3151 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3152 return STG_E_READFAULT
;
3157 /******************************************************************************
3158 * StorageImpl_WriteRawDirEntry
3160 * This method will write the raw data from a directory entry in the file.
3162 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3164 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3166 ULARGE_INTEGER offset
;
3169 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3171 return BlockChainStream_WriteAt(
3172 This
->rootBlockChain
,
3179 /***************************************************************************
3183 * Mark a directory entry in the file as free.
3185 static HRESULT
StorageImpl_DestroyDirEntry(
3186 StorageBaseImpl
*base
,
3189 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3190 StorageImpl
*storage
= (StorageImpl
*)base
;
3192 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3194 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3197 /******************************************************************************
3200 * Update raw directory entry data from the fields in newData.
3202 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3204 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3206 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3209 buffer
+ OFFSET_PS_NAME
,
3211 DIRENTRY_NAME_BUFFER_LEN
);
3213 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3215 StorageUtl_WriteWord(
3217 OFFSET_PS_NAMELENGTH
,
3218 newData
->sizeOfNameString
);
3220 StorageUtl_WriteDWord(
3222 OFFSET_PS_LEFTCHILD
,
3223 newData
->leftChild
);
3225 StorageUtl_WriteDWord(
3227 OFFSET_PS_RIGHTCHILD
,
3228 newData
->rightChild
);
3230 StorageUtl_WriteDWord(
3233 newData
->dirRootEntry
);
3235 StorageUtl_WriteGUID(
3240 StorageUtl_WriteDWord(
3243 newData
->ctime
.dwLowDateTime
);
3245 StorageUtl_WriteDWord(
3247 OFFSET_PS_CTIMEHIGH
,
3248 newData
->ctime
.dwHighDateTime
);
3250 StorageUtl_WriteDWord(
3253 newData
->mtime
.dwLowDateTime
);
3255 StorageUtl_WriteDWord(
3257 OFFSET_PS_MTIMEHIGH
,
3258 newData
->ctime
.dwHighDateTime
);
3260 StorageUtl_WriteDWord(
3262 OFFSET_PS_STARTBLOCK
,
3263 newData
->startingBlock
);
3265 StorageUtl_WriteDWord(
3268 newData
->size
.u
.LowPart
);
3270 StorageUtl_WriteDWord(
3272 OFFSET_PS_SIZE_HIGH
,
3273 newData
->size
.u
.HighPart
);
3276 /***************************************************************************
3280 * Reserve a directory entry in the file and initialize it.
3282 static HRESULT
StorageImpl_CreateDirEntry(
3283 StorageBaseImpl
*base
,
3284 const DirEntry
*newData
,
3287 StorageImpl
*storage
= (StorageImpl
*)base
;
3288 ULONG currentEntryIndex
= 0;
3289 ULONG newEntryIndex
= DIRENTRY_NULL
;
3291 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3292 WORD sizeOfNameString
;
3296 hr
= StorageImpl_ReadRawDirEntry(storage
,
3302 StorageUtl_ReadWord(
3304 OFFSET_PS_NAMELENGTH
,
3307 if (sizeOfNameString
== 0)
3310 * The entry exists and is available, we found it.
3312 newEntryIndex
= currentEntryIndex
;
3318 * We exhausted the directory entries, we will create more space below
3320 newEntryIndex
= currentEntryIndex
;
3322 currentEntryIndex
++;
3324 } while (newEntryIndex
== DIRENTRY_NULL
);
3327 * grow the directory stream
3331 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3332 ULARGE_INTEGER newSize
;
3334 ULONG lastEntry
= 0;
3335 ULONG blockCount
= 0;
3338 * obtain the new count of blocks in the directory stream
3340 blockCount
= BlockChainStream_GetCount(
3341 storage
->rootBlockChain
)+1;
3344 * initialize the size used by the directory stream
3346 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3349 * add a block to the directory stream
3351 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3354 * memset the empty entry in order to initialize the unused newly
3357 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3362 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3365 entryIndex
= newEntryIndex
+ 1;
3366 entryIndex
< lastEntry
;
3369 StorageImpl_WriteRawDirEntry(
3375 StorageImpl_SaveFileHeader(storage
);
3378 UpdateRawDirEntry(currentData
, newData
);
3380 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3383 *index
= newEntryIndex
;
3388 /******************************************************************************
3389 * StorageImpl_ReadDirEntry
3391 * This method will read the specified directory entry.
3393 static HRESULT
StorageImpl_ReadDirEntry(
3398 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3401 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3403 if (SUCCEEDED(readRes
))
3405 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3408 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3409 DIRENTRY_NAME_BUFFER_LEN
);
3410 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3412 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3414 StorageUtl_ReadWord(
3416 OFFSET_PS_NAMELENGTH
,
3417 &buffer
->sizeOfNameString
);
3419 StorageUtl_ReadDWord(
3421 OFFSET_PS_LEFTCHILD
,
3422 &buffer
->leftChild
);
3424 StorageUtl_ReadDWord(
3426 OFFSET_PS_RIGHTCHILD
,
3427 &buffer
->rightChild
);
3429 StorageUtl_ReadDWord(
3432 &buffer
->dirRootEntry
);
3434 StorageUtl_ReadGUID(
3439 StorageUtl_ReadDWord(
3442 &buffer
->ctime
.dwLowDateTime
);
3444 StorageUtl_ReadDWord(
3446 OFFSET_PS_CTIMEHIGH
,
3447 &buffer
->ctime
.dwHighDateTime
);
3449 StorageUtl_ReadDWord(
3452 &buffer
->mtime
.dwLowDateTime
);
3454 StorageUtl_ReadDWord(
3456 OFFSET_PS_MTIMEHIGH
,
3457 &buffer
->mtime
.dwHighDateTime
);
3459 StorageUtl_ReadDWord(
3461 OFFSET_PS_STARTBLOCK
,
3462 &buffer
->startingBlock
);
3464 StorageUtl_ReadDWord(
3467 &buffer
->size
.u
.LowPart
);
3469 if (This
->bigBlockSize
< 4096)
3471 /* Version 3 files may have junk in the high part of size. */
3472 buffer
->size
.u
.HighPart
= 0;
3476 StorageUtl_ReadDWord(
3478 OFFSET_PS_SIZE_HIGH
,
3479 &buffer
->size
.u
.HighPart
);
3486 /*********************************************************************
3487 * Write the specified directory entry to the file
3489 static HRESULT
StorageImpl_WriteDirEntry(
3492 const DirEntry
* buffer
)
3494 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3496 UpdateRawDirEntry(currentEntry
, buffer
);
3498 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3502 /************************************************************************
3503 * StorageImpl implementation : Block methods
3504 ***********************************************************************/
3506 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3508 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3511 static HRESULT
StorageImpl_ReadBigBlock(
3517 ULARGE_INTEGER ulOffset
;
3521 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3523 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3525 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3527 /* File ends during this block; fill the rest with 0's. */
3528 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3531 if (out_read
) *out_read
= read
;
3536 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3542 ULARGE_INTEGER ulOffset
;
3546 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3547 ulOffset
.QuadPart
+= offset
;
3549 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3550 *value
= lendian32toh(tmp
);
3551 return (read
== sizeof(DWORD
));
3554 static BOOL
StorageImpl_WriteBigBlock(
3559 ULARGE_INTEGER ulOffset
;
3562 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3564 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3565 return (wrote
== This
->bigBlockSize
);
3568 static BOOL
StorageImpl_WriteDWordToBigBlock(
3574 ULARGE_INTEGER ulOffset
;
3577 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3578 ulOffset
.QuadPart
+= offset
;
3580 value
= htole32(value
);
3581 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3582 return (wrote
== sizeof(DWORD
));
3585 /******************************************************************************
3586 * Storage32Impl_SmallBlocksToBigBlocks
3588 * This method will convert a small block chain to a big block chain.
3589 * The small block chain will be destroyed.
3591 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3593 SmallBlockChainStream
** ppsbChain
)
3595 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3596 ULARGE_INTEGER size
, offset
;
3597 ULONG cbRead
, cbWritten
;
3598 ULARGE_INTEGER cbTotalRead
;
3599 DirRef streamEntryRef
;
3600 HRESULT resWrite
= S_OK
;
3602 DirEntry streamEntry
;
3604 BlockChainStream
*bbTempChain
= NULL
;
3605 BlockChainStream
*bigBlockChain
= NULL
;
3608 * Create a temporary big block chain that doesn't have
3609 * an associated directory entry. This temporary chain will be
3610 * used to copy data from small blocks to big blocks.
3612 bbTempChain
= BlockChainStream_Construct(This
,
3615 if(!bbTempChain
) return NULL
;
3617 * Grow the big block chain.
3619 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3620 BlockChainStream_SetSize(bbTempChain
, size
);
3623 * Copy the contents of the small block chain to the big block chain
3624 * by small block size increments.
3626 offset
.u
.LowPart
= 0;
3627 offset
.u
.HighPart
= 0;
3628 cbTotalRead
.QuadPart
= 0;
3630 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3633 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3635 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3638 if (FAILED(resRead
))
3643 cbTotalRead
.QuadPart
+= cbRead
;
3645 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3651 if (FAILED(resWrite
))
3654 offset
.u
.LowPart
+= cbRead
;
3658 resRead
= STG_E_READFAULT
;
3661 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3662 HeapFree(GetProcessHeap(),0,buffer
);
3664 size
.u
.HighPart
= 0;
3667 if (FAILED(resRead
) || FAILED(resWrite
))
3669 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead
, resWrite
);
3670 BlockChainStream_SetSize(bbTempChain
, size
);
3671 BlockChainStream_Destroy(bbTempChain
);
3676 * Destroy the small block chain.
3678 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3679 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3680 SmallBlockChainStream_Destroy(*ppsbChain
);
3684 * Change the directory entry. This chain is now a big block chain
3685 * and it doesn't reside in the small blocks chain anymore.
3687 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3689 streamEntry
.startingBlock
= bbHeadOfChain
;
3691 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3694 * Destroy the temporary entryless big block chain.
3695 * Create a new big block chain associated with this entry.
3697 BlockChainStream_Destroy(bbTempChain
);
3698 bigBlockChain
= BlockChainStream_Construct(This
,
3702 return bigBlockChain
;
3705 /******************************************************************************
3706 * Storage32Impl_BigBlocksToSmallBlocks
3708 * This method will convert a big block chain to a small block chain.
3709 * The big block chain will be destroyed on success.
3711 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3713 BlockChainStream
** ppbbChain
,
3714 ULARGE_INTEGER newSize
)
3716 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3717 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3718 DirRef streamEntryRef
;
3719 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3720 DirEntry streamEntry
;
3722 SmallBlockChainStream
* sbTempChain
;
3724 TRACE("%p %p\n", This
, ppbbChain
);
3726 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3732 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3733 size
= BlockChainStream_GetSize(*ppbbChain
);
3734 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3736 offset
.u
.HighPart
= 0;
3737 offset
.u
.LowPart
= 0;
3738 cbTotalRead
.QuadPart
= 0;
3739 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3740 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3742 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3743 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3751 cbTotalRead
.QuadPart
+= cbRead
;
3753 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3754 cbRead
, buffer
, &cbWritten
);
3756 if(FAILED(resWrite
))
3759 offset
.u
.LowPart
+= cbRead
;
3763 resRead
= STG_E_READFAULT
;
3767 HeapFree(GetProcessHeap(), 0, buffer
);
3769 size
.u
.HighPart
= 0;
3772 if(FAILED(resRead
) || FAILED(resWrite
))
3774 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead
, resWrite
);
3775 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3776 SmallBlockChainStream_Destroy(sbTempChain
);
3780 /* destroy the original big block chain */
3781 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3782 BlockChainStream_SetSize(*ppbbChain
, size
);
3783 BlockChainStream_Destroy(*ppbbChain
);
3786 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3787 streamEntry
.startingBlock
= sbHeadOfChain
;
3788 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3790 SmallBlockChainStream_Destroy(sbTempChain
);
3791 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3794 /******************************************************************************
3795 * Storage32Impl_AddBlockDepot
3797 * This will create a depot block, essentially it is a block initialized
3800 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3802 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3803 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3804 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3805 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3808 * Initialize blocks as free
3810 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3812 /* Reserve the range lock sector */
3813 if (depotIndex
== rangeLockDepot
)
3815 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3818 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3821 /******************************************************************************
3822 * Storage32Impl_GetExtDepotBlock
3824 * Returns the index of the block that corresponds to the specified depot
3825 * index. This method is only for depot indexes equal or greater than
3826 * COUNT_BBDEPOTINHEADER.
3828 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3830 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3831 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3832 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3833 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3834 ULONG blockIndex
= BLOCK_UNUSED
;
3835 ULONG extBlockIndex
;
3836 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3837 int index
, num_blocks
;
3839 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3841 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3842 return BLOCK_UNUSED
;
3844 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3846 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3848 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3850 num_blocks
= This
->bigBlockSize
/ 4;
3852 for (index
= 0; index
< num_blocks
; index
++)
3854 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3855 This
->extBlockDepotCached
[index
] = blockIndex
;
3858 This
->indexExtBlockDepotCached
= extBlockCount
;
3861 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3866 /******************************************************************************
3867 * Storage32Impl_SetExtDepotBlock
3869 * Associates the specified block index to the specified depot index.
3870 * This method is only for depot indexes equal or greater than
3871 * COUNT_BBDEPOTINHEADER.
3873 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3875 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3876 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3877 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3878 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3879 ULONG extBlockIndex
;
3881 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3883 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3885 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3887 if (extBlockIndex
!= BLOCK_UNUSED
)
3889 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3890 extBlockOffset
* sizeof(ULONG
),
3894 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3896 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3900 /******************************************************************************
3901 * Storage32Impl_AddExtBlockDepot
3903 * Creates an extended depot block.
3905 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3907 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3908 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3909 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3910 ULONG index
= BLOCK_UNUSED
;
3911 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3912 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3913 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3915 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3916 blocksPerDepotBlock
;
3918 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3921 * The first extended block.
3923 This
->extBigBlockDepotStart
= index
;
3928 * Find the last existing extended block.
3930 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3933 * Add the new extended block to the chain.
3935 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3940 * Initialize this block.
3942 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3943 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3945 /* Add the block to our cache. */
3946 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3948 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3949 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3951 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3952 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3954 This
->extBigBlockDepotLocations
= new_cache
;
3955 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3957 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3962 /************************************************************************
3963 * StorageImpl_GetNextBlockInChain
3965 * This method will retrieve the block index of the next big block in
3968 * Params: This - Pointer to the Storage object.
3969 * blockIndex - Index of the block to retrieve the chain
3971 * nextBlockIndex - receives the return value.
3973 * Returns: This method returns the index of the next block in the chain.
3974 * It will return the constants:
3975 * BLOCK_SPECIAL - If the block given was not part of a
3977 * BLOCK_END_OF_CHAIN - If the block given was the last in
3979 * BLOCK_UNUSED - If the block given was not past of a chain
3981 * BLOCK_EXTBBDEPOT - This block is part of the extended
3984 * See Windows documentation for more details on IStorage methods.
3986 static HRESULT
StorageImpl_GetNextBlockInChain(
3989 ULONG
* nextBlockIndex
)
3991 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3992 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3993 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3994 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3996 ULONG depotBlockIndexPos
;
3997 int index
, num_blocks
;
3999 *nextBlockIndex
= BLOCK_SPECIAL
;
4001 if(depotBlockCount
>= This
->bigBlockDepotCount
)
4003 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
, This
->bigBlockDepotCount
);
4004 return STG_E_READFAULT
;
4008 * Cache the currently accessed depot block.
4010 if (depotBlockCount
!= This
->indexBlockDepotCached
)
4012 This
->indexBlockDepotCached
= depotBlockCount
;
4014 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4016 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4021 * We have to look in the extended depot.
4023 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4026 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4029 return STG_E_READFAULT
;
4031 num_blocks
= This
->bigBlockSize
/ 4;
4033 for (index
= 0; index
< num_blocks
; index
++)
4035 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4036 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4040 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4045 /******************************************************************************
4046 * Storage32Impl_GetNextExtendedBlock
4048 * Given an extended block this method will return the next extended block.
4051 * The last ULONG of an extended block is the block index of the next
4052 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4056 * - The index of the next extended block
4057 * - BLOCK_UNUSED: there is no next extended block.
4058 * - Any other return values denotes failure.
4060 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4062 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4063 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4065 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4068 return nextBlockIndex
;
4071 /******************************************************************************
4072 * StorageImpl_SetNextBlockInChain
4074 * This method will write the index of the specified block's next block
4075 * in the big block depot.
4077 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4080 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4081 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4082 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4085 static void StorageImpl_SetNextBlockInChain(
4090 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4091 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4092 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4093 ULONG depotBlockIndexPos
;
4095 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4096 assert(blockIndex
!= nextBlock
);
4098 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4099 /* This should never happen (storage file format spec forbids it), but
4100 * older versions of Wine may have generated broken files. We don't want to
4101 * assert and potentially lose data, but we do want to know if this ever
4102 * happens in a newly-created file. */
4103 ERR("Using range lock page\n");
4105 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4107 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4112 * We have to look in the extended depot.
4114 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4117 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4120 * Update the cached block depot, if necessary.
4122 if (depotBlockCount
== This
->indexBlockDepotCached
)
4124 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4128 /******************************************************************************
4129 * StorageImpl_GetNextFreeBigBlock
4131 * Returns the index of the next free big block.
4132 * If the big block depot is filled, this method will enlarge it.
4135 static ULONG
StorageImpl_GetNextFreeBigBlock(
4136 StorageImpl
* This
, ULONG neededAddNumBlocks
)
4138 ULONG depotBlockIndexPos
;
4139 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4140 ULONG depotBlockOffset
;
4141 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4142 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4144 ULONG freeBlock
= BLOCK_UNUSED
;
4146 ULARGE_INTEGER neededSize
;
4149 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4150 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4153 * Scan the entire big block depot until we find a block marked free
4155 while (nextBlockIndex
!= BLOCK_UNUSED
)
4157 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4159 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4162 * Grow the primary depot.
4164 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4166 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4169 * Add a block depot.
4171 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4172 This
->bigBlockDepotCount
++;
4173 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4176 * Flag it as a block depot.
4178 StorageImpl_SetNextBlockInChain(This
,
4182 /* Save new header information.
4184 StorageImpl_SaveFileHeader(This
);
4189 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4191 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4194 * Grow the extended depot.
4196 ULONG extIndex
= BLOCK_UNUSED
;
4197 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4198 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4200 if (extBlockOffset
== 0)
4202 /* We need an extended block.
4204 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4205 This
->extBigBlockDepotCount
++;
4206 depotBlockIndexPos
= extIndex
+ 1;
4209 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4212 * Add a block depot and mark it in the extended block.
4214 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4215 This
->bigBlockDepotCount
++;
4216 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4218 /* Flag the block depot.
4220 StorageImpl_SetNextBlockInChain(This
,
4224 /* If necessary, flag the extended depot block.
4226 if (extIndex
!= BLOCK_UNUSED
)
4227 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4229 /* Save header information.
4231 StorageImpl_SaveFileHeader(This
);
4235 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4239 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4240 ( nextBlockIndex
!= BLOCK_UNUSED
))
4242 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4244 if (nextBlockIndex
== BLOCK_UNUSED
)
4246 freeBlock
= (depotIndex
* blocksPerDepot
) +
4247 (depotBlockOffset
/sizeof(ULONG
));
4250 depotBlockOffset
+= sizeof(ULONG
);
4255 depotBlockOffset
= 0;
4259 * make sure that the block physically exists before using it
4261 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
* neededAddNumBlocks
;
4263 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4265 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4266 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4268 This
->prevFreeBlock
= freeBlock
;
4273 /******************************************************************************
4274 * StorageImpl_FreeBigBlock
4276 * This method will flag the specified block as free in the big block depot.
4278 static void StorageImpl_FreeBigBlock(
4282 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4284 if (blockIndex
< This
->prevFreeBlock
)
4285 This
->prevFreeBlock
= blockIndex
;
4289 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4290 DirRef index
, const DirEntry
*data
)
4292 StorageImpl
*This
= (StorageImpl
*)base
;
4293 return StorageImpl_WriteDirEntry(This
, index
, data
);
4296 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4297 DirRef index
, DirEntry
*data
)
4299 StorageImpl
*This
= (StorageImpl
*)base
;
4300 return StorageImpl_ReadDirEntry(This
, index
, data
);
4303 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4307 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4309 if (!This
->blockChainCache
[i
])
4311 return &This
->blockChainCache
[i
];
4315 i
= This
->blockChainToEvict
;
4317 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4318 This
->blockChainCache
[i
] = NULL
;
4320 This
->blockChainToEvict
++;
4321 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4322 This
->blockChainToEvict
= 0;
4324 return &This
->blockChainCache
[i
];
4327 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4330 int i
, free_index
=-1;
4332 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4334 if (!This
->blockChainCache
[i
])
4336 if (free_index
== -1) free_index
= i
;
4338 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4340 return &This
->blockChainCache
[i
];
4344 if (free_index
== -1)
4346 free_index
= This
->blockChainToEvict
;
4348 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4349 This
->blockChainCache
[free_index
] = NULL
;
4351 This
->blockChainToEvict
++;
4352 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4353 This
->blockChainToEvict
= 0;
4356 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4357 return &This
->blockChainCache
[free_index
];
4360 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4364 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4366 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4368 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4369 This
->blockChainCache
[i
] = NULL
;
4375 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4376 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4378 StorageImpl
*This
= (StorageImpl
*)base
;
4383 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4384 if (FAILED(hr
)) return hr
;
4386 if (data
.size
.QuadPart
== 0)
4392 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4394 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4401 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4403 SmallBlockChainStream
*stream
;
4405 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4406 if (!stream
) return E_OUTOFMEMORY
;
4408 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4410 SmallBlockChainStream_Destroy(stream
);
4416 BlockChainStream
*stream
= NULL
;
4418 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4419 if (!stream
) return E_OUTOFMEMORY
;
4421 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4427 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4428 ULARGE_INTEGER newsize
)
4430 StorageImpl
*This
= (StorageImpl
*)base
;
4433 SmallBlockChainStream
*smallblock
=NULL
;
4434 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4436 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4437 if (FAILED(hr
)) return hr
;
4439 /* In simple mode keep the stream size above the small block limit */
4440 if (This
->base
.openFlags
& STGM_SIMPLE
)
4441 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4443 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4446 /* Create a block chain object of the appropriate type */
4447 if (data
.size
.QuadPart
== 0)
4449 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4451 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4452 if (!smallblock
) return E_OUTOFMEMORY
;
4456 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4457 bigblock
= *pbigblock
;
4458 if (!bigblock
) return E_OUTOFMEMORY
;
4461 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4463 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4464 if (!smallblock
) return E_OUTOFMEMORY
;
4468 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4469 bigblock
= *pbigblock
;
4470 if (!bigblock
) return E_OUTOFMEMORY
;
4473 /* Change the block chain type if necessary. */
4474 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4476 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4479 SmallBlockChainStream_Destroy(smallblock
);
4483 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4484 *pbigblock
= bigblock
;
4486 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4488 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4493 /* Set the size of the block chain. */
4496 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4497 SmallBlockChainStream_Destroy(smallblock
);
4501 BlockChainStream_SetSize(bigblock
, newsize
);
4504 /* Set the size in the directory entry. */
4505 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4508 data
.size
= newsize
;
4510 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4515 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4516 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4518 StorageImpl
*This
= (StorageImpl
*)base
;
4521 ULARGE_INTEGER newSize
;
4523 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4524 if (FAILED(hr
)) return hr
;
4526 /* Grow the stream if necessary */
4527 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4529 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4531 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4535 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4536 if (FAILED(hr
)) return hr
;
4539 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4541 SmallBlockChainStream
*stream
;
4543 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4544 if (!stream
) return E_OUTOFMEMORY
;
4546 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4548 SmallBlockChainStream_Destroy(stream
);
4554 BlockChainStream
*stream
;
4556 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4557 if (!stream
) return E_OUTOFMEMORY
;
4559 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4563 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4566 StorageImpl
*This
= (StorageImpl
*)base
;
4567 DirEntry dst_data
, src_data
;
4570 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4573 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4577 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4578 dst_data
.startingBlock
= src_data
.startingBlock
;
4579 dst_data
.size
= src_data
.size
;
4581 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4587 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4590 DirEntry currentEntry
;
4591 DirRef currentEntryRef
;
4592 BlockChainStream
*blockChainStream
;
4596 ULARGE_INTEGER size
;
4597 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4599 /* Discard any existing data. */
4601 ILockBytes_SetSize(This
->lockBytes
, size
);
4604 * Initialize all header variables:
4605 * - The big block depot consists of one block and it is at block 0
4606 * - The directory table starts at block 1
4607 * - There is no small block depot
4609 memset( This
->bigBlockDepotStart
,
4611 sizeof(This
->bigBlockDepotStart
));
4613 This
->bigBlockDepotCount
= 1;
4614 This
->bigBlockDepotStart
[0] = 0;
4615 This
->rootStartBlock
= 1;
4616 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4617 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4618 if (This
->bigBlockSize
== 4096)
4619 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4621 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4622 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4623 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4624 This
->extBigBlockDepotCount
= 0;
4626 StorageImpl_SaveFileHeader(This
);
4629 * Add one block for the big block depot and one block for the directory table
4631 size
.u
.HighPart
= 0;
4632 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4633 ILockBytes_SetSize(This
->lockBytes
, size
);
4636 * Initialize the big block depot
4638 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4639 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4640 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4641 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4646 * Load the header for the file.
4648 hr
= StorageImpl_LoadFileHeader(This
);
4657 * There is no block depot cached yet.
4659 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4660 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4663 * Start searching for free blocks with block 0.
4665 This
->prevFreeBlock
= 0;
4667 This
->firstFreeSmallBlock
= 0;
4669 /* Read the extended big block depot locations. */
4670 if (This
->extBigBlockDepotCount
!= 0)
4672 ULONG current_block
= This
->extBigBlockDepotStart
;
4673 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4676 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4677 if (!This
->extBigBlockDepotLocations
)
4679 return E_OUTOFMEMORY
;
4682 This
->extBigBlockDepotLocationsSize
= cache_size
;
4684 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4686 if (current_block
== BLOCK_END_OF_CHAIN
)
4688 WARN("File has too few extended big block depot blocks.\n");
4689 return STG_E_DOCFILECORRUPT
;
4691 This
->extBigBlockDepotLocations
[i
] = current_block
;
4692 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4697 This
->extBigBlockDepotLocations
= NULL
;
4698 This
->extBigBlockDepotLocationsSize
= 0;
4702 * Create the block chain abstractions.
4704 if(!(blockChainStream
=
4705 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4707 return STG_E_READFAULT
;
4710 BlockChainStream_Destroy(This
->rootBlockChain
);
4711 This
->rootBlockChain
= blockChainStream
;
4713 if(!(blockChainStream
=
4714 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4717 return STG_E_READFAULT
;
4720 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4721 This
->smallBlockDepotChain
= blockChainStream
;
4724 * Write the root storage entry (memory only)
4730 * Initialize the directory table
4732 memset(&rootEntry
, 0, sizeof(rootEntry
));
4733 lstrcpyW(rootEntry
.name
, L
"Root Entry");
4734 rootEntry
.sizeOfNameString
= sizeof(L
"Root Entry");
4735 rootEntry
.stgType
= STGTY_ROOT
;
4736 rootEntry
.leftChild
= DIRENTRY_NULL
;
4737 rootEntry
.rightChild
= DIRENTRY_NULL
;
4738 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4739 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4740 rootEntry
.size
.u
.HighPart
= 0;
4741 rootEntry
.size
.u
.LowPart
= 0;
4743 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4747 * Find the ID of the root storage.
4749 currentEntryRef
= 0;
4753 hr
= StorageImpl_ReadDirEntry(
4760 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4761 (currentEntry
.stgType
== STGTY_ROOT
) )
4763 This
->base
.storageDirEntry
= currentEntryRef
;
4769 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4773 return STG_E_READFAULT
;
4777 * Create the block chain abstraction for the small block root chain.
4779 if(!(blockChainStream
=
4780 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4782 return STG_E_READFAULT
;
4785 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4786 This
->smallBlockRootChain
= blockChainStream
;
4791 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4793 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4794 This
->blockChainCache
[i
] = NULL
;
4801 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4802 ULONG
* result
, BOOL refresh
)
4804 StorageImpl
*This
= (StorageImpl
*)base
;
4806 DWORD oldTransactionSig
= This
->transactionSig
;
4810 ULARGE_INTEGER offset
;
4814 offset
.u
.HighPart
= 0;
4815 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4816 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4820 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4822 if (oldTransactionSig
!= This
->transactionSig
)
4824 /* Someone else wrote to this, so toss all cached information. */
4825 TRACE("signature changed\n");
4827 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4831 This
->transactionSig
= oldTransactionSig
;
4835 *result
= This
->transactionSig
;
4840 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4843 StorageImpl
*This
= (StorageImpl
*)base
;
4845 This
->transactionSig
= value
;
4846 StorageImpl_SaveFileHeader(This
);
4851 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4852 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4854 if ((dwLockType
& This
->locks_supported
) == 0)
4856 if (supported
) *supported
= FALSE
;
4860 if (supported
) *supported
= TRUE
;
4861 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4864 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4865 ULARGE_INTEGER cb
, DWORD dwLockType
)
4867 if ((dwLockType
& This
->locks_supported
) == 0)
4870 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4873 /* Internal function */
4874 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4875 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4879 DWORD start_time
= GetTickCount();
4880 DWORD last_sanity_check
= start_time
;
4881 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4883 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4884 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4888 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4890 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4892 DWORD current_time
= GetTickCount();
4893 if (current_time
- start_time
>= 20000)
4898 if (current_time
- last_sanity_check
>= 500)
4900 /* Any storage implementation with the file open in a
4901 * shared mode should not lock these bytes for writing. However,
4902 * some programs (LibreOffice Writer) will keep ALL bytes locked
4903 * when opening in exclusive mode. We can use a read lock to
4904 * detect this case early, and not hang a full 20 seconds.
4906 * This can collide with another attempt to open the file in
4907 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4908 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4909 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4913 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4914 hr
= STG_E_ACCESSDENIED
;
4917 last_sanity_check
= current_time
;
4920 if (delay
< 150) delay
++;
4922 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4927 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4929 StorageImpl
*This
= (StorageImpl
*)base
;
4931 ULARGE_INTEGER offset
, cb
;
4935 /* Synchronous grab of second priority range, the commit lock, and the
4936 * lock-checking lock. */
4937 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4938 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4942 offset
.QuadPart
= RANGELOCK_COMMIT
;
4946 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4951 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4953 StorageImpl
*This
= (StorageImpl
*)base
;
4955 ULARGE_INTEGER offset
, cb
;
4959 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4960 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4964 offset
.QuadPart
= RANGELOCK_COMMIT
;
4968 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4973 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4975 StorageImpl
*This
= (StorageImpl
*) iface
;
4979 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
4981 *result
= statstg
.pwcsName
;
4986 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
4987 ULONG end
, HRESULT fail_hr
)
4990 ULARGE_INTEGER offset
, cb
;
4992 offset
.QuadPart
= start
;
4993 cb
.QuadPart
= 1 + end
- start
;
4995 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4996 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5004 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
5008 ULARGE_INTEGER offset
, cb
;
5012 for (i
=start
; i
<=end
; i
++)
5014 offset
.QuadPart
= i
;
5015 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5016 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
5022 for (j
= 0; j
< ARRAY_SIZE(This
->locked_bytes
); j
++)
5024 if (This
->locked_bytes
[j
] == 0)
5026 This
->locked_bytes
[j
] = i
;
5035 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5038 ULARGE_INTEGER offset
;
5040 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5043 if (openFlags
& STGM_NOSNAPSHOT
)
5045 /* STGM_NOSNAPSHOT implies deny write */
5046 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5047 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5050 /* Wrap all other locking inside a single lock so we can check ranges safely */
5051 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5053 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5055 /* If the ILockBytes doesn't support locking that's ok. */
5056 if (!supported
) return S_OK
;
5057 else if (FAILED(hr
)) return hr
;
5061 /* First check for any conflicting locks. */
5062 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5063 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5065 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5066 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5068 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5069 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5071 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5072 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5074 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5075 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5077 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5079 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5082 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5085 /* Then grab our locks. */
5086 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5088 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5090 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5093 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5094 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5096 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5097 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5099 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5100 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5102 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5103 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5105 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5106 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5108 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5110 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5115 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5117 StorageImpl
*This
= (StorageImpl
*)storage
;
5120 TRACE("(%p)\n", This
);
5122 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5125 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5128 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5130 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5131 if (This
->blockChainCache
[i
])
5132 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5135 hr
= ILockBytes_Flush(This
->lockBytes
);
5140 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5142 StorageImpl
*This
= (StorageImpl
*) iface
;
5144 StorageBaseImpl_DeleteAll(&This
->base
);
5146 This
->base
.reverted
= TRUE
;
5149 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5151 StorageImpl
*This
= (StorageImpl
*) iface
;
5153 TRACE("(%p)\n", This
);
5155 StorageImpl_Flush(iface
);
5157 StorageImpl_Invalidate(iface
);
5159 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5161 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5162 BlockChainStream_Destroy(This
->rootBlockChain
);
5163 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5165 for (i
= 0; i
< BLOCKCHAIN_CACHE_SIZE
; i
++)
5166 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5168 for (i
= 0; i
< ARRAY_SIZE(This
->locked_bytes
); i
++)
5170 ULARGE_INTEGER offset
, cb
;
5172 if (This
->locked_bytes
[i
] != 0)
5174 offset
.QuadPart
= This
->locked_bytes
[i
];
5175 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5179 if (This
->lockBytes
)
5180 ILockBytes_Release(This
->lockBytes
);
5181 HeapFree(GetProcessHeap(), 0, This
);
5185 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5187 StorageImpl_Destroy
,
5188 StorageImpl_Invalidate
,
5190 StorageImpl_GetFilename
,
5191 StorageImpl_CreateDirEntry
,
5192 StorageImpl_BaseWriteDirEntry
,
5193 StorageImpl_BaseReadDirEntry
,
5194 StorageImpl_DestroyDirEntry
,
5195 StorageImpl_StreamReadAt
,
5196 StorageImpl_StreamWriteAt
,
5197 StorageImpl_StreamSetSize
,
5198 StorageImpl_StreamLink
,
5199 StorageImpl_GetTransactionSig
,
5200 StorageImpl_SetTransactionSig
,
5201 StorageImpl_LockTransaction
,
5202 StorageImpl_UnlockTransaction
5207 * Virtual function table for the IStorageBaseImpl class.
5209 static const IStorageVtbl StorageImpl_Vtbl
=
5211 StorageBaseImpl_QueryInterface
,
5212 StorageBaseImpl_AddRef
,
5213 StorageBaseImpl_Release
,
5214 StorageBaseImpl_CreateStream
,
5215 StorageBaseImpl_OpenStream
,
5216 StorageBaseImpl_CreateStorage
,
5217 StorageBaseImpl_OpenStorage
,
5218 StorageBaseImpl_CopyTo
,
5219 StorageBaseImpl_MoveElementTo
,
5220 StorageBaseImpl_Commit
,
5221 StorageBaseImpl_Revert
,
5222 StorageBaseImpl_EnumElements
,
5223 StorageBaseImpl_DestroyElement
,
5224 StorageBaseImpl_RenameElement
,
5225 StorageBaseImpl_SetElementTimes
,
5226 StorageBaseImpl_SetClass
,
5227 StorageBaseImpl_SetStateBits
,
5228 StorageBaseImpl_Stat
5231 static HRESULT
StorageImpl_Construct(
5239 StorageImpl
** result
)
5245 if ( FAILED( validateSTGM(openFlags
) ))
5246 return STG_E_INVALIDFLAG
;
5248 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5250 return E_OUTOFMEMORY
;
5252 memset(This
, 0, sizeof(StorageImpl
));
5254 list_init(&This
->base
.strmHead
);
5256 list_init(&This
->base
.storageHead
);
5258 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5259 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5260 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5261 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5262 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5264 This
->base
.create
= create
;
5266 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5267 This
->base
.lockingrole
= SWMR_Writer
;
5268 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5269 This
->base
.lockingrole
= SWMR_Reader
;
5271 This
->base
.lockingrole
= SWMR_None
;
5273 This
->base
.reverted
= FALSE
;
5276 * Initialize the big block cache.
5278 This
->bigBlockSize
= sector_size
;
5279 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5281 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5284 This
->lockBytes
= pLkbyt
;
5285 ILockBytes_AddRef(pLkbyt
);
5289 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5293 This
->locks_supported
= stat
.grfLocksSupported
;
5295 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5296 This
->locks_supported
&= ~WINE_LOCK_READ
;
5298 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5302 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5306 IStorage_Release(&This
->base
.IStorage_iface
);
5311 StorageImpl_Flush(&This
->base
);
5319 /************************************************************************
5320 * StorageInternalImpl implementation
5321 ***********************************************************************/
5323 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5325 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5327 if (!This
->base
.reverted
)
5329 TRACE("Storage invalidated (stg=%p)\n", This
);
5331 This
->base
.reverted
= TRUE
;
5333 This
->parentStorage
= NULL
;
5335 StorageBaseImpl_DeleteAll(&This
->base
);
5337 list_remove(&This
->ParentListEntry
);
5341 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5343 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5345 StorageInternalImpl_Invalidate(&This
->base
);
5347 HeapFree(GetProcessHeap(), 0, This
);
5350 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5352 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5354 return StorageBaseImpl_Flush(This
->parentStorage
);
5357 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5359 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5361 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5364 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5365 const DirEntry
*newData
, DirRef
*index
)
5367 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5369 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5373 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5374 DirRef index
, const DirEntry
*data
)
5376 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5378 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5382 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5383 DirRef index
, DirEntry
*data
)
5385 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5387 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5391 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5394 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5396 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5400 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5401 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5403 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5405 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5406 index
, offset
, size
, buffer
, bytesRead
);
5409 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5410 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5412 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5414 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5415 index
, offset
, size
, buffer
, bytesWritten
);
5418 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5419 DirRef index
, ULARGE_INTEGER newsize
)
5421 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5423 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5427 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5428 DirRef dst
, DirRef src
)
5430 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5432 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5436 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5437 ULONG
* result
, BOOL refresh
)
5442 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5448 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5453 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5458 /******************************************************************************
5460 ** StorageInternalImpl_Commit
5463 static HRESULT WINAPI
StorageInternalImpl_Commit(
5465 DWORD grfCommitFlags
) /* [in] */
5467 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5468 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
5469 return StorageBaseImpl_Flush(This
);
5472 /******************************************************************************
5474 ** StorageInternalImpl_Revert
5477 static HRESULT WINAPI
StorageInternalImpl_Revert(
5480 FIXME("(%p): stub\n", iface
);
5485 * Virtual function table for the StorageInternalImpl class.
5487 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5489 StorageBaseImpl_QueryInterface
,
5490 StorageBaseImpl_AddRef
,
5491 StorageBaseImpl_Release
,
5492 StorageBaseImpl_CreateStream
,
5493 StorageBaseImpl_OpenStream
,
5494 StorageBaseImpl_CreateStorage
,
5495 StorageBaseImpl_OpenStorage
,
5496 StorageBaseImpl_CopyTo
,
5497 StorageBaseImpl_MoveElementTo
,
5498 StorageInternalImpl_Commit
,
5499 StorageInternalImpl_Revert
,
5500 StorageBaseImpl_EnumElements
,
5501 StorageBaseImpl_DestroyElement
,
5502 StorageBaseImpl_RenameElement
,
5503 StorageBaseImpl_SetElementTimes
,
5504 StorageBaseImpl_SetClass
,
5505 StorageBaseImpl_SetStateBits
,
5506 StorageBaseImpl_Stat
5509 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5511 StorageInternalImpl_Destroy
,
5512 StorageInternalImpl_Invalidate
,
5513 StorageInternalImpl_Flush
,
5514 StorageInternalImpl_GetFilename
,
5515 StorageInternalImpl_CreateDirEntry
,
5516 StorageInternalImpl_WriteDirEntry
,
5517 StorageInternalImpl_ReadDirEntry
,
5518 StorageInternalImpl_DestroyDirEntry
,
5519 StorageInternalImpl_StreamReadAt
,
5520 StorageInternalImpl_StreamWriteAt
,
5521 StorageInternalImpl_StreamSetSize
,
5522 StorageInternalImpl_StreamLink
,
5523 StorageInternalImpl_GetTransactionSig
,
5524 StorageInternalImpl_SetTransactionSig
,
5525 StorageInternalImpl_LockTransaction
,
5526 StorageInternalImpl_UnlockTransaction
5529 static StorageInternalImpl
* StorageInternalImpl_Construct(
5530 StorageBaseImpl
* parentStorage
,
5532 DirRef storageDirEntry
)
5534 StorageInternalImpl
* newStorage
;
5536 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5540 list_init(&newStorage
->base
.strmHead
);
5542 list_init(&newStorage
->base
.storageHead
);
5545 * Initialize the virtual function table.
5547 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5548 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5549 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5550 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5552 newStorage
->base
.reverted
= FALSE
;
5554 newStorage
->base
.ref
= 1;
5556 newStorage
->parentStorage
= parentStorage
;
5559 * Keep a reference to the directory entry of this storage
5561 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5563 newStorage
->base
.create
= FALSE
;
5572 /************************************************************************
5573 * TransactedSnapshotImpl implementation
5574 ***********************************************************************/
5576 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5578 DirRef result
=This
->firstFreeEntry
;
5580 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5583 if (result
== This
->entries_size
)
5585 ULONG new_size
= This
->entries_size
* 2;
5586 TransactedDirEntry
*new_entries
;
5588 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5589 if (!new_entries
) return DIRENTRY_NULL
;
5591 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5592 HeapFree(GetProcessHeap(), 0, This
->entries
);
5594 This
->entries
= new_entries
;
5595 This
->entries_size
= new_size
;
5598 This
->entries
[result
].inuse
= TRUE
;
5600 This
->firstFreeEntry
= result
+1;
5605 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5606 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5608 DirRef stubEntryRef
;
5609 TransactedDirEntry
*entry
;
5611 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5613 if (stubEntryRef
!= DIRENTRY_NULL
)
5615 entry
= &This
->entries
[stubEntryRef
];
5617 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5619 entry
->read
= FALSE
;
5622 return stubEntryRef
;
5625 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5626 TransactedSnapshotImpl
*This
, DirRef entry
)
5631 if (!This
->entries
[entry
].read
)
5633 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5634 This
->entries
[entry
].transactedParentEntry
,
5637 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5639 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5641 if (data
.leftChild
== DIRENTRY_NULL
)
5645 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5647 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5649 if (data
.rightChild
== DIRENTRY_NULL
)
5653 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5655 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5657 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5663 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5664 This
->entries
[entry
].read
= TRUE
;
5671 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5672 TransactedSnapshotImpl
*This
, DirRef entry
)
5676 if (!This
->entries
[entry
].stream_dirty
)
5678 DirEntry new_entrydata
;
5680 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5681 new_entrydata
.name
[0] = 'S';
5682 new_entrydata
.sizeOfNameString
= 1;
5683 new_entrydata
.stgType
= STGTY_STREAM
;
5684 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5685 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5686 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5687 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5689 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5690 &This
->entries
[entry
].stream_entry
);
5692 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5694 hr
= StorageBaseImpl_CopyStream(
5695 This
->scratch
, This
->entries
[entry
].stream_entry
,
5696 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5699 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5703 This
->entries
[entry
].stream_dirty
= TRUE
;
5705 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5707 /* Since this entry is modified, and we aren't using its stream data, we
5708 * no longer care about the original entry. */
5710 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5712 if (delete_ref
!= DIRENTRY_NULL
)
5713 This
->entries
[delete_ref
].deleted
= TRUE
;
5715 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5722 /* Find the first entry in a depth-first traversal. */
5723 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5724 TransactedSnapshotImpl
* This
, DirRef parent
)
5726 DirRef cursor
, prev
;
5727 TransactedDirEntry
*entry
;
5730 entry
= &This
->entries
[cursor
];
5733 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5736 cursor
= entry
->data
.leftChild
;
5737 entry
= &This
->entries
[cursor
];
5738 entry
->parent
= prev
;
5740 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5743 cursor
= entry
->data
.rightChild
;
5744 entry
= &This
->entries
[cursor
];
5745 entry
->parent
= prev
;
5747 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5750 cursor
= entry
->data
.dirRootEntry
;
5751 entry
= &This
->entries
[cursor
];
5752 entry
->parent
= prev
;
5761 /* Find the next entry in a depth-first traversal. */
5762 static DirRef
TransactedSnapshotImpl_FindNextChild(
5763 TransactedSnapshotImpl
* This
, DirRef current
)
5766 TransactedDirEntry
*parent_entry
;
5768 parent
= This
->entries
[current
].parent
;
5769 parent_entry
= &This
->entries
[parent
];
5771 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5773 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5775 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5776 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5779 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5781 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5782 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5789 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5790 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5791 TransactedSnapshotImpl
* This
, DirRef entry
)
5793 return entry
!= DIRENTRY_NULL
&&
5794 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5797 /* Destroy the entries created by CopyTree. */
5798 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5799 TransactedSnapshotImpl
* This
, DirRef stop
)
5802 TransactedDirEntry
*entry
;
5803 ULARGE_INTEGER zero
;
5807 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5810 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5812 if (cursor
== DIRENTRY_NULL
)
5815 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5817 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5819 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5821 entry
= &This
->entries
[cursor
];
5823 if (entry
->stream_dirty
)
5824 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5825 entry
->newTransactedParentEntry
, zero
);
5827 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5828 entry
->newTransactedParentEntry
);
5830 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5833 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5837 /* Make a copy of our edited tree that we can use in the parent. */
5838 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5841 TransactedDirEntry
*entry
;
5844 cursor
= This
->base
.storageDirEntry
;
5845 entry
= &This
->entries
[cursor
];
5846 entry
->parent
= DIRENTRY_NULL
;
5847 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5849 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5852 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5854 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5855 entry
= &This
->entries
[cursor
];
5857 while (cursor
!= DIRENTRY_NULL
)
5859 /* Make a copy of this entry in the transacted parent. */
5861 (!entry
->dirty
&& !entry
->stream_dirty
&&
5862 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5863 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5864 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5865 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5870 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5872 newData
.size
.QuadPart
= 0;
5873 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5875 if (newData
.leftChild
!= DIRENTRY_NULL
)
5876 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5878 if (newData
.rightChild
!= DIRENTRY_NULL
)
5879 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5881 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5882 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5884 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5885 &entry
->newTransactedParentEntry
);
5888 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5892 if (entry
->stream_dirty
)
5894 hr
= StorageBaseImpl_CopyStream(
5895 This
->transactedParent
, entry
->newTransactedParentEntry
,
5896 This
->scratch
, entry
->stream_entry
);
5898 else if (entry
->data
.size
.QuadPart
)
5900 hr
= StorageBaseImpl_StreamLink(
5901 This
->transactedParent
, entry
->newTransactedParentEntry
,
5902 entry
->transactedParentEntry
);
5907 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5908 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5913 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5914 entry
= &This
->entries
[cursor
];
5920 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5922 DWORD grfCommitFlags
) /* [in] */
5924 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5925 TransactedDirEntry
*root_entry
;
5926 DirRef i
, dir_root_ref
;
5928 ULARGE_INTEGER zero
;
5930 ULONG transactionSig
;
5934 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
5936 /* Cannot commit a read-only transacted storage */
5937 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5938 return STG_E_ACCESSDENIED
;
5940 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5941 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5944 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5947 if (transactionSig
!= This
->lastTransactionSig
)
5949 ERR("file was externally modified\n");
5950 hr
= STG_E_NOTCURRENT
;
5955 This
->lastTransactionSig
= transactionSig
+1;
5956 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5959 else if (hr
== E_NOTIMPL
)
5962 if (FAILED(hr
)) goto end
;
5964 /* To prevent data loss, we create the new structure in the file before we
5965 * delete the old one, so that in case of errors the old data is intact. We
5966 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5967 * needed in the rare situation where we have just enough free disk space to
5968 * overwrite the existing data. */
5970 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5972 if (!root_entry
->read
)
5975 hr
= TransactedSnapshotImpl_CopyTree(This
);
5976 if (FAILED(hr
)) goto end
;
5978 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5979 dir_root_ref
= DIRENTRY_NULL
;
5981 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5983 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5985 /* Update the storage to use the new data in one step. */
5987 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5988 root_entry
->transactedParentEntry
, &data
);
5992 data
.dirRootEntry
= dir_root_ref
;
5993 data
.clsid
= root_entry
->data
.clsid
;
5994 data
.ctime
= root_entry
->data
.ctime
;
5995 data
.mtime
= root_entry
->data
.mtime
;
5997 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5998 root_entry
->transactedParentEntry
, &data
);
6001 /* Try to flush after updating the root storage, but if the flush fails, keep
6002 * going, on the theory that it'll either succeed later or the subsequent
6003 * writes will fail. */
6004 StorageBaseImpl_Flush(This
->transactedParent
);
6008 /* Destroy the old now-orphaned data. */
6009 for (i
=0; i
<This
->entries_size
; i
++)
6011 TransactedDirEntry
*entry
= &This
->entries
[i
];
6016 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
6017 entry
->transactedParentEntry
, zero
);
6018 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6019 entry
->transactedParentEntry
);
6020 memset(entry
, 0, sizeof(TransactedDirEntry
));
6021 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
6023 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
6025 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
6026 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6027 entry
->transactedParentEntry
);
6028 if (entry
->stream_dirty
)
6030 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
6031 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
6032 entry
->stream_dirty
= FALSE
;
6034 entry
->dirty
= FALSE
;
6035 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
6042 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6046 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6048 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6051 TRACE("<-- %#lx\n", hr
);
6055 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6058 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6059 ULARGE_INTEGER zero
;
6062 TRACE("(%p)\n", iface
);
6064 /* Destroy the open objects. */
6065 StorageBaseImpl_DeleteAll(&This
->base
);
6067 /* Clear out the scratch file. */
6069 for (i
=0; i
<This
->entries_size
; i
++)
6071 if (This
->entries
[i
].stream_dirty
)
6073 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6076 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6080 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6082 This
->firstFreeEntry
= 0;
6083 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6088 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6090 if (!This
->reverted
)
6092 TRACE("Storage invalidated (stg=%p)\n", This
);
6094 This
->reverted
= TRUE
;
6096 StorageBaseImpl_DeleteAll(This
);
6100 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6102 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6104 IStorage_Revert(&This
->base
.IStorage_iface
);
6105 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6106 IStorage_Release(&This
->scratch
->IStorage_iface
);
6107 HeapFree(GetProcessHeap(), 0, This
->entries
);
6108 HeapFree(GetProcessHeap(), 0, This
);
6111 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6113 /* We only need to flush when committing. */
6117 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6119 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6121 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6124 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6125 const DirEntry
*newData
, DirRef
*index
)
6127 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6129 TransactedDirEntry
*new_entry
;
6131 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6132 if (new_ref
== DIRENTRY_NULL
)
6133 return E_OUTOFMEMORY
;
6135 new_entry
= &This
->entries
[new_ref
];
6137 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6138 new_entry
->read
= TRUE
;
6139 new_entry
->dirty
= TRUE
;
6140 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6144 TRACE("%s l=%lx r=%lx d=%lx <-- %lx\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6149 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6150 DirRef index
, const DirEntry
*data
)
6152 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6155 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6157 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6160 TRACE("<-- %#lx\n", hr
);
6164 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6166 if (index
!= This
->base
.storageDirEntry
)
6168 This
->entries
[index
].dirty
= TRUE
;
6170 if (data
->size
.QuadPart
== 0 &&
6171 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6173 /* Since this entry is modified, and we aren't using its stream data, we
6174 * no longer care about the original entry. */
6176 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6178 if (delete_ref
!= DIRENTRY_NULL
)
6179 This
->entries
[delete_ref
].deleted
= TRUE
;
6181 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6184 TRACE("<-- S_OK\n");
6188 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6189 DirRef index
, DirEntry
*data
)
6191 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6194 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6197 TRACE("<-- %#lx\n", hr
);
6201 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6203 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6208 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6211 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6213 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6214 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6216 /* If we deleted this entry while it has stream data. We must have left the
6217 * data because some other entry is using it, and we need to leave the
6218 * original entry alone. */
6219 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6220 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6224 This
->entries
[index
].deleted
= TRUE
;
6230 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6231 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6233 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6235 if (This
->entries
[index
].stream_dirty
)
6237 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6238 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6240 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6242 /* This stream doesn't live in the parent, and we haven't allocated storage
6249 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6250 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6254 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6255 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6257 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6260 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6263 TRACE("<-- %#lx\n", hr
);
6267 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6270 TRACE("<-- %#lx\n", hr
);
6274 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6275 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6277 if (SUCCEEDED(hr
) && size
!= 0)
6278 This
->entries
[index
].data
.size
.QuadPart
= max(
6279 This
->entries
[index
].data
.size
.QuadPart
,
6280 offset
.QuadPart
+ size
);
6282 TRACE("<-- %#lx\n", hr
);
6286 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6287 DirRef index
, ULARGE_INTEGER newsize
)
6289 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6292 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6295 TRACE("<-- %#lx\n", hr
);
6299 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6302 if (newsize
.QuadPart
== 0)
6304 /* Destroy any parent references or entries in the scratch file. */
6305 if (This
->entries
[index
].stream_dirty
)
6307 ULARGE_INTEGER zero
;
6309 StorageBaseImpl_StreamSetSize(This
->scratch
,
6310 This
->entries
[index
].stream_entry
, zero
);
6311 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6312 This
->entries
[index
].stream_entry
);
6313 This
->entries
[index
].stream_dirty
= FALSE
;
6315 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6318 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6320 if (delete_ref
!= DIRENTRY_NULL
)
6321 This
->entries
[delete_ref
].deleted
= TRUE
;
6323 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6328 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6329 if (FAILED(hr
)) return hr
;
6331 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6332 This
->entries
[index
].stream_entry
, newsize
);
6336 This
->entries
[index
].data
.size
= newsize
;
6338 TRACE("<-- %#lx\n", hr
);
6342 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6343 DirRef dst
, DirRef src
)
6345 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6347 TransactedDirEntry
*dst_entry
, *src_entry
;
6349 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6352 TRACE("<-- %#lx\n", hr
);
6356 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6359 TRACE("<-- %#lx\n", hr
);
6363 dst_entry
= &This
->entries
[dst
];
6364 src_entry
= &This
->entries
[src
];
6366 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6367 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6368 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6369 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6370 dst_entry
->data
.size
= src_entry
->data
.size
;
6375 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6376 ULONG
* result
, BOOL refresh
)
6381 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6387 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6392 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6397 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6399 StorageBaseImpl_QueryInterface
,
6400 StorageBaseImpl_AddRef
,
6401 StorageBaseImpl_Release
,
6402 StorageBaseImpl_CreateStream
,
6403 StorageBaseImpl_OpenStream
,
6404 StorageBaseImpl_CreateStorage
,
6405 StorageBaseImpl_OpenStorage
,
6406 StorageBaseImpl_CopyTo
,
6407 StorageBaseImpl_MoveElementTo
,
6408 TransactedSnapshotImpl_Commit
,
6409 TransactedSnapshotImpl_Revert
,
6410 StorageBaseImpl_EnumElements
,
6411 StorageBaseImpl_DestroyElement
,
6412 StorageBaseImpl_RenameElement
,
6413 StorageBaseImpl_SetElementTimes
,
6414 StorageBaseImpl_SetClass
,
6415 StorageBaseImpl_SetStateBits
,
6416 StorageBaseImpl_Stat
6419 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6421 TransactedSnapshotImpl_Destroy
,
6422 TransactedSnapshotImpl_Invalidate
,
6423 TransactedSnapshotImpl_Flush
,
6424 TransactedSnapshotImpl_GetFilename
,
6425 TransactedSnapshotImpl_CreateDirEntry
,
6426 TransactedSnapshotImpl_WriteDirEntry
,
6427 TransactedSnapshotImpl_ReadDirEntry
,
6428 TransactedSnapshotImpl_DestroyDirEntry
,
6429 TransactedSnapshotImpl_StreamReadAt
,
6430 TransactedSnapshotImpl_StreamWriteAt
,
6431 TransactedSnapshotImpl_StreamSetSize
,
6432 TransactedSnapshotImpl_StreamLink
,
6433 TransactedSnapshotImpl_GetTransactionSig
,
6434 TransactedSnapshotImpl_SetTransactionSig
,
6435 TransactedSnapshotImpl_LockTransaction
,
6436 TransactedSnapshotImpl_UnlockTransaction
6439 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6440 TransactedSnapshotImpl
** result
)
6444 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6449 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6451 /* This is OK because the property set storage functions use the IStorage functions. */
6452 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6453 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6455 list_init(&(*result
)->base
.strmHead
);
6457 list_init(&(*result
)->base
.storageHead
);
6459 (*result
)->base
.ref
= 1;
6461 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6463 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6464 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6466 /* Create a new temporary storage to act as the scratch file. */
6467 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6469 (*result
)->scratch
= impl_from_IStorage(scratch
);
6473 ULONG num_entries
= 20;
6475 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6476 (*result
)->entries_size
= num_entries
;
6477 (*result
)->firstFreeEntry
= 0;
6479 if ((*result
)->entries
)
6481 /* parentStorage already has 1 reference, which we take over here. */
6482 (*result
)->transactedParent
= parentStorage
;
6484 parentStorage
->transactedChild
= &(*result
)->base
;
6486 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6490 IStorage_Release(scratch
);
6496 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6501 return E_OUTOFMEMORY
;
6505 /************************************************************************
6506 * TransactedSharedImpl implementation
6507 ***********************************************************************/
6509 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6511 if (!This
->reverted
)
6513 TRACE("Storage invalidated (stg=%p)\n", This
);
6515 This
->reverted
= TRUE
;
6517 StorageBaseImpl_DeleteAll(This
);
6521 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6523 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6525 TransactedSharedImpl_Invalidate(&This
->base
);
6526 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6527 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6528 HeapFree(GetProcessHeap(), 0, This
);
6531 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6533 /* We only need to flush when committing. */
6537 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6539 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6541 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6544 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6545 const DirEntry
*newData
, DirRef
*index
)
6547 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6549 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6553 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6554 DirRef index
, const DirEntry
*data
)
6556 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6558 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6562 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6563 DirRef index
, DirEntry
*data
)
6565 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6567 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6571 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6574 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6576 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6580 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6581 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6583 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6585 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6586 index
, offset
, size
, buffer
, bytesRead
);
6589 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6590 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6592 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6594 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6595 index
, offset
, size
, buffer
, bytesWritten
);
6598 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6599 DirRef index
, ULARGE_INTEGER newsize
)
6601 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6603 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6607 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6608 DirRef dst
, DirRef src
)
6610 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6612 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6616 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6617 ULONG
* result
, BOOL refresh
)
6622 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6628 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6633 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6638 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6640 DWORD grfCommitFlags
) /* [in] */
6642 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6643 DirRef new_storage_ref
, prev_storage_ref
;
6644 DirEntry src_data
, dst_data
;
6646 ULONG transactionSig
;
6648 TRACE("%p, %#lx\n", iface
, grfCommitFlags
);
6650 /* Cannot commit a read-only transacted storage */
6651 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6652 return STG_E_ACCESSDENIED
;
6654 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6655 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6658 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6661 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6662 hr
= STG_E_NOTCURRENT
;
6665 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6667 else if (hr
== E_NOTIMPL
)
6671 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6673 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6675 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6678 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6681 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6685 prev_storage_ref
= dst_data
.dirRootEntry
;
6686 dst_data
.dirRootEntry
= new_storage_ref
;
6687 dst_data
.clsid
= src_data
.clsid
;
6688 dst_data
.ctime
= src_data
.ctime
;
6689 dst_data
.mtime
= src_data
.mtime
;
6690 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6695 /* Try to flush after updating the root storage, but if the flush fails, keep
6696 * going, on the theory that it'll either succeed later or the subsequent
6697 * writes will fail. */
6698 StorageBaseImpl_Flush(This
->transactedParent
);
6700 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6704 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6706 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6709 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6713 This
->lastTransactionSig
= transactionSig
+1;
6716 TRACE("<-- %#lx\n", hr
);
6720 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6723 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6725 TRACE("(%p)\n", iface
);
6727 /* Destroy the open objects. */
6728 StorageBaseImpl_DeleteAll(&This
->base
);
6730 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6733 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6735 StorageBaseImpl_QueryInterface
,
6736 StorageBaseImpl_AddRef
,
6737 StorageBaseImpl_Release
,
6738 StorageBaseImpl_CreateStream
,
6739 StorageBaseImpl_OpenStream
,
6740 StorageBaseImpl_CreateStorage
,
6741 StorageBaseImpl_OpenStorage
,
6742 StorageBaseImpl_CopyTo
,
6743 StorageBaseImpl_MoveElementTo
,
6744 TransactedSharedImpl_Commit
,
6745 TransactedSharedImpl_Revert
,
6746 StorageBaseImpl_EnumElements
,
6747 StorageBaseImpl_DestroyElement
,
6748 StorageBaseImpl_RenameElement
,
6749 StorageBaseImpl_SetElementTimes
,
6750 StorageBaseImpl_SetClass
,
6751 StorageBaseImpl_SetStateBits
,
6752 StorageBaseImpl_Stat
6755 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6757 TransactedSharedImpl_Destroy
,
6758 TransactedSharedImpl_Invalidate
,
6759 TransactedSharedImpl_Flush
,
6760 TransactedSharedImpl_GetFilename
,
6761 TransactedSharedImpl_CreateDirEntry
,
6762 TransactedSharedImpl_WriteDirEntry
,
6763 TransactedSharedImpl_ReadDirEntry
,
6764 TransactedSharedImpl_DestroyDirEntry
,
6765 TransactedSharedImpl_StreamReadAt
,
6766 TransactedSharedImpl_StreamWriteAt
,
6767 TransactedSharedImpl_StreamSetSize
,
6768 TransactedSharedImpl_StreamLink
,
6769 TransactedSharedImpl_GetTransactionSig
,
6770 TransactedSharedImpl_SetTransactionSig
,
6771 TransactedSharedImpl_LockTransaction
,
6772 TransactedSharedImpl_UnlockTransaction
6775 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6776 TransactedSharedImpl
** result
)
6780 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6785 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6787 /* This is OK because the property set storage functions use the IStorage functions. */
6788 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6789 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6791 list_init(&(*result
)->base
.strmHead
);
6793 list_init(&(*result
)->base
.storageHead
);
6795 (*result
)->base
.ref
= 1;
6797 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6799 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6805 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6806 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6810 stgo
.ulSectorSize
= 4096;
6811 stgo
.pwcsTemplateFile
= NULL
;
6813 /* Create a new temporary storage to act as the scratch file. */
6814 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6815 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6816 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6820 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6821 parentStorage
, parentStorage
->storageDirEntry
);
6825 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6827 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6828 (*result
)->transactedParent
= parentStorage
;
6832 IStorage_Release(scratch
);
6835 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6838 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6843 return E_OUTOFMEMORY
;
6846 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6847 BOOL toplevel
, StorageBaseImpl
** result
)
6849 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6851 if (parentStorage
->openFlags
& fixme_flags
)
6853 fixme_flags
&= ~parentStorage
->openFlags
;
6854 FIXME("Unimplemented flags %lx\n", parentStorage
->openFlags
);
6857 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6858 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6859 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6861 /* Need to create a temp file for the snapshot */
6862 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6865 return TransactedSnapshotImpl_Construct(parentStorage
,
6866 (TransactedSnapshotImpl
**)result
);
6869 static HRESULT
Storage_Construct(
6877 StorageBaseImpl
** result
)
6879 StorageImpl
*newStorage
;
6880 StorageBaseImpl
*newTransactedStorage
;
6883 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6884 if (FAILED(hr
)) goto end
;
6886 if (openFlags
& STGM_TRANSACTED
)
6888 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6890 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6892 *result
= newTransactedStorage
;
6895 *result
= &newStorage
->base
;
6902 /************************************************************************
6903 * StorageUtl helper functions
6904 ***********************************************************************/
6906 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6910 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6911 *value
= lendian16toh(tmp
);
6914 void StorageUtl_WriteWord(void *buffer
, ULONG offset
, WORD value
)
6916 value
= htole16(value
);
6917 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(WORD
));
6920 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6924 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6925 *value
= lendian32toh(tmp
);
6928 void StorageUtl_WriteDWord(void *buffer
, ULONG offset
, DWORD value
)
6930 value
= htole32(value
);
6931 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(DWORD
));
6934 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6935 ULARGE_INTEGER
* value
)
6937 #ifdef WORDS_BIGENDIAN
6940 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6941 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6942 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6944 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6948 void StorageUtl_WriteULargeInteger(void *buffer
, ULONG offset
, const ULARGE_INTEGER
*value
)
6950 #ifdef WORDS_BIGENDIAN
6953 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6954 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6955 memcpy((BYTE
*)buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6957 memcpy((BYTE
*)buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6961 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6963 StorageUtl_ReadDWord(buffer
, offset
, (DWORD
*)&value
->Data1
);
6964 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6965 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6967 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6970 void StorageUtl_WriteGUID(void *buffer
, ULONG offset
, const GUID
* value
)
6972 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6973 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6974 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6976 memcpy((BYTE
*)buffer
+ offset
+ 8, value
->Data4
, sizeof(value
->Data4
));
6979 void StorageUtl_CopyDirEntryToSTATSTG(
6980 StorageBaseImpl
* storage
,
6981 STATSTG
* destination
,
6982 const DirEntry
* source
,
6986 * The copy of the string occurs only when the flag is not set
6988 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6990 /* Use the filename for the root storage. */
6991 destination
->pwcsName
= 0;
6992 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6994 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6995 (source
->name
[0] == 0) )
6997 destination
->pwcsName
= 0;
7001 destination
->pwcsName
=
7002 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
7004 lstrcpyW(destination
->pwcsName
, source
->name
);
7007 switch (source
->stgType
)
7011 destination
->type
= STGTY_STORAGE
;
7014 destination
->type
= STGTY_STREAM
;
7017 destination
->type
= STGTY_STREAM
;
7021 destination
->cbSize
= source
->size
;
7023 currentReturnStruct->mtime = {0}; TODO
7024 currentReturnStruct->ctime = {0};
7025 currentReturnStruct->atime = {0};
7027 destination
->grfMode
= 0;
7028 destination
->grfLocksSupported
= 0;
7029 destination
->clsid
= source
->clsid
;
7030 destination
->grfStateBits
= 0;
7031 destination
->reserved
= 0;
7035 /************************************************************************
7036 * BlockChainStream implementation
7037 ***********************************************************************/
7039 /******************************************************************************
7040 * BlockChainStream_GetHeadOfChain
7042 * Returns the head of this stream chain.
7043 * Some special chains don't have directory entries, their heads are kept in
7044 * This->headOfStreamPlaceHolder.
7047 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7049 DirEntry chainEntry
;
7052 if (This
->headOfStreamPlaceHolder
!= 0)
7053 return *(This
->headOfStreamPlaceHolder
);
7055 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7057 hr
= StorageImpl_ReadDirEntry(
7058 This
->parentStorage
,
7059 This
->ownerDirEntry
,
7062 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7063 return chainEntry
.startingBlock
;
7066 return BLOCK_END_OF_CHAIN
;
7069 /* Read and save the index of all blocks in this stream. */
7070 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
7072 ULONG next_sector
, next_offset
;
7074 struct BlockChainRun
*last_run
;
7076 if (This
->indexCacheLen
== 0)
7080 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7084 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7085 next_offset
= last_run
->lastOffset
+1;
7086 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7087 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7089 if (FAILED(hr
)) return hr
;
7092 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7094 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7096 /* Add the current block to the cache. */
7097 if (This
->indexCacheSize
== 0)
7099 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7100 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7101 This
->indexCacheSize
= 16;
7103 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7105 struct BlockChainRun
*new_cache
;
7108 new_size
= This
->indexCacheSize
* 2;
7109 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7110 if (!new_cache
) return E_OUTOFMEMORY
;
7111 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7113 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7114 This
->indexCache
= new_cache
;
7115 This
->indexCacheSize
= new_size
;
7118 This
->indexCacheLen
++;
7119 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7120 last_run
->firstSector
= next_sector
;
7121 last_run
->firstOffset
= next_offset
;
7124 last_run
->lastOffset
= next_offset
;
7126 /* Find the next block. */
7128 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7129 if (FAILED(hr
)) return hr
;
7132 if (This
->indexCacheLen
)
7134 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7135 This
->numBlocks
= last_run
->lastOffset
+1;
7139 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7140 This
->numBlocks
= 0;
7146 /* Locate the nth block in this stream. */
7147 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7149 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7150 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7152 if (offset
>= This
->numBlocks
)
7153 return BLOCK_END_OF_CHAIN
;
7155 while (min_run
< max_run
)
7157 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7158 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7160 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7161 max_run
= run_to_check
-1;
7163 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7165 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7166 min_run
= run_to_check
+1;
7169 /* Block is in this run. */
7170 min_run
= max_run
= run_to_check
;
7173 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7176 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7177 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7179 BlockChainBlock
*result
=NULL
;
7183 if (This
->cachedBlocks
[i
].index
== index
)
7185 *sector
= This
->cachedBlocks
[i
].sector
;
7186 *block
= &This
->cachedBlocks
[i
];
7190 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7191 if (*sector
== BLOCK_END_OF_CHAIN
)
7192 return STG_E_DOCFILECORRUPT
;
7196 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7197 result
= &This
->cachedBlocks
[0];
7198 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7199 result
= &This
->cachedBlocks
[1];
7202 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7203 if (This
->blockToEvict
== 2)
7204 This
->blockToEvict
= 0;
7209 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7210 return STG_E_WRITEFAULT
;
7211 result
->dirty
= FALSE
;
7214 result
->read
= FALSE
;
7215 result
->index
= index
;
7216 result
->sector
= *sector
;
7223 BlockChainStream
* BlockChainStream_Construct(
7224 StorageImpl
* parentStorage
,
7225 ULONG
* headOfStreamPlaceHolder
,
7228 BlockChainStream
* newStream
;
7230 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7234 newStream
->parentStorage
= parentStorage
;
7235 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7236 newStream
->ownerDirEntry
= dirEntry
;
7237 newStream
->indexCache
= NULL
;
7238 newStream
->indexCacheLen
= 0;
7239 newStream
->indexCacheSize
= 0;
7240 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7241 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7242 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7243 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7244 newStream
->blockToEvict
= 0;
7246 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7248 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7249 HeapFree(GetProcessHeap(), 0, newStream
);
7256 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7259 if (!This
) return S_OK
;
7262 if (This
->cachedBlocks
[i
].dirty
)
7264 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7265 This
->cachedBlocks
[i
].dirty
= FALSE
;
7267 return STG_E_WRITEFAULT
;
7273 void BlockChainStream_Destroy(BlockChainStream
* This
)
7277 BlockChainStream_Flush(This
);
7278 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7280 HeapFree(GetProcessHeap(), 0, This
);
7283 /******************************************************************************
7284 * BlockChainStream_Shrink
7286 * Shrinks this chain in the big block depot.
7288 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7289 ULARGE_INTEGER newSize
)
7296 * Figure out how many blocks are needed to contain the new size
7298 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7300 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7306 * Go to the new end of chain
7308 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7310 /* Mark the new end of chain */
7311 StorageImpl_SetNextBlockInChain(
7312 This
->parentStorage
,
7314 BLOCK_END_OF_CHAIN
);
7316 This
->tailIndex
= blockIndex
;
7320 if (This
->headOfStreamPlaceHolder
!= 0)
7322 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7326 DirEntry chainEntry
;
7327 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7329 StorageImpl_ReadDirEntry(
7330 This
->parentStorage
,
7331 This
->ownerDirEntry
,
7334 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7336 StorageImpl_WriteDirEntry(
7337 This
->parentStorage
,
7338 This
->ownerDirEntry
,
7342 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7345 This
->numBlocks
= numBlocks
;
7348 * Mark the extra blocks as free
7350 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7352 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7353 StorageImpl_FreeBigBlock(This
->parentStorage
,
7354 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7355 if (last_run
->lastOffset
== last_run
->firstOffset
)
7356 This
->indexCacheLen
--;
7358 last_run
->lastOffset
--;
7362 * Reset the last accessed block cache.
7366 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7368 This
->cachedBlocks
[i
].index
= 0xffffffff;
7369 This
->cachedBlocks
[i
].dirty
= FALSE
;
7376 /******************************************************************************
7377 * BlockChainStream_Enlarge
7379 * Grows this chain in the big block depot.
7381 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7382 ULARGE_INTEGER newSize
)
7384 ULONG blockIndex
, currentBlock
;
7386 ULONG oldNumBlocks
= 0;
7388 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7391 * Empty chain. Create the head.
7393 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7395 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
, 1);
7396 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7398 BLOCK_END_OF_CHAIN
);
7400 if (This
->headOfStreamPlaceHolder
!= 0)
7402 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7406 DirEntry chainEntry
;
7407 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7409 StorageImpl_ReadDirEntry(
7410 This
->parentStorage
,
7411 This
->ownerDirEntry
,
7414 chainEntry
.startingBlock
= blockIndex
;
7416 StorageImpl_WriteDirEntry(
7417 This
->parentStorage
,
7418 This
->ownerDirEntry
,
7422 This
->tailIndex
= blockIndex
;
7423 This
->numBlocks
= 1;
7427 * Figure out how many blocks are needed to contain this stream
7429 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7431 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7435 * Go to the current end of chain
7437 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7439 currentBlock
= blockIndex
;
7441 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7444 currentBlock
= blockIndex
;
7446 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7451 This
->tailIndex
= currentBlock
;
7454 currentBlock
= This
->tailIndex
;
7455 oldNumBlocks
= This
->numBlocks
;
7458 * Add new blocks to the chain
7460 if (oldNumBlocks
< newNumBlocks
)
7462 while (oldNumBlocks
< newNumBlocks
)
7464 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
, newNumBlocks
- oldNumBlocks
);
7466 StorageImpl_SetNextBlockInChain(
7467 This
->parentStorage
,
7471 StorageImpl_SetNextBlockInChain(
7472 This
->parentStorage
,
7474 BLOCK_END_OF_CHAIN
);
7476 currentBlock
= blockIndex
;
7480 This
->tailIndex
= blockIndex
;
7481 This
->numBlocks
= newNumBlocks
;
7484 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7491 /******************************************************************************
7492 * BlockChainStream_GetSize
7494 * Returns the size of this chain.
7495 * Will return the block count if this chain doesn't have a directory entry.
7497 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7499 DirEntry chainEntry
;
7501 if(This
->headOfStreamPlaceHolder
== NULL
)
7504 * This chain has a directory entry so use the size value from there.
7506 StorageImpl_ReadDirEntry(
7507 This
->parentStorage
,
7508 This
->ownerDirEntry
,
7511 return chainEntry
.size
;
7516 * this chain is a chain that does not have a directory entry, figure out the
7517 * size by making the product number of used blocks times the
7520 ULARGE_INTEGER result
;
7522 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7523 This
->parentStorage
->bigBlockSize
;
7529 /******************************************************************************
7530 * BlockChainStream_SetSize
7532 * Sets the size of this stream. The big block depot will be updated.
7533 * The file will grow if we grow the chain.
7535 * TODO: Free the actual blocks in the file when we shrink the chain.
7536 * Currently, the blocks are still in the file. So the file size
7537 * doesn't shrink even if we shrink streams.
7539 BOOL
BlockChainStream_SetSize(
7540 BlockChainStream
* This
,
7541 ULARGE_INTEGER newSize
)
7543 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7545 if (newSize
.QuadPart
== size
.QuadPart
)
7548 if (newSize
.QuadPart
< size
.QuadPart
)
7550 BlockChainStream_Shrink(This
, newSize
);
7554 BlockChainStream_Enlarge(This
, newSize
);
7560 /******************************************************************************
7561 * BlockChainStream_ReadAt
7563 * Reads a specified number of bytes from this chain at the specified offset.
7564 * bytesRead may be NULL.
7565 * Failure will be returned if the specified number of bytes has not been read.
7567 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7568 ULARGE_INTEGER offset
,
7573 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7574 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7575 ULONG bytesToReadInBuffer
;
7578 ULARGE_INTEGER stream_size
;
7580 BlockChainBlock
*cachedBlock
;
7582 TRACE("%p, %li, %p, %lu, %p.\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7585 * Find the first block in the stream that contains part of the buffer.
7587 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7591 stream_size
= BlockChainStream_GetSize(This
);
7592 if (stream_size
.QuadPart
> offset
.QuadPart
)
7593 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7598 * Start reading the buffer.
7600 bufferWalker
= buffer
;
7604 ULARGE_INTEGER ulOffset
;
7608 * Calculate how many bytes we can copy from this big block.
7610 bytesToReadInBuffer
=
7611 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7613 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7620 /* Not in cache, and we're going to read past the end of the block. */
7621 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7624 StorageImpl_ReadAt(This
->parentStorage
,
7627 bytesToReadInBuffer
,
7632 if (!cachedBlock
->read
)
7635 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7636 return STG_E_READFAULT
;
7638 cachedBlock
->read
= TRUE
;
7641 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7642 bytesReadAt
= bytesToReadInBuffer
;
7645 blockNoInSequence
++;
7646 bufferWalker
+= bytesReadAt
;
7647 size
-= bytesReadAt
;
7648 *bytesRead
+= bytesReadAt
;
7649 offsetInBlock
= 0; /* There is no offset on the next block */
7651 if (bytesToReadInBuffer
!= bytesReadAt
)
7658 /******************************************************************************
7659 * BlockChainStream_WriteAt
7661 * Writes the specified number of bytes to this chain at the specified offset.
7662 * Will fail if not all specified number of bytes have been written.
7664 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7665 ULARGE_INTEGER offset
,
7668 ULONG
* bytesWritten
)
7670 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7671 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7674 const BYTE
* bufferWalker
;
7676 BlockChainBlock
*cachedBlock
;
7679 bufferWalker
= buffer
;
7683 ULARGE_INTEGER ulOffset
;
7684 DWORD bytesWrittenAt
;
7687 * Calculate how many bytes we can copy to this big block.
7690 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7692 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7694 /* BlockChainStream_SetSize should have already been called to ensure we have
7695 * enough blocks in the chain to write into */
7698 ERR("not enough blocks in chain to write data\n");
7704 /* Not in cache, and we're going to write past the end of the block. */
7705 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7708 StorageImpl_WriteAt(This
->parentStorage
,
7716 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7719 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7720 return STG_E_READFAULT
;
7723 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7724 bytesWrittenAt
= bytesToWrite
;
7725 cachedBlock
->read
= TRUE
;
7726 cachedBlock
->dirty
= TRUE
;
7729 blockNoInSequence
++;
7730 bufferWalker
+= bytesWrittenAt
;
7731 size
-= bytesWrittenAt
;
7732 *bytesWritten
+= bytesWrittenAt
;
7733 offsetInBlock
= 0; /* There is no offset on the next block */
7735 if (bytesWrittenAt
!= bytesToWrite
)
7739 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7743 /************************************************************************
7744 * SmallBlockChainStream implementation
7745 ***********************************************************************/
7747 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7748 StorageImpl
* parentStorage
,
7749 ULONG
* headOfStreamPlaceHolder
,
7752 SmallBlockChainStream
* newStream
;
7754 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7756 newStream
->parentStorage
= parentStorage
;
7757 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7758 newStream
->ownerDirEntry
= dirEntry
;
7763 void SmallBlockChainStream_Destroy(
7764 SmallBlockChainStream
* This
)
7766 HeapFree(GetProcessHeap(), 0, This
);
7769 /******************************************************************************
7770 * SmallBlockChainStream_GetHeadOfChain
7772 * Returns the head of this chain of small blocks.
7774 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7775 SmallBlockChainStream
* This
)
7777 DirEntry chainEntry
;
7780 if (This
->headOfStreamPlaceHolder
!= NULL
)
7781 return *(This
->headOfStreamPlaceHolder
);
7783 if (This
->ownerDirEntry
)
7785 hr
= StorageImpl_ReadDirEntry(
7786 This
->parentStorage
,
7787 This
->ownerDirEntry
,
7790 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7791 return chainEntry
.startingBlock
;
7794 return BLOCK_END_OF_CHAIN
;
7797 /******************************************************************************
7798 * SmallBlockChainStream_GetNextBlockInChain
7800 * Returns the index of the next small block in this chain.
7803 * - BLOCK_END_OF_CHAIN: end of this chain
7804 * - BLOCK_UNUSED: small block 'blockIndex' is free
7806 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7807 SmallBlockChainStream
* This
,
7809 ULONG
* nextBlockInChain
)
7811 ULARGE_INTEGER offsetOfBlockInDepot
;
7816 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7818 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7821 * Read those bytes in the buffer from the small block file.
7823 res
= BlockChainStream_ReadAt(
7824 This
->parentStorage
->smallBlockDepotChain
,
7825 offsetOfBlockInDepot
,
7830 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7831 res
= STG_E_READFAULT
;
7835 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7842 /******************************************************************************
7843 * SmallBlockChainStream_SetNextBlockInChain
7845 * Writes the index of the next block of the specified block in the small
7847 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7848 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7850 static void SmallBlockChainStream_SetNextBlockInChain(
7851 SmallBlockChainStream
* This
,
7855 ULARGE_INTEGER offsetOfBlockInDepot
;
7859 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7861 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
7864 * Read those bytes in the buffer from the small block file.
7866 BlockChainStream_WriteAt(
7867 This
->parentStorage
->smallBlockDepotChain
,
7868 offsetOfBlockInDepot
,
7874 /******************************************************************************
7875 * SmallBlockChainStream_FreeBlock
7877 * Flag small block 'blockIndex' as free in the small block depot.
7879 static void SmallBlockChainStream_FreeBlock(
7880 SmallBlockChainStream
* This
,
7883 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7886 /******************************************************************************
7887 * SmallBlockChainStream_GetNextFreeBlock
7889 * Returns the index of a free small block. The small block depot will be
7890 * enlarged if necessary. The small block chain will also be enlarged if
7893 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7894 SmallBlockChainStream
* This
)
7896 ULARGE_INTEGER offsetOfBlockInDepot
;
7899 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7900 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7902 ULONG smallBlocksPerBigBlock
;
7904 ULONG blocksRequired
;
7905 ULARGE_INTEGER old_size
, size_required
;
7907 offsetOfBlockInDepot
.u
.HighPart
= 0;
7910 * Scan the small block depot for a free block
7912 while (nextBlockIndex
!= BLOCK_UNUSED
)
7914 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7916 res
= BlockChainStream_ReadAt(
7917 This
->parentStorage
->smallBlockDepotChain
,
7918 offsetOfBlockInDepot
,
7924 * If we run out of space for the small block depot, enlarge it
7926 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7928 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7930 if (nextBlockIndex
!= BLOCK_UNUSED
)
7936 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7938 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7939 ULARGE_INTEGER newSize
, offset
;
7942 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7943 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7946 * Initialize all the small blocks to free
7948 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7949 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7950 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7951 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7953 StorageImpl_SaveFileHeader(This
->parentStorage
);
7957 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7959 smallBlocksPerBigBlock
=
7960 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7963 * Verify if we have to allocate big blocks to contain small blocks
7965 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7967 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7969 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7971 if (size_required
.QuadPart
> old_size
.QuadPart
)
7973 BlockChainStream_SetSize(
7974 This
->parentStorage
->smallBlockRootChain
,
7977 StorageImpl_ReadDirEntry(
7978 This
->parentStorage
,
7979 This
->parentStorage
->base
.storageDirEntry
,
7982 rootEntry
.size
= size_required
;
7984 StorageImpl_WriteDirEntry(
7985 This
->parentStorage
,
7986 This
->parentStorage
->base
.storageDirEntry
,
7993 /******************************************************************************
7994 * SmallBlockChainStream_ReadAt
7996 * Reads a specified number of bytes from this chain at the specified offset.
7997 * bytesRead may be NULL.
7998 * Failure will be returned if the specified number of bytes has not been read.
8000 HRESULT
SmallBlockChainStream_ReadAt(
8001 SmallBlockChainStream
* This
,
8002 ULARGE_INTEGER offset
,
8008 ULARGE_INTEGER offsetInBigBlockFile
;
8009 ULONG blockNoInSequence
=
8010 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8012 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8013 ULONG bytesToReadInBuffer
;
8015 ULONG bytesReadFromBigBlockFile
;
8017 ULARGE_INTEGER stream_size
;
8020 * This should never happen on a small block file.
8022 assert(offset
.u
.HighPart
==0);
8026 stream_size
= SmallBlockChainStream_GetSize(This
);
8027 if (stream_size
.QuadPart
> offset
.QuadPart
)
8028 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
8033 * Find the first block in the stream that contains part of the buffer.
8035 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8037 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8039 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8042 blockNoInSequence
--;
8046 * Start reading the buffer.
8048 bufferWalker
= buffer
;
8050 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8053 * Calculate how many bytes we can copy from this small block.
8055 bytesToReadInBuffer
=
8056 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8059 * Calculate the offset of the small block in the small block file.
8061 offsetInBigBlockFile
.QuadPart
=
8062 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8064 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8067 * Read those bytes in the buffer from the small block file.
8068 * The small block has already been identified so it shouldn't fail
8069 * unless the file is corrupt.
8071 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
8072 offsetInBigBlockFile
,
8073 bytesToReadInBuffer
,
8075 &bytesReadFromBigBlockFile
);
8080 if (!bytesReadFromBigBlockFile
)
8081 return STG_E_DOCFILECORRUPT
;
8084 * Step to the next big block.
8086 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8088 return STG_E_DOCFILECORRUPT
;
8090 bufferWalker
+= bytesReadFromBigBlockFile
;
8091 size
-= bytesReadFromBigBlockFile
;
8092 *bytesRead
+= bytesReadFromBigBlockFile
;
8093 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8099 /******************************************************************************
8100 * SmallBlockChainStream_WriteAt
8102 * Writes the specified number of bytes to this chain at the specified offset.
8103 * Will fail if not all specified number of bytes have been written.
8105 HRESULT
SmallBlockChainStream_WriteAt(
8106 SmallBlockChainStream
* This
,
8107 ULARGE_INTEGER offset
,
8110 ULONG
* bytesWritten
)
8112 ULARGE_INTEGER offsetInBigBlockFile
;
8113 ULONG blockNoInSequence
=
8114 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8116 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8117 ULONG bytesToWriteInBuffer
;
8119 ULONG bytesWrittenToBigBlockFile
;
8120 const BYTE
* bufferWalker
;
8124 * This should never happen on a small block file.
8126 assert(offset
.u
.HighPart
==0);
8129 * Find the first block in the stream that contains part of the buffer.
8131 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8133 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8135 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8136 return STG_E_DOCFILECORRUPT
;
8137 blockNoInSequence
--;
8141 * Start writing the buffer.
8144 bufferWalker
= buffer
;
8145 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8148 * Calculate how many bytes we can copy to this small block.
8150 bytesToWriteInBuffer
=
8151 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8154 * Calculate the offset of the small block in the small block file.
8156 offsetInBigBlockFile
.QuadPart
=
8157 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8159 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8162 * Write those bytes in the buffer to the small block file.
8164 res
= BlockChainStream_WriteAt(
8165 This
->parentStorage
->smallBlockRootChain
,
8166 offsetInBigBlockFile
,
8167 bytesToWriteInBuffer
,
8169 &bytesWrittenToBigBlockFile
);
8174 * Step to the next big block.
8176 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8179 bufferWalker
+= bytesWrittenToBigBlockFile
;
8180 size
-= bytesWrittenToBigBlockFile
;
8181 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8182 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8185 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8188 /******************************************************************************
8189 * SmallBlockChainStream_Shrink
8191 * Shrinks this chain in the small block depot.
8193 static BOOL
SmallBlockChainStream_Shrink(
8194 SmallBlockChainStream
* This
,
8195 ULARGE_INTEGER newSize
)
8197 ULONG blockIndex
, extraBlock
;
8201 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8203 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8206 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8209 * Go to the new end of chain
8211 while (count
< numBlocks
)
8213 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8220 * If the count is 0, we have a special case, the head of the chain was
8225 DirEntry chainEntry
;
8227 StorageImpl_ReadDirEntry(This
->parentStorage
,
8228 This
->ownerDirEntry
,
8231 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8233 StorageImpl_WriteDirEntry(This
->parentStorage
,
8234 This
->ownerDirEntry
,
8238 * We start freeing the chain at the head block.
8240 extraBlock
= blockIndex
;
8244 /* Get the next block before marking the new end */
8245 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8249 /* Mark the new end of chain */
8250 SmallBlockChainStream_SetNextBlockInChain(
8253 BLOCK_END_OF_CHAIN
);
8257 * Mark the extra blocks as free
8259 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8261 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8264 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8265 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8266 extraBlock
= blockIndex
;
8272 /******************************************************************************
8273 * SmallBlockChainStream_Enlarge
8275 * Grows this chain in the small block depot.
8277 static BOOL
SmallBlockChainStream_Enlarge(
8278 SmallBlockChainStream
* This
,
8279 ULARGE_INTEGER newSize
)
8281 ULONG blockIndex
, currentBlock
;
8283 ULONG oldNumBlocks
= 0;
8285 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8288 * Empty chain. Create the head.
8290 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8292 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8293 SmallBlockChainStream_SetNextBlockInChain(
8296 BLOCK_END_OF_CHAIN
);
8298 if (This
->headOfStreamPlaceHolder
!= NULL
)
8300 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8304 DirEntry chainEntry
;
8306 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8309 chainEntry
.startingBlock
= blockIndex
;
8311 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8316 currentBlock
= blockIndex
;
8319 * Figure out how many blocks are needed to contain this stream
8321 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8323 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8327 * Go to the current end of chain
8329 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8332 currentBlock
= blockIndex
;
8333 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8338 * Add new blocks to the chain
8340 while (oldNumBlocks
< newNumBlocks
)
8342 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8343 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8345 SmallBlockChainStream_SetNextBlockInChain(
8348 BLOCK_END_OF_CHAIN
);
8350 currentBlock
= blockIndex
;
8357 /******************************************************************************
8358 * SmallBlockChainStream_SetSize
8360 * Sets the size of this stream.
8361 * The file will grow if we grow the chain.
8363 * TODO: Free the actual blocks in the file when we shrink the chain.
8364 * Currently, the blocks are still in the file. So the file size
8365 * doesn't shrink even if we shrink streams.
8367 BOOL
SmallBlockChainStream_SetSize(
8368 SmallBlockChainStream
* This
,
8369 ULARGE_INTEGER newSize
)
8371 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8373 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8376 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8378 SmallBlockChainStream_Shrink(This
, newSize
);
8382 SmallBlockChainStream_Enlarge(This
, newSize
);
8388 /******************************************************************************
8389 * SmallBlockChainStream_GetCount
8391 * Returns the number of small blocks that comprises this chain.
8392 * This is not the size of the stream as the last block may not be full!
8395 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8400 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8402 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8406 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8407 blockIndex
, &blockIndex
)))
8414 /******************************************************************************
8415 * SmallBlockChainStream_GetSize
8417 * Returns the size of this chain.
8419 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8421 DirEntry chainEntry
;
8423 if(This
->headOfStreamPlaceHolder
!= NULL
)
8425 ULARGE_INTEGER result
;
8426 result
.u
.HighPart
= 0;
8428 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8429 This
->parentStorage
->smallBlockSize
;
8434 StorageImpl_ReadDirEntry(
8435 This
->parentStorage
,
8436 This
->ownerDirEntry
,
8439 return chainEntry
.size
;
8443 /************************************************************************
8444 * Miscellaneous storage functions
8445 ***********************************************************************/
8447 static HRESULT
create_storagefile(
8451 STGOPTIONS
* pStgOptions
,
8455 StorageBaseImpl
* newStorage
= 0;
8456 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8457 HRESULT hr
= STG_E_INVALIDFLAG
;
8461 DWORD fileAttributes
;
8462 WCHAR tempFileName
[MAX_PATH
];
8465 return STG_E_INVALIDPOINTER
;
8467 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8468 return STG_E_INVALIDPARAMETER
;
8470 /* if no share mode given then DENY_NONE is the default */
8471 if (STGM_SHARE_MODE(grfMode
) == 0)
8472 grfMode
|= STGM_SHARE_DENY_NONE
;
8474 if ( FAILED( validateSTGM(grfMode
) ))
8477 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8478 switch(STGM_ACCESS_MODE(grfMode
))
8481 case STGM_READWRITE
:
8487 /* in direct mode, can only use SHARE_EXCLUSIVE */
8488 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8491 /* but in transacted mode, any share mode is valid */
8494 * Generate a unique name.
8498 WCHAR tempPath
[MAX_PATH
];
8500 memset(tempPath
, 0, sizeof(tempPath
));
8501 memset(tempFileName
, 0, sizeof(tempFileName
));
8503 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8506 if (GetTempFileNameW(tempPath
, L
"STO", 0, tempFileName
) != 0)
8507 pwcsName
= tempFileName
;
8510 hr
= STG_E_INSUFFICIENTMEMORY
;
8514 creationMode
= TRUNCATE_EXISTING
;
8518 creationMode
= GetCreationModeFromSTGM(grfMode
);
8522 * Interpret the STGM value grfMode
8524 shareMode
= GetShareModeFromSTGM(grfMode
);
8525 accessMode
= GetAccessModeFromSTGM(grfMode
);
8527 if (grfMode
& STGM_DELETEONRELEASE
)
8528 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8530 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8534 hFile
= CreateFileW(pwcsName
,
8542 if (hFile
== INVALID_HANDLE_VALUE
)
8544 if(GetLastError() == ERROR_FILE_EXISTS
)
8545 hr
= STG_E_FILEALREADYEXISTS
;
8552 * Allocate and initialize the new IStorage object.
8554 hr
= Storage_Construct(
8561 pStgOptions
->ulSectorSize
,
8569 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8570 IStorage_Release(&newStorage
->IStorage_iface
);
8573 TRACE("<-- %p r = %#lx\n", *ppstgOpen
, hr
);
8578 /******************************************************************************
8579 * StgCreateDocfile [OLE32.@]
8580 * Creates a new compound file storage object
8583 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8584 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8585 * reserved [ ?] unused?, usually 0
8586 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8589 * S_OK if the file was successfully created
8590 * some STG_E_ value if error
8592 * if pwcsName is NULL, create file with new unique name
8593 * the function can returns
8594 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8597 HRESULT WINAPI
StgCreateDocfile(
8601 IStorage
**ppstgOpen
)
8603 STGOPTIONS stgoptions
= {1, 0, 512};
8605 TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName
), grfMode
, reserved
, ppstgOpen
);
8608 return STG_E_INVALIDPOINTER
;
8610 return STG_E_INVALIDPARAMETER
;
8612 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8615 /******************************************************************************
8616 * StgCreateStorageEx [OLE32.@]
8618 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8620 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName
),
8621 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8623 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8625 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8626 return STG_E_INVALIDPARAMETER
;
8629 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8631 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8632 return STG_E_INVALIDPARAMETER
;
8635 if (stgfmt
== STGFMT_FILE
)
8637 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8638 return STG_E_INVALIDPARAMETER
;
8641 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8643 STGOPTIONS defaultOptions
= {1, 0, 512};
8645 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8646 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8650 ERR("Invalid stgfmt argument\n");
8651 return STG_E_INVALIDPARAMETER
;
8654 /******************************************************************************
8655 * StgCreatePropSetStg [OLE32.@]
8657 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8658 IPropertySetStorage
**propset
)
8660 TRACE("%p, %#lx, %p.\n", pstg
, reserved
, propset
);
8662 return STG_E_INVALIDPARAMETER
;
8664 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8667 /******************************************************************************
8668 * StgOpenStorageEx [OLE32.@]
8670 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8672 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName
),
8673 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8675 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8677 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8678 return STG_E_INVALIDPARAMETER
;
8684 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8685 return STG_E_INVALIDPARAMETER
;
8687 case STGFMT_STORAGE
:
8690 case STGFMT_DOCFILE
:
8691 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8693 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8694 return STG_E_INVALIDPARAMETER
;
8696 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8700 WARN("STGFMT_ANY assuming storage\n");
8704 return STG_E_INVALIDPARAMETER
;
8707 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8711 /******************************************************************************
8712 * StgOpenStorage [OLE32.@]
8714 HRESULT WINAPI
StgOpenStorage(
8715 const OLECHAR
*pwcsName
,
8716 IStorage
*pstgPriority
,
8720 IStorage
**ppstgOpen
)
8722 StorageBaseImpl
* newStorage
= 0;
8727 LPWSTR temp_name
= NULL
;
8729 TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8730 snbExclude
, reserved
, ppstgOpen
);
8734 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8735 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8736 if (FAILED(hr
)) goto end
;
8737 pwcsName
= temp_name
;
8738 TRACE("using filename %s\n", debugstr_w(temp_name
));
8743 hr
= STG_E_INVALIDNAME
;
8749 hr
= STG_E_INVALIDPOINTER
;
8755 hr
= STG_E_INVALIDPARAMETER
;
8759 if (grfMode
& STGM_PRIORITY
)
8761 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8762 return STG_E_INVALIDFLAG
;
8763 if (grfMode
& STGM_DELETEONRELEASE
)
8764 return STG_E_INVALIDFUNCTION
;
8765 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8766 return STG_E_INVALIDFLAG
;
8767 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8768 grfMode
|= STGM_SHARE_DENY_NONE
;
8772 * Validate the sharing mode
8774 if (grfMode
& STGM_DIRECT_SWMR
)
8776 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8777 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8779 hr
= STG_E_INVALIDFLAG
;
8783 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8784 switch(STGM_SHARE_MODE(grfMode
))
8786 case STGM_SHARE_EXCLUSIVE
:
8787 case STGM_SHARE_DENY_WRITE
:
8790 hr
= STG_E_INVALIDFLAG
;
8794 if ( FAILED( validateSTGM(grfMode
) ) ||
8795 (grfMode
&STGM_CREATE
))
8797 hr
= STG_E_INVALIDFLAG
;
8801 /* shared reading requires transacted or single writer mode */
8802 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8803 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8804 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8806 hr
= STG_E_INVALIDFLAG
;
8811 * Interpret the STGM value grfMode
8813 shareMode
= GetShareModeFromSTGM(grfMode
);
8814 accessMode
= GetAccessModeFromSTGM(grfMode
);
8818 hFile
= CreateFileW( pwcsName
,
8823 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8826 if (hFile
==INVALID_HANDLE_VALUE
)
8828 DWORD last_error
= GetLastError();
8834 case ERROR_FILE_NOT_FOUND
:
8835 hr
= STG_E_FILENOTFOUND
;
8838 case ERROR_PATH_NOT_FOUND
:
8839 hr
= STG_E_PATHNOTFOUND
;
8842 case ERROR_ACCESS_DENIED
:
8843 case ERROR_WRITE_PROTECT
:
8844 hr
= STG_E_ACCESSDENIED
;
8847 case ERROR_SHARING_VIOLATION
:
8848 hr
= STG_E_SHAREVIOLATION
;
8859 * Refuse to open the file if it's too small to be a structured storage file
8860 * FIXME: verify the file when reading instead of here
8862 if (GetFileSize(hFile
, NULL
) < HEADER_SIZE
)
8865 hr
= STG_E_FILEALREADYEXISTS
;
8870 * Allocate and initialize the new IStorage object.
8872 hr
= Storage_Construct(
8885 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8887 if(hr
== STG_E_INVALIDHEADER
)
8888 hr
= STG_E_FILEALREADYEXISTS
;
8892 *ppstgOpen
= &newStorage
->IStorage_iface
;
8895 CoTaskMemFree(temp_name
);
8896 if (pstgPriority
) IStorage_Release(pstgPriority
);
8897 TRACE("<-- %#lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8901 /******************************************************************************
8902 * StgCreateDocfileOnILockBytes [OLE32.@]
8904 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8908 IStorage
** ppstgOpen
)
8910 StorageBaseImpl
* newStorage
= 0;
8913 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8914 return STG_E_INVALIDPOINTER
;
8917 * Allocate and initialize the new IStorage object.
8919 hr
= Storage_Construct(
8934 *ppstgOpen
= &newStorage
->IStorage_iface
;
8939 /******************************************************************************
8940 * StgOpenStorageOnILockBytes [OLE32.@]
8942 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8944 IStorage
*pstgPriority
,
8948 IStorage
**ppstgOpen
)
8950 StorageBaseImpl
* newStorage
= 0;
8953 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8954 return STG_E_INVALIDPOINTER
;
8956 if ( FAILED( validateSTGM(grfMode
) ))
8957 return STG_E_INVALIDFLAG
;
8962 * Allocate and initialize the new IStorage object.
8964 hr
= Storage_Construct(
8979 *ppstgOpen
= &newStorage
->IStorage_iface
;
8984 /******************************************************************************
8985 * StgSetTimes [ole32.@]
8986 * StgSetTimes [OLE32.@]
8990 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8991 FILETIME
const *patime
, FILETIME
const *pmtime
)
8993 IStorage
*stg
= NULL
;
8996 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8998 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
9002 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
9003 IStorage_Release(stg
);
9009 /******************************************************************************
9010 * StgIsStorageILockBytes [OLE32.@]
9012 * Determines if the ILockBytes contains a storage object.
9014 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
9016 BYTE sig
[sizeof(STORAGE_magic
)];
9017 ULARGE_INTEGER offset
;
9020 offset
.u
.HighPart
= 0;
9021 offset
.u
.LowPart
= 0;
9023 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
9025 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
9031 /******************************************************************************
9032 * WriteClassStg [OLE32.@]
9034 * This method will store the specified CLSID in the specified storage object
9036 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
9039 return E_INVALIDARG
;
9042 return STG_E_INVALIDPOINTER
;
9044 return IStorage_SetClass(pStg
, rclsid
);
9047 /***********************************************************************
9048 * ReadClassStg (OLE32.@)
9050 * This method reads the CLSID previously written to a storage object with
9051 * the WriteClassStg.
9054 * pstg [I] IStorage pointer
9055 * pclsid [O] Pointer to where the CLSID is written
9059 * Failure: HRESULT code.
9061 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
9066 TRACE("(%p, %p)\n", pstg
, pclsid
);
9068 if(!pstg
|| !pclsid
)
9069 return E_INVALIDARG
;
9072 * read a STATSTG structure (contains the clsid) from the storage
9074 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9077 *pclsid
=pstatstg
.clsid
;
9082 /***********************************************************************
9083 * OleLoadFromStream (OLE32.@)
9085 * This function loads an object from stream
9087 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9091 LPPERSISTSTREAM xstm
;
9093 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9095 res
=ReadClassStm(pStm
,&clsid
);
9098 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9101 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9103 IUnknown_Release((IUnknown
*)*ppvObj
);
9106 res
=IPersistStream_Load(xstm
,pStm
);
9107 IPersistStream_Release(xstm
);
9108 /* FIXME: all refcounts ok at this point? I think they should be:
9111 * xstm : 0 (released)
9116 /***********************************************************************
9117 * OleSaveToStream (OLE32.@)
9119 * This function saves an object with the IPersistStream interface on it
9120 * to the specified stream.
9122 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9128 TRACE("(%p,%p)\n",pPStm
,pStm
);
9130 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9132 if (SUCCEEDED(res
)){
9134 res
=WriteClassStm(pStm
,&clsid
);
9138 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9141 TRACE("Finished Save\n");
9145 /*************************************************************************
9146 * STORAGE_CreateOleStream [Internal]
9148 * Creates the "\001OLE" stream in the IStorage if necessary.
9151 * storage [I] Dest storage to create the stream in
9152 * flags [I] flags to be set for newly created stream
9155 * HRESULT return value
9159 * This stream is still unknown, MS Word seems to have extra data
9160 * but since the data is stored in the OLESTREAM there should be
9161 * no need to recreate the stream. If the stream is manually
9162 * deleted it will create it with this default data.
9165 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9167 static const DWORD version_magic
= 0x02000001;
9171 hr
= IStorage_CreateStream(storage
, L
"\1Ole", STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9174 struct empty_1ole_stream
{
9175 DWORD version_magic
;
9177 DWORD update_options
;
9179 DWORD mon_stream_size
;
9181 struct empty_1ole_stream stream_data
;
9183 stream_data
.version_magic
= version_magic
;
9184 stream_data
.flags
= flags
;
9185 stream_data
.update_options
= 0;
9186 stream_data
.reserved
= 0;
9187 stream_data
.mon_stream_size
= 0;
9189 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9190 IStream_Release(stream
);
9196 /* write a string to a stream, preceded by its length */
9197 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9204 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9205 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9210 str
= CoTaskMemAlloc( len
);
9211 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9212 r
= IStream_Write( stm
, str
, len
, NULL
);
9213 CoTaskMemFree( str
);
9217 /* read a string preceded by its length from a stream */
9218 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9221 DWORD len
, count
= 0;
9225 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9228 if( count
!= sizeof(len
) )
9229 return E_OUTOFMEMORY
;
9231 TRACE("%ld bytes\n",len
);
9233 str
= CoTaskMemAlloc( len
);
9235 return E_OUTOFMEMORY
;
9237 r
= IStream_Read( stm
, str
, len
, &count
);
9240 CoTaskMemFree( str
);
9245 CoTaskMemFree( str
);
9246 return E_OUTOFMEMORY
;
9249 TRACE("Read string %s\n",debugstr_an(str
,len
));
9251 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9252 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9255 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9258 CoTaskMemFree( str
);
9266 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9267 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9272 static const BYTE unknown1
[12] =
9273 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9274 0xFF, 0xFF, 0xFF, 0xFF};
9275 static const BYTE unknown2
[16] =
9276 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9279 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9280 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9281 debugstr_w(szProgIDName
));
9283 /* Create a CompObj stream */
9284 r
= IStorage_CreateStream(pstg
, L
"\1CompObj",
9285 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9289 /* Write CompObj Structure to stream */
9290 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9292 if( SUCCEEDED( r
) )
9293 r
= WriteClassStm( pstm
, clsid
);
9295 if( SUCCEEDED( r
) )
9296 r
= STREAM_WriteString( pstm
, lpszUserType
);
9297 if( SUCCEEDED( r
) )
9298 r
= STREAM_WriteString( pstm
, szClipName
);
9299 if( SUCCEEDED( r
) )
9300 r
= STREAM_WriteString( pstm
, szProgIDName
);
9301 if( SUCCEEDED( r
) )
9302 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9304 IStream_Release( pstm
);
9309 /***********************************************************************
9310 * WriteFmtUserTypeStg (OLE32.@)
9312 HRESULT WINAPI
WriteFmtUserTypeStg(
9313 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9317 WCHAR szwClipName
[0x40];
9319 LPWSTR wstrProgID
= NULL
;
9322 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9324 /* get the clipboard format name */
9327 n
= GetClipboardFormatNameW(cf
, szwClipName
, ARRAY_SIZE(szwClipName
));
9331 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9333 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9339 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9341 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9343 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9344 cf
? szwClipName
: NULL
, wstrProgID
);
9346 CoTaskMemFree(wstrProgID
);
9352 /******************************************************************************
9353 * ReadFmtUserTypeStg [OLE32.@]
9355 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9359 unsigned char unknown1
[12];
9360 unsigned char unknown2
[16];
9362 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9365 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9367 r
= IStorage_OpenStream( pstg
, L
"\1CompObj", NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9370 WARN("Failed to open stream r = %#lx\n", r
);
9374 /* read the various parts of the structure */
9375 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9376 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9378 r
= ReadClassStm( stm
, &clsid
);
9382 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9386 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9390 r
= STREAM_ReadString( stm
, &szProgIDName
);
9394 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9395 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9398 /* ok, success... now we just need to store what we found */
9400 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9402 if( lplpszUserType
)
9404 *lplpszUserType
= szCLSIDName
;
9409 CoTaskMemFree( szCLSIDName
);
9410 CoTaskMemFree( szOleTypeName
);
9411 CoTaskMemFree( szProgIDName
);
9412 IStream_Release( stm
);
9417 /******************************************************************************
9418 * StgIsStorageFile [OLE32.@]
9419 * Verify if the file contains a storage object
9425 * S_OK if file has magic bytes as a storage object
9426 * S_FALSE if file is not storage
9429 StgIsStorageFile(LPCOLESTR fn
)
9435 TRACE("%s\n", debugstr_w(fn
));
9436 hf
= CreateFileW(fn
, GENERIC_READ
,
9437 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9438 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9440 if (hf
== INVALID_HANDLE_VALUE
)
9441 return STG_E_FILENOTFOUND
;
9443 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9445 WARN(" unable to read file\n");
9452 if (bytes_read
!= 8) {
9453 TRACE(" too short\n");
9457 if (!memcmp(magic
,STORAGE_magic
,8)) {
9462 TRACE(" -> Invalid header.\n");
9466 /***********************************************************************
9467 * WriteClassStm (OLE32.@)
9469 * Writes a CLSID to a stream.
9472 * pStm [I] Stream to write to.
9473 * rclsid [I] CLSID to write.
9477 * Failure: HRESULT code.
9479 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9481 TRACE("(%p,%p)\n",pStm
,rclsid
);
9483 if (!pStm
|| !rclsid
)
9484 return E_INVALIDARG
;
9486 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9489 /***********************************************************************
9490 * ReadClassStm (OLE32.@)
9492 * Reads a CLSID from a stream.
9495 * pStm [I] Stream to read from.
9496 * rclsid [O] CLSID to read.
9500 * Failure: HRESULT code.
9502 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9507 TRACE("(%p,%p)\n",pStm
,pclsid
);
9509 if (!pStm
|| !pclsid
)
9510 return E_INVALIDARG
;
9512 /* clear the output args */
9513 *pclsid
= CLSID_NULL
;
9515 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9520 if (nbByte
!= sizeof(CLSID
))
9521 return STG_E_READFAULT
;
9527 /************************************************************************
9528 * OleConvert Functions
9529 ***********************************************************************/
9531 #define OLESTREAM_ID 0x501
9532 #define OLESTREAM_MAX_STR_LEN 255
9534 /* OLESTREAM memory structure to use for Get and Put Routines */
9539 DWORD dwOleTypeNameLength
;
9540 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9541 CHAR
*pstrOleObjFileName
;
9542 DWORD dwOleObjFileNameLength
;
9543 DWORD dwMetaFileWidth
;
9544 DWORD dwMetaFileHeight
;
9545 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9548 } OLECONVERT_OLESTREAM_DATA
;
9550 /* CompObj Stream structure */
9553 BYTE byUnknown1
[12];
9555 DWORD dwCLSIDNameLength
;
9556 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9557 DWORD dwOleTypeNameLength
;
9558 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9559 DWORD dwProgIDNameLength
;
9560 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9561 BYTE byUnknown2
[16];
9562 } OLECONVERT_ISTORAGE_COMPOBJ
;
9564 /* Ole Presentation Stream structure */
9567 BYTE byUnknown1
[28];
9572 } OLECONVERT_ISTORAGE_OLEPRES
;
9575 /*************************************************************************
9576 * OLECONVERT_LoadOLE10 [Internal]
9578 * Loads the OLE10 STREAM to memory
9581 * pOleStream [I] The OLESTREAM
9582 * pData [I] Data Structure for the OLESTREAM Data
9586 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9587 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9590 * This function is used by OleConvertOLESTREAMToIStorage only.
9592 * Memory allocated for pData must be freed by the caller
9594 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStream1
)
9597 HRESULT hRes
= S_OK
;
9601 pData
->pData
= NULL
;
9602 pData
->pstrOleObjFileName
= NULL
;
9604 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9607 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9608 if(dwSize
!= sizeof(pData
->dwOleID
))
9610 hRes
= CONVERT10_E_OLESTREAM_GET
;
9612 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9614 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9625 /* Get the TypeID... more info needed for this field */
9626 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9627 if(dwSize
!= sizeof(pData
->dwTypeID
))
9629 hRes
= CONVERT10_E_OLESTREAM_GET
;
9634 if(pData
->dwTypeID
!= 0)
9636 /* Get the length of the OleTypeName */
9637 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9638 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9640 hRes
= CONVERT10_E_OLESTREAM_GET
;
9645 if(pData
->dwOleTypeNameLength
> 0)
9647 /* Get the OleTypeName */
9648 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9649 if(dwSize
!= pData
->dwOleTypeNameLength
)
9651 hRes
= CONVERT10_E_OLESTREAM_GET
;
9657 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9658 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9660 hRes
= CONVERT10_E_OLESTREAM_GET
;
9664 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9665 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9666 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9667 if(pData
->pstrOleObjFileName
)
9669 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9670 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9672 hRes
= CONVERT10_E_OLESTREAM_GET
;
9676 hRes
= CONVERT10_E_OLESTREAM_GET
;
9681 /* Get the Width of the Metafile */
9682 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9683 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9685 hRes
= CONVERT10_E_OLESTREAM_GET
;
9689 /* Get the Height of the Metafile */
9690 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9691 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9693 hRes
= CONVERT10_E_OLESTREAM_GET
;
9699 /* Get the Length of the Data */
9700 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9701 if(dwSize
!= sizeof(pData
->dwDataLength
))
9703 hRes
= CONVERT10_E_OLESTREAM_GET
;
9707 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9709 if(!bStream1
) /* if it is a second OLE stream data */
9711 pData
->dwDataLength
-= 8;
9712 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9713 if(dwSize
!= sizeof(pData
->strUnknown
))
9715 hRes
= CONVERT10_E_OLESTREAM_GET
;
9721 if(pData
->dwDataLength
> 0)
9723 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9725 /* Get Data (ex. IStorage, Metafile, or BMP) */
9728 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9729 if(dwSize
!= pData
->dwDataLength
)
9731 hRes
= CONVERT10_E_OLESTREAM_GET
;
9736 hRes
= CONVERT10_E_OLESTREAM_GET
;
9745 /*************************************************************************
9746 * OLECONVERT_SaveOLE10 [Internal]
9748 * Saves the OLE10 STREAM From memory
9751 * pData [I] Data Structure for the OLESTREAM Data
9752 * pOleStream [I] The OLESTREAM to save
9756 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9759 * This function is used by OleConvertIStorageToOLESTREAM only.
9762 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9765 HRESULT hRes
= S_OK
;
9769 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9770 if(dwSize
!= sizeof(pData
->dwOleID
))
9772 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9777 /* Set the TypeID */
9778 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9779 if(dwSize
!= sizeof(pData
->dwTypeID
))
9781 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9785 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9787 /* Set the Length of the OleTypeName */
9788 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9789 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9791 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9796 if(pData
->dwOleTypeNameLength
> 0)
9798 /* Set the OleTypeName */
9799 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9800 if(dwSize
!= pData
->dwOleTypeNameLength
)
9802 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9809 /* Set the width of the Metafile */
9810 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9811 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9813 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9819 /* Set the height of the Metafile */
9820 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9821 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9823 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9829 /* Set the length of the Data */
9830 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9831 if(dwSize
!= sizeof(pData
->dwDataLength
))
9833 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9839 if(pData
->dwDataLength
> 0)
9841 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9842 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9843 if(dwSize
!= pData
->dwDataLength
)
9845 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9853 /*************************************************************************
9854 * OLECONVERT_GetOLE20FromOLE10[Internal]
9856 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9857 * opens it, and copies the content to the dest IStorage for
9858 * OleConvertOLESTREAMToIStorage
9862 * pDestStorage [I] The IStorage to copy the data to
9863 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9864 * nBufferLength [I] The size of the buffer
9873 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9877 IStorage
*pTempStorage
;
9878 DWORD dwNumOfBytesWritten
;
9879 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9881 /* Create a temp File */
9882 GetTempPathW(MAX_PATH
, wstrTempDir
);
9883 GetTempFileNameW(wstrTempDir
, L
"sis", 0, wstrTempFile
);
9884 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9886 if(hFile
!= INVALID_HANDLE_VALUE
)
9888 /* Write IStorage Data to File */
9889 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9892 /* Open and copy temp storage to the Dest Storage */
9893 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9896 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9897 IStorage_Release(pTempStorage
);
9899 DeleteFileW(wstrTempFile
);
9904 /*************************************************************************
9905 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9907 * Saves the OLE10 STREAM From memory
9910 * pStorage [I] The Src IStorage to copy
9911 * pData [I] The Dest Memory to write to.
9914 * The size in bytes allocated for pData
9917 * Memory allocated for pData must be freed by the caller
9919 * Used by OleConvertIStorageToOLESTREAM only.
9922 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9926 DWORD nDataLength
= 0;
9927 IStorage
*pTempStorage
;
9928 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9932 /* Create temp Storage */
9933 GetTempPathW(MAX_PATH
, wstrTempDir
);
9934 GetTempFileNameW(wstrTempDir
, L
"sis", 0, wstrTempFile
);
9935 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9939 /* Copy Src Storage to the Temp Storage */
9940 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9941 IStorage_Release(pTempStorage
);
9943 /* Open Temp Storage as a file and copy to memory */
9944 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9945 if(hFile
!= INVALID_HANDLE_VALUE
)
9947 nDataLength
= GetFileSize(hFile
, NULL
);
9948 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9949 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9952 DeleteFileW(wstrTempFile
);
9957 /*************************************************************************
9958 * OLECONVERT_CreateCompObjStream [Internal]
9960 * Creates a "\001CompObj" is the destination IStorage if necessary.
9963 * pStorage [I] The dest IStorage to create the CompObj Stream
9965 * strOleTypeName [I] The ProgID
9969 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9972 * This function is used by OleConvertOLESTREAMToIStorage only.
9974 * The stream data is stored in the OLESTREAM and there should be
9975 * no need to recreate the stream. If the stream is manually
9976 * deleted it will attempt to create it by querying the registry.
9980 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9983 HRESULT hStorageRes
, hRes
= S_OK
;
9984 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9985 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9987 static const BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9988 static const BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9990 /* Initialize the CompObj structure */
9991 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9992 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9993 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9996 /* Create a CompObj stream if it doesn't exist */
9997 hStorageRes
= IStorage_CreateStream(pStorage
, L
"\1CompObj",
9998 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9999 if(hStorageRes
== S_OK
)
10001 /* copy the OleTypeName to the compobj struct */
10002 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
10003 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
10005 /* copy the OleTypeName to the compobj struct */
10006 /* Note: in the test made, these were Identical */
10007 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
10008 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
10010 /* Get the CLSID */
10011 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
10012 bufferW
, OLESTREAM_MAX_STR_LEN
);
10013 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
10019 /* Get the CLSID Default Name from the Registry */
10020 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
10021 if(hErr
== ERROR_SUCCESS
)
10023 char strTemp
[OLESTREAM_MAX_STR_LEN
];
10024 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
10025 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
10026 if(hErr
== ERROR_SUCCESS
)
10028 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
10034 /* Write CompObj Structure to stream */
10035 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
10037 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
10039 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
10040 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
10042 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
10044 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
10045 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
10047 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
10049 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
10050 if(IStorageCompObj
.dwProgIDNameLength
> 0)
10052 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
10054 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
10055 IStream_Release(pStream
);
10061 /*************************************************************************
10062 * OLECONVERT_CreateOlePresStream[Internal]
10064 * Creates the "\002OlePres000" Stream with the Metafile data
10067 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10068 * dwExtentX [I] Width of the Metafile
10069 * dwExtentY [I] Height of the Metafile
10070 * pData [I] Metafile data
10071 * dwDataLength [I] Size of the Metafile data
10075 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10078 * This function is used by OleConvertOLESTREAMToIStorage only.
10081 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10085 static const BYTE pOlePresStreamHeader
[] =
10087 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10088 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10089 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10090 0x00, 0x00, 0x00, 0x00
10093 static const BYTE pOlePresStreamHeaderEmpty
[] =
10095 0x00, 0x00, 0x00, 0x00,
10096 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10097 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10098 0x00, 0x00, 0x00, 0x00
10101 /* Create the OlePres000 Stream */
10102 hRes
= IStorage_CreateStream(pStorage
, L
"\2OlePres000",
10103 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10108 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10110 memset(&OlePres
, 0, sizeof(OlePres
));
10111 /* Do we have any metafile data to save */
10112 if(dwDataLength
> 0)
10114 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10115 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10119 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10120 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10122 /* Set width and height of the metafile */
10123 OlePres
.dwExtentX
= dwExtentX
;
10124 OlePres
.dwExtentY
= -dwExtentY
;
10126 /* Set Data and Length */
10127 if(dwDataLength
> sizeof(METAFILEPICT16
))
10129 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10130 OlePres
.pData
= &(pData
[8]);
10132 /* Save OlePres000 Data to Stream */
10133 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10134 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10135 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10136 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10137 if(OlePres
.dwSize
> 0)
10139 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10141 IStream_Release(pStream
);
10145 /*************************************************************************
10146 * OLECONVERT_CreateOle10NativeStream [Internal]
10148 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10151 * pStorage [I] Dest storage to create the stream in
10152 * pData [I] Ole10 Native Data (ex. bmp)
10153 * dwDataLength [I] Size of the Ole10 Native Data
10159 * This function is used by OleConvertOLESTREAMToIStorage only.
10161 * Might need to verify the data and return appropriate error message
10164 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10169 /* Create the Ole10Native Stream */
10170 hRes
= IStorage_CreateStream(pStorage
, L
"\1Ole10Native",
10171 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10175 /* Write info to stream */
10176 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10177 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10178 IStream_Release(pStream
);
10183 /*************************************************************************
10184 * OLECONVERT_GetOLE10ProgID [Internal]
10186 * Finds the ProgID (or OleTypeID) from the IStorage
10189 * pStorage [I] The Src IStorage to get the ProgID
10190 * strProgID [I] the ProgID string to get
10191 * dwSize [I] the size of the string
10195 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10198 * This function is used by OleConvertIStorageToOLESTREAM only.
10202 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10206 LARGE_INTEGER iSeekPos
;
10207 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10209 /* Open the CompObj Stream */
10210 hRes
= IStorage_OpenStream(pStorage
, L
"\1CompObj", NULL
,
10211 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10215 /*Get the OleType from the CompObj Stream */
10216 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10217 iSeekPos
.u
.HighPart
= 0;
10219 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10220 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10221 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10222 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10223 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10224 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10225 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10227 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10230 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10232 IStream_Release(pStream
);
10237 LPOLESTR wstrProgID
;
10239 /* Get the OleType from the registry */
10240 REFCLSID clsid
= &(stat
.clsid
);
10241 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10242 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10245 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10246 CoTaskMemFree(wstrProgID
);
10253 /*************************************************************************
10254 * OLECONVERT_GetOle10PresData [Internal]
10256 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10259 * pStorage [I] Src IStroage
10260 * pOleStream [I] Dest OleStream Mem Struct
10266 * This function is used by OleConvertIStorageToOLESTREAM only.
10268 * Memory allocated for pData must be freed by the caller
10272 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10278 /* Initialize Default data for OLESTREAM */
10279 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10280 pOleStreamData
[0].dwTypeID
= 2;
10281 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10282 pOleStreamData
[1].dwTypeID
= 0;
10283 pOleStreamData
[0].dwMetaFileWidth
= 0;
10284 pOleStreamData
[0].dwMetaFileHeight
= 0;
10285 pOleStreamData
[0].pData
= NULL
;
10286 pOleStreamData
[1].pData
= NULL
;
10288 /* Open Ole10Native Stream */
10289 hRes
= IStorage_OpenStream(pStorage
, L
"\1Ole10Native", NULL
,
10290 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10294 /* Read Size and Data */
10295 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10296 if(pOleStreamData
->dwDataLength
> 0)
10298 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10299 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10301 IStream_Release(pStream
);
10307 /*************************************************************************
10308 * OLECONVERT_GetOle20PresData[Internal]
10310 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10313 * pStorage [I] Src IStroage
10314 * pOleStreamData [I] Dest OleStream Mem Struct
10320 * This function is used by OleConvertIStorageToOLESTREAM only.
10322 * Memory allocated for pData must be freed by the caller
10324 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10328 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10330 /* Initialize Default data for OLESTREAM */
10331 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10332 pOleStreamData
[0].dwTypeID
= 2;
10333 pOleStreamData
[0].dwMetaFileWidth
= 0;
10334 pOleStreamData
[0].dwMetaFileHeight
= 0;
10335 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10336 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10337 pOleStreamData
[1].dwTypeID
= 0;
10338 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10339 pOleStreamData
[1].strOleTypeName
[0] = 0;
10340 pOleStreamData
[1].dwMetaFileWidth
= 0;
10341 pOleStreamData
[1].dwMetaFileHeight
= 0;
10342 pOleStreamData
[1].pData
= NULL
;
10343 pOleStreamData
[1].dwDataLength
= 0;
10346 /* Open OlePress000 stream */
10347 hRes
= IStorage_OpenStream(pStorage
, L
"\2OlePres000", NULL
,
10348 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10351 LARGE_INTEGER iSeekPos
;
10352 METAFILEPICT16 MetaFilePict
;
10353 static const char strMetafilePictName
[] = "METAFILEPICT";
10355 /* Set the TypeID for a Metafile */
10356 pOleStreamData
[1].dwTypeID
= 5;
10358 /* Set the OleTypeName to Metafile */
10359 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10360 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10362 iSeekPos
.u
.HighPart
= 0;
10363 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10365 /* Get Presentation Data */
10366 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10367 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10368 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10369 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10371 /*Set width and Height */
10372 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10373 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10374 if(olePress
.dwSize
> 0)
10377 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10379 /* Set MetaFilePict struct */
10380 MetaFilePict
.mm
= 8;
10381 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10382 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10383 MetaFilePict
.hMF
= 0;
10385 /* Get Metafile Data */
10386 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10387 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10388 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10390 IStream_Release(pStream
);
10394 /*************************************************************************
10395 * OleConvertOLESTREAMToIStorage [OLE32.@]
10397 * Read info on MSDN
10400 * DVTARGETDEVICE parameter is not handled
10401 * Still unsure of some mem fields for OLE 10 Stream
10402 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10403 * and "\001OLE" streams
10406 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10407 LPOLESTREAM pOleStream
,
10409 const DVTARGETDEVICE
* ptd
)
10413 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10415 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10417 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10421 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10424 if(pstg
== NULL
|| pOleStream
== NULL
)
10426 hRes
= E_INVALIDARG
;
10431 /* Load the OLESTREAM to Memory */
10432 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10437 /* Load the OLESTREAM to Memory (part 2)*/
10438 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10444 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10446 /* Do we have the IStorage Data in the OLESTREAM */
10447 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10449 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10450 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10454 /* It must be an original OLE 1.0 source */
10455 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10460 /* It must be an original OLE 1.0 source */
10461 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10464 /* Create CompObj Stream if necessary */
10465 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10468 /*Create the Ole Stream if necessary */
10469 STORAGE_CreateOleStream(pstg
, 0);
10474 /* Free allocated memory */
10475 for(i
=0; i
< 2; i
++)
10477 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10478 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10479 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10484 /*************************************************************************
10485 * OleConvertIStorageToOLESTREAM [OLE32.@]
10487 * Read info on MSDN
10489 * Read info on MSDN
10492 * Still unsure of some mem fields for OLE 10 Stream
10493 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10494 * and "\001OLE" streams.
10497 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10499 LPOLESTREAM pOleStream
)
10502 HRESULT hRes
= S_OK
;
10504 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10506 TRACE("%p %p\n", pstg
, pOleStream
);
10508 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10510 if(pstg
== NULL
|| pOleStream
== NULL
)
10512 hRes
= E_INVALIDARG
;
10516 /* Get the ProgID */
10517 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10518 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10522 /* Was it originally Ole10 */
10523 hRes
= IStorage_OpenStream(pstg
, L
"\1Ole10Native", 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10526 IStream_Release(pStream
);
10527 /* Get Presentation Data for Ole10Native */
10528 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10532 /* Get Presentation Data (OLE20) */
10533 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10536 /* Save OLESTREAM */
10537 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10540 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10545 /* Free allocated memory */
10546 for(i
=0; i
< 2; i
++)
10548 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10554 enum stream_1ole_flags
{
10555 OleStream_LinkedObject
= 0x00000001,
10556 OleStream_Convert
= 0x00000004
10559 /*************************************************************************
10560 * OleConvertIStorageToOLESTREAMEx [OLE32.@]
10562 HRESULT WINAPI
OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg
, CLIPFORMAT cf
, LONG width
, LONG height
,
10563 DWORD size
, LPSTGMEDIUM medium
, LPOLESTREAM olestream
)
10565 FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg
, cf
, width
, height
, size
, medium
, olestream
);
10570 /***********************************************************************
10571 * GetConvertStg (OLE32.@)
10573 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10575 static const DWORD version_magic
= 0x02000001;
10580 TRACE("%p\n", stg
);
10582 if (!stg
) return E_INVALIDARG
;
10584 hr
= IStorage_OpenStream(stg
, L
"\1Ole", NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10585 if (FAILED(hr
)) return hr
;
10587 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10588 IStream_Release(stream
);
10589 if (FAILED(hr
)) return hr
;
10591 if (header
[0] != version_magic
)
10593 ERR("got wrong version magic for 1Ole stream, %#lx.\n", header
[0]);
10597 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10600 /***********************************************************************
10601 * SetConvertStg (OLE32.@)
10603 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10605 DWORD flags
= convert
? OleStream_Convert
: 0;
10610 TRACE("(%p, %d)\n", storage
, convert
);
10612 hr
= IStorage_OpenStream(storage
, L
"\1Ole", NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10615 if (hr
!= STG_E_FILENOTFOUND
)
10618 return STORAGE_CreateOleStream(storage
, flags
);
10621 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10624 IStream_Release(stream
);
10628 /* update flag if differs */
10629 if ((header
[1] ^ flags
) & OleStream_Convert
)
10631 LARGE_INTEGER pos
= {{0}};
10633 if (header
[1] & OleStream_Convert
)
10634 flags
= header
[1] & ~OleStream_Convert
;
10636 flags
= header
[1] | OleStream_Convert
;
10638 pos
.QuadPart
= sizeof(DWORD
);
10639 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10642 IStream_Release(stream
);
10646 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10649 IStream_Release(stream
);