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 %08x\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)->(%d): 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("<-- %08x\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,%u,%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("<-- %08x (asked %u, got %u)\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,%u\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("<-- %08x\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) AddRef to %d\n", This
, 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) ReleaseRef to %d\n", This
, 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("<-- %08x\n", hr
);
1454 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1456 StgStreamImpl
*strm
;
1458 TRACE("%p,%d\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,%d\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, %x, %d, %p)\n",
1510 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1512 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1520 if ( FAILED( validateSTGM(grfMode
) ) ||
1521 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1523 res
= STG_E_INVALIDFLAG
;
1530 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1532 res
= STG_E_INVALIDFUNCTION
;
1538 res
= STG_E_REVERTED
;
1543 * Check that we're compatible with the parent's storage mode, but
1544 * only if we are not in transacted mode
1546 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1547 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1549 res
= STG_E_INVALIDFLAG
;
1555 * Search for the element with the given name
1557 streamEntryRef
= findElement(
1559 This
->storageDirEntry
,
1564 * If it was found, construct the stream object and return a pointer to it.
1566 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1567 (currentEntry
.stgType
==STGTY_STREAM
) )
1569 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1571 /* A single stream cannot be opened a second time. */
1572 res
= STG_E_ACCESSDENIED
;
1576 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1580 newStream
->grfMode
= grfMode
;
1581 *ppstm
= &newStream
->IStream_iface
;
1583 IStream_AddRef(*ppstm
);
1589 res
= E_OUTOFMEMORY
;
1593 res
= STG_E_FILENOTFOUND
;
1597 TRACE("<-- IStream %p\n", *ppstm
);
1598 TRACE("<-- %08x\n", res
);
1602 /************************************************************************
1603 * StorageBaseImpl_OpenStorage (IStorage)
1605 * This method will open a new storage object from the current storage.
1607 * See Windows documentation for more details on IStorage methods.
1609 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1611 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1612 IStorage
* pstgPriority
, /* [unique][in] */
1613 DWORD grfMode
, /* [in] */
1614 SNB snbExclude
, /* [unique][in] */
1615 DWORD reserved
, /* [in] */
1616 IStorage
** ppstg
) /* [out] */
1618 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1619 StorageInternalImpl
* newStorage
;
1620 StorageBaseImpl
* newTransactedStorage
;
1621 DirEntry currentEntry
;
1622 DirRef storageEntryRef
;
1623 HRESULT res
= STG_E_UNKNOWN
;
1625 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1626 iface
, debugstr_w(pwcsName
), pstgPriority
,
1627 grfMode
, snbExclude
, reserved
, ppstg
);
1629 if ((pwcsName
==NULL
) || (ppstg
==0) )
1635 if (This
->openFlags
& STGM_SIMPLE
)
1637 res
= STG_E_INVALIDFUNCTION
;
1642 if (snbExclude
!= NULL
)
1644 res
= STG_E_INVALIDPARAMETER
;
1648 if ( FAILED( validateSTGM(grfMode
) ))
1650 res
= STG_E_INVALIDFLAG
;
1657 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1658 (grfMode
& STGM_DELETEONRELEASE
) ||
1659 (grfMode
& STGM_PRIORITY
) )
1661 res
= STG_E_INVALIDFUNCTION
;
1666 return STG_E_REVERTED
;
1669 * Check that we're compatible with the parent's storage mode,
1670 * but only if we are not transacted
1672 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1673 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1675 res
= STG_E_ACCESSDENIED
;
1682 storageEntryRef
= findElement(
1684 This
->storageDirEntry
,
1688 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1689 (currentEntry
.stgType
==STGTY_STORAGE
) )
1691 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1693 /* A single storage cannot be opened a second time. */
1694 res
= STG_E_ACCESSDENIED
;
1698 newStorage
= StorageInternalImpl_Construct(
1703 if (newStorage
!= 0)
1705 if (grfMode
& STGM_TRANSACTED
)
1707 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1711 HeapFree(GetProcessHeap(), 0, newStorage
);
1715 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1719 *ppstg
= &newStorage
->base
.IStorage_iface
;
1722 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1728 res
= STG_E_INSUFFICIENTMEMORY
;
1732 res
= STG_E_FILENOTFOUND
;
1735 TRACE("<-- %08x\n", res
);
1739 /************************************************************************
1740 * StorageBaseImpl_EnumElements (IStorage)
1742 * This method will create an enumerator object that can be used to
1743 * retrieve information about all the elements in the storage object.
1745 * See Windows documentation for more details on IStorage methods.
1747 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1749 DWORD reserved1
, /* [in] */
1750 void* reserved2
, /* [size_is][unique][in] */
1751 DWORD reserved3
, /* [in] */
1752 IEnumSTATSTG
** ppenum
) /* [out] */
1754 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1755 IEnumSTATSTGImpl
* newEnum
;
1757 TRACE("(%p, %d, %p, %d, %p)\n",
1758 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1761 return E_INVALIDARG
;
1764 return STG_E_REVERTED
;
1766 newEnum
= IEnumSTATSTGImpl_Construct(
1768 This
->storageDirEntry
);
1772 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1776 return E_OUTOFMEMORY
;
1779 /************************************************************************
1780 * StorageBaseImpl_Stat (IStorage)
1782 * This method will retrieve information about this storage object.
1784 * See Windows documentation for more details on IStorage methods.
1786 static HRESULT WINAPI
StorageBaseImpl_Stat(
1788 STATSTG
* pstatstg
, /* [out] */
1789 DWORD grfStatFlag
) /* [in] */
1791 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1792 DirEntry currentEntry
;
1793 HRESULT res
= STG_E_UNKNOWN
;
1795 TRACE("(%p, %p, %x)\n",
1796 iface
, pstatstg
, grfStatFlag
);
1806 res
= STG_E_REVERTED
;
1810 res
= StorageBaseImpl_ReadDirEntry(
1812 This
->storageDirEntry
,
1817 StorageUtl_CopyDirEntryToSTATSTG(
1823 pstatstg
->grfMode
= This
->openFlags
;
1824 pstatstg
->grfStateBits
= This
->stateBits
;
1830 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
1832 TRACE("<-- %08x\n", res
);
1836 /************************************************************************
1837 * StorageBaseImpl_RenameElement (IStorage)
1839 * This method will rename the specified element.
1841 * See Windows documentation for more details on IStorage methods.
1843 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1845 const OLECHAR
* pwcsOldName
, /* [in] */
1846 const OLECHAR
* pwcsNewName
) /* [in] */
1848 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1849 DirEntry currentEntry
;
1850 DirRef currentEntryRef
;
1852 TRACE("(%p, %s, %s)\n",
1853 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1856 return STG_E_REVERTED
;
1858 currentEntryRef
= findElement(This
,
1859 This
->storageDirEntry
,
1863 if (currentEntryRef
!= DIRENTRY_NULL
)
1866 * There is already an element with the new name
1868 return STG_E_FILEALREADYEXISTS
;
1872 * Search for the old element name
1874 currentEntryRef
= findElement(This
,
1875 This
->storageDirEntry
,
1879 if (currentEntryRef
!= DIRENTRY_NULL
)
1881 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1882 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1884 WARN("Element is already open; cannot rename.\n");
1885 return STG_E_ACCESSDENIED
;
1888 /* Remove the element from its current position in the tree */
1889 removeFromTree(This
, This
->storageDirEntry
,
1892 /* Change the name of the element */
1893 lstrcpyW(currentEntry
.name
, pwcsNewName
);
1895 /* Delete any sibling links */
1896 currentEntry
.leftChild
= DIRENTRY_NULL
;
1897 currentEntry
.rightChild
= DIRENTRY_NULL
;
1899 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1902 /* Insert the element in a new position in the tree */
1903 insertIntoTree(This
, This
->storageDirEntry
,
1909 * There is no element with the old name
1911 return STG_E_FILENOTFOUND
;
1914 return StorageBaseImpl_Flush(This
);
1917 /************************************************************************
1918 * StorageBaseImpl_CreateStream (IStorage)
1920 * This method will create a stream object within this storage
1922 * See Windows documentation for more details on IStorage methods.
1924 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1926 const OLECHAR
* pwcsName
, /* [string][in] */
1927 DWORD grfMode
, /* [in] */
1928 DWORD reserved1
, /* [in] */
1929 DWORD reserved2
, /* [in] */
1930 IStream
** ppstm
) /* [out] */
1932 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1933 StgStreamImpl
* newStream
;
1934 DirEntry currentEntry
, newStreamEntry
;
1935 DirRef currentEntryRef
, newStreamEntryRef
;
1938 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1939 iface
, debugstr_w(pwcsName
), grfMode
,
1940 reserved1
, reserved2
, ppstm
);
1943 return STG_E_INVALIDPOINTER
;
1946 return STG_E_INVALIDNAME
;
1948 if (reserved1
|| reserved2
)
1949 return STG_E_INVALIDPARAMETER
;
1951 if ( FAILED( validateSTGM(grfMode
) ))
1952 return STG_E_INVALIDFLAG
;
1954 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1955 return STG_E_INVALIDFLAG
;
1958 return STG_E_REVERTED
;
1963 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1964 (grfMode
& STGM_TRANSACTED
))
1965 return STG_E_INVALIDFUNCTION
;
1968 * Don't worry about permissions in transacted mode, as we can always write
1969 * changes; we just can't always commit them.
1971 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1972 /* Can't create a stream on read-only storage */
1973 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1974 return STG_E_ACCESSDENIED
;
1976 /* Can't create a stream with greater access than the parent. */
1977 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1978 return STG_E_ACCESSDENIED
;
1981 if(This
->openFlags
& STGM_SIMPLE
)
1982 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1986 currentEntryRef
= findElement(This
,
1987 This
->storageDirEntry
,
1991 if (currentEntryRef
!= DIRENTRY_NULL
)
1994 * An element with this name already exists
1996 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1998 IStorage_DestroyElement(iface
, pwcsName
);
2001 return STG_E_FILEALREADYEXISTS
;
2005 * memset the empty entry
2007 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
2009 newStreamEntry
.sizeOfNameString
=
2010 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
2012 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2013 return STG_E_INVALIDNAME
;
2015 lstrcpyW(newStreamEntry
.name
, pwcsName
);
2017 newStreamEntry
.stgType
= STGTY_STREAM
;
2018 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2019 newStreamEntry
.size
.u
.LowPart
= 0;
2020 newStreamEntry
.size
.u
.HighPart
= 0;
2022 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
2023 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
2024 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
2026 /* call CoFileTime to get the current time
2027 newStreamEntry.ctime
2028 newStreamEntry.mtime
2031 /* newStreamEntry.clsid */
2034 * Create an entry with the new data
2036 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
2041 * Insert the new entry in the parent storage's tree.
2043 hr
= insertIntoTree(
2045 This
->storageDirEntry
,
2049 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
2054 * Open the stream to return it.
2056 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2060 *ppstm
= &newStream
->IStream_iface
;
2061 IStream_AddRef(*ppstm
);
2065 return STG_E_INSUFFICIENTMEMORY
;
2068 return StorageBaseImpl_Flush(This
);
2071 /************************************************************************
2072 * StorageBaseImpl_SetClass (IStorage)
2074 * This method will write the specified CLSID in the directory entry of this
2077 * See Windows documentation for more details on IStorage methods.
2079 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2081 REFCLSID clsid
) /* [in] */
2083 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2085 DirEntry currentEntry
;
2087 TRACE("(%p, %s)\n", iface
, wine_dbgstr_guid(clsid
));
2090 return STG_E_REVERTED
;
2092 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2093 This
->storageDirEntry
,
2095 if (SUCCEEDED(hRes
))
2097 currentEntry
.clsid
= *clsid
;
2099 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2100 This
->storageDirEntry
,
2104 if (SUCCEEDED(hRes
))
2105 hRes
= StorageBaseImpl_Flush(This
);
2110 /************************************************************************
2111 * StorageBaseImpl_CreateStorage (IStorage)
2113 * This method will create the storage object within the provided storage.
2115 * See Windows documentation for more details on IStorage methods.
2117 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2119 const OLECHAR
*pwcsName
, /* [string][in] */
2120 DWORD grfMode
, /* [in] */
2121 DWORD reserved1
, /* [in] */
2122 DWORD reserved2
, /* [in] */
2123 IStorage
**ppstg
) /* [out] */
2125 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2127 DirEntry currentEntry
;
2129 DirRef currentEntryRef
;
2133 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2134 iface
, debugstr_w(pwcsName
), grfMode
,
2135 reserved1
, reserved2
, ppstg
);
2138 return STG_E_INVALIDPOINTER
;
2140 if (This
->openFlags
& STGM_SIMPLE
)
2142 return STG_E_INVALIDFUNCTION
;
2146 return STG_E_INVALIDNAME
;
2150 if ( FAILED( validateSTGM(grfMode
) ) ||
2151 (grfMode
& STGM_DELETEONRELEASE
) )
2153 WARN("bad grfMode: 0x%x\n", grfMode
);
2154 return STG_E_INVALIDFLAG
;
2158 return STG_E_REVERTED
;
2161 * Check that we're compatible with the parent's storage mode
2163 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2164 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2166 WARN("access denied\n");
2167 return STG_E_ACCESSDENIED
;
2170 currentEntryRef
= findElement(This
,
2171 This
->storageDirEntry
,
2175 if (currentEntryRef
!= DIRENTRY_NULL
)
2178 * An element with this name already exists
2180 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2181 ((This
->openFlags
& STGM_TRANSACTED
) ||
2182 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2184 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2190 WARN("file already exists\n");
2191 return STG_E_FILEALREADYEXISTS
;
2194 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2195 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2197 WARN("read-only storage\n");
2198 return STG_E_ACCESSDENIED
;
2201 memset(&newEntry
, 0, sizeof(DirEntry
));
2203 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2205 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2207 FIXME("name too long\n");
2208 return STG_E_INVALIDNAME
;
2211 lstrcpyW(newEntry
.name
, pwcsName
);
2213 newEntry
.stgType
= STGTY_STORAGE
;
2214 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2215 newEntry
.size
.u
.LowPart
= 0;
2216 newEntry
.size
.u
.HighPart
= 0;
2218 newEntry
.leftChild
= DIRENTRY_NULL
;
2219 newEntry
.rightChild
= DIRENTRY_NULL
;
2220 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2222 /* call CoFileTime to get the current time
2227 /* newEntry.clsid */
2230 * Create a new directory entry for the storage
2232 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2237 * Insert the new directory entry into the parent storage's tree
2239 hr
= insertIntoTree(
2241 This
->storageDirEntry
,
2245 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2250 * Open it to get a pointer to return.
2252 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2254 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2260 hr
= StorageBaseImpl_Flush(This
);
2265 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2266 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2267 SNB snbExclude
, IStorage
*pstgDest
)
2272 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2275 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2278 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2279 skip_stream
, snbExclude
, pstgDest
);
2281 TRACE("<-- %08x\n", hr
);
2285 /*************************************************************************
2288 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2290 DWORD ciidExclude
, /* [in] */
2291 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2292 SNB snbExclude
, /* [unique][in] */
2293 IStorage
* pstgDest
) /* [unique][in] */
2295 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2297 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2300 TRACE("(%p, %d, %p, %p, %p)\n",
2301 iface
, ciidExclude
, rgiidExclude
,
2302 snbExclude
, pstgDest
);
2304 if ( pstgDest
== 0 )
2305 return STG_E_INVALIDPOINTER
;
2307 for(i
= 0; i
< ciidExclude
; ++i
)
2309 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2310 skip_storage
= TRUE
;
2311 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2314 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2319 /* Give up early if it looks like this would be infinitely recursive.
2320 * Oddly enough, this includes some cases that aren't really recursive, like
2321 * copying to a transacted child. */
2322 IStorage
*pstgDestAncestor
= pstgDest
;
2323 IStorage
*pstgDestAncestorChild
= NULL
;
2325 /* Go up the chain from the destination until we find the source storage. */
2326 while (pstgDestAncestor
!= iface
) {
2327 pstgDestAncestorChild
= pstgDest
;
2329 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2331 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2333 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2335 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2337 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2339 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2345 if (pstgDestAncestor
== iface
)
2349 if (pstgDestAncestorChild
&& snbExclude
)
2351 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2353 WCHAR
**snb
= snbExclude
;
2355 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2357 while ( *snb
!= NULL
&& fail
)
2359 if ( wcscmp(data
.name
, *snb
) == 0 )
2366 return STG_E_ACCESSDENIED
;
2370 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2371 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2374 /*************************************************************************
2375 * MoveElementTo (IStorage)
2377 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2379 const OLECHAR
*pwcsName
, /* [string][in] */
2380 IStorage
*pstgDest
, /* [unique][in] */
2381 const OLECHAR
*pwcsNewName
,/* [string][in] */
2382 DWORD grfFlags
) /* [in] */
2384 FIXME("(%p %s %p %s %u): stub\n", iface
,
2385 debugstr_w(pwcsName
), pstgDest
,
2386 debugstr_w(pwcsNewName
), grfFlags
);
2390 /*************************************************************************
2393 * Ensures that any changes made to a storage object open in transacted mode
2394 * are reflected in the parent storage
2396 * In a non-transacted mode, this ensures all cached writes are completed.
2398 static HRESULT WINAPI
StorageBaseImpl_Commit(
2400 DWORD grfCommitFlags
)/* [in] */
2402 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2403 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
2404 return StorageBaseImpl_Flush(This
);
2407 /*************************************************************************
2410 * Discard all changes that have been made since the last commit operation
2412 static HRESULT WINAPI
StorageBaseImpl_Revert(
2415 TRACE("(%p)\n", iface
);
2419 /*********************************************************************
2421 * Internal helper function for StorageBaseImpl_DestroyElement()
2423 * Delete the contents of a storage entry.
2426 static HRESULT
deleteStorageContents(
2427 StorageBaseImpl
*parentStorage
,
2428 DirRef indexToDelete
,
2429 DirEntry entryDataToDelete
)
2431 IEnumSTATSTG
*elements
= 0;
2432 IStorage
*childStorage
= 0;
2433 STATSTG currentElement
;
2435 HRESULT destroyHr
= S_OK
;
2436 StorageInternalImpl
*stg
, *stg2
;
2438 TRACE("%p,%d\n", parentStorage
, indexToDelete
);
2440 /* Invalidate any open storage objects. */
2441 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2443 if (stg
->base
.storageDirEntry
== indexToDelete
)
2445 StorageBaseImpl_Invalidate(&stg
->base
);
2450 * Open the storage and enumerate it
2452 hr
= IStorage_OpenStorage(
2453 &parentStorage
->IStorage_iface
,
2454 entryDataToDelete
.name
,
2456 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2463 TRACE("<-- %08x\n", hr
);
2468 * Enumerate the elements
2470 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2473 IStorage_Release(childStorage
);
2474 TRACE("<-- %08x\n", hr
);
2481 * Obtain the next element
2483 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2486 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2488 CoTaskMemFree(currentElement
.pwcsName
);
2492 * We need to Reset the enumeration every time because we delete elements
2493 * and the enumeration could be invalid
2495 IEnumSTATSTG_Reset(elements
);
2497 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2499 IStorage_Release(childStorage
);
2500 IEnumSTATSTG_Release(elements
);
2502 TRACE("%08x\n", hr
);
2506 /*********************************************************************
2508 * Internal helper function for StorageBaseImpl_DestroyElement()
2510 * Perform the deletion of a stream's data
2513 static HRESULT
deleteStreamContents(
2514 StorageBaseImpl
*parentStorage
,
2515 DirRef indexToDelete
,
2516 DirEntry entryDataToDelete
)
2520 ULARGE_INTEGER size
;
2521 StgStreamImpl
*strm
, *strm2
;
2523 /* Invalidate any open stream objects. */
2524 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2526 if (strm
->dirEntry
== indexToDelete
)
2528 TRACE("Stream deleted %p\n", strm
);
2529 strm
->parentStorage
= NULL
;
2530 list_remove(&strm
->StrmListEntry
);
2534 size
.u
.HighPart
= 0;
2537 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2538 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2542 TRACE("<-- %08x\n", hr
);
2549 hr
= IStream_SetSize(pis
, size
);
2553 TRACE("<-- %08x\n", hr
);
2558 * Release the stream object.
2560 IStream_Release(pis
);
2561 TRACE("<-- %08x\n", hr
);
2565 /*************************************************************************
2566 * DestroyElement (IStorage)
2568 * Strategy: This implementation is built this way for simplicity not for speed.
2569 * I always delete the topmost element of the enumeration and adjust
2570 * the deleted element pointer all the time. This takes longer to
2571 * do but allows reinvoking DestroyElement whenever we encounter a
2572 * storage object. The optimisation resides in the usage of another
2573 * enumeration strategy that would give all the leaves of a storage
2574 * first. (postfix order)
2576 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2578 const OLECHAR
*pwcsName
)/* [string][in] */
2580 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2583 DirEntry entryToDelete
;
2584 DirRef entryToDeleteRef
;
2587 iface
, debugstr_w(pwcsName
));
2590 return STG_E_INVALIDPOINTER
;
2593 return STG_E_REVERTED
;
2595 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2596 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2597 return STG_E_ACCESSDENIED
;
2599 entryToDeleteRef
= findElement(
2601 This
->storageDirEntry
,
2605 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2607 TRACE("<-- STG_E_FILENOTFOUND\n");
2608 return STG_E_FILENOTFOUND
;
2611 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2613 hr
= deleteStorageContents(
2618 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2620 hr
= deleteStreamContents(
2628 TRACE("<-- %08x\n", hr
);
2633 * Remove the entry from its parent storage
2635 hr
= removeFromTree(
2637 This
->storageDirEntry
,
2641 * Invalidate the entry
2644 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2647 hr
= StorageBaseImpl_Flush(This
);
2649 TRACE("<-- %08x\n", hr
);
2653 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2655 struct list
*cur
, *cur2
;
2656 StgStreamImpl
*strm
=NULL
;
2657 StorageInternalImpl
*childstg
=NULL
;
2659 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2660 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2661 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2662 strm
->parentStorage
= NULL
;
2666 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2667 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2668 StorageBaseImpl_Invalidate( &childstg
->base
);
2671 if (stg
->transactedChild
)
2673 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2675 stg
->transactedChild
= NULL
;
2679 /******************************************************************************
2680 * SetElementTimes (IStorage)
2682 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2684 const OLECHAR
*pwcsName
,/* [string][in] */
2685 const FILETIME
*pctime
, /* [in] */
2686 const FILETIME
*patime
, /* [in] */
2687 const FILETIME
*pmtime
) /* [in] */
2689 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2693 /******************************************************************************
2694 * SetStateBits (IStorage)
2696 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2698 DWORD grfStateBits
,/* [in] */
2699 DWORD grfMask
) /* [in] */
2701 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2704 return STG_E_REVERTED
;
2706 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2710 /******************************************************************************
2711 * Internal stream list handlers
2714 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2716 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2717 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2720 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2722 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2723 list_remove(&(strm
->StrmListEntry
));
2726 static HRESULT
StorageBaseImpl_CopyStream(
2727 StorageBaseImpl
*dst
, DirRef dst_entry
,
2728 StorageBaseImpl
*src
, DirRef src_entry
)
2733 ULARGE_INTEGER bytes_copied
;
2734 ULONG bytestocopy
, bytesread
, byteswritten
;
2736 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2740 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2742 bytes_copied
.QuadPart
= 0;
2743 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2745 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2747 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2749 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2752 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2753 data
, &byteswritten
);
2756 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2757 bytes_copied
.QuadPart
+= byteswritten
;
2765 static HRESULT
StorageBaseImpl_DupStorageTree(
2766 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2767 StorageBaseImpl
*src
, DirRef src_entry
)
2771 BOOL has_stream
=FALSE
;
2773 if (src_entry
== DIRENTRY_NULL
)
2775 *dst_entry
= DIRENTRY_NULL
;
2779 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2782 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2783 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2784 data
.size
.QuadPart
= 0;
2786 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2790 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2793 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2796 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2798 if (SUCCEEDED(hr
) && has_stream
)
2799 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2804 static HRESULT
StorageBaseImpl_CopyStorageTree(
2805 StorageBaseImpl
*dst
, DirRef dst_entry
,
2806 StorageBaseImpl
*src
, DirRef src_entry
)
2809 DirEntry src_data
, dst_data
;
2810 DirRef new_root_entry
;
2812 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2816 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2821 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2822 dst_data
.clsid
= src_data
.clsid
;
2823 dst_data
.ctime
= src_data
.ctime
;
2824 dst_data
.mtime
= src_data
.mtime
;
2825 dst_data
.dirRootEntry
= new_root_entry
;
2829 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2834 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2838 ULARGE_INTEGER zero
;
2840 if (entry
== DIRENTRY_NULL
)
2845 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2847 if (SUCCEEDED(hr
) && include_siblings
)
2848 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2850 if (SUCCEEDED(hr
) && include_siblings
)
2851 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2854 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2856 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2857 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2860 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2866 /************************************************************************
2867 * StorageImpl implementation
2868 ***********************************************************************/
2870 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2871 ULARGE_INTEGER offset
,
2876 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2879 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2880 ULARGE_INTEGER offset
,
2883 ULONG
* bytesWritten
)
2885 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2888 /******************************************************************************
2889 * StorageImpl_LoadFileHeader
2891 * This method will read in the file header
2893 static HRESULT
StorageImpl_LoadFileHeader(
2897 BYTE headerBigBlock
[HEADER_SIZE
];
2899 ULARGE_INTEGER offset
;
2904 * Get a pointer to the big block of data containing the header.
2906 offset
.u
.HighPart
= 0;
2907 offset
.u
.LowPart
= 0;
2908 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2909 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2910 hr
= STG_E_FILENOTFOUND
;
2913 * Extract the information from the header.
2918 * Check for the "magic number" signature and return an error if it is not
2921 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2923 return STG_E_OLDFORMAT
;
2926 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2928 return STG_E_INVALIDHEADER
;
2931 StorageUtl_ReadWord(
2933 OFFSET_BIGBLOCKSIZEBITS
,
2934 &This
->bigBlockSizeBits
);
2936 StorageUtl_ReadWord(
2938 OFFSET_SMALLBLOCKSIZEBITS
,
2939 &This
->smallBlockSizeBits
);
2941 StorageUtl_ReadDWord(
2943 OFFSET_BBDEPOTCOUNT
,
2944 &This
->bigBlockDepotCount
);
2946 StorageUtl_ReadDWord(
2948 OFFSET_ROOTSTARTBLOCK
,
2949 &This
->rootStartBlock
);
2951 StorageUtl_ReadDWord(
2953 OFFSET_TRANSACTIONSIG
,
2954 &This
->transactionSig
);
2956 StorageUtl_ReadDWord(
2958 OFFSET_SMALLBLOCKLIMIT
,
2959 &This
->smallBlockLimit
);
2961 StorageUtl_ReadDWord(
2963 OFFSET_SBDEPOTSTART
,
2964 &This
->smallBlockDepotStart
);
2966 StorageUtl_ReadDWord(
2968 OFFSET_EXTBBDEPOTSTART
,
2969 &This
->extBigBlockDepotStart
);
2971 StorageUtl_ReadDWord(
2973 OFFSET_EXTBBDEPOTCOUNT
,
2974 &This
->extBigBlockDepotCount
);
2976 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2978 StorageUtl_ReadDWord(
2980 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2981 &(This
->bigBlockDepotStart
[index
]));
2985 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2987 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2988 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2991 * Right now, the code is making some assumptions about the size of the
2992 * blocks, just make sure they are what we're expecting.
2994 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2995 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2996 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2998 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2999 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3000 hr
= STG_E_INVALIDHEADER
;
3009 /******************************************************************************
3010 * StorageImpl_SaveFileHeader
3012 * This method will save to the file the header
3014 static void StorageImpl_SaveFileHeader(
3017 BYTE headerBigBlock
[HEADER_SIZE
];
3020 ULARGE_INTEGER offset
;
3021 DWORD bytes_read
, bytes_written
;
3022 DWORD major_version
, dirsectorcount
;
3025 * Get a pointer to the big block of data containing the header.
3027 offset
.u
.HighPart
= 0;
3028 offset
.u
.LowPart
= 0;
3029 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3030 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3031 hr
= STG_E_FILENOTFOUND
;
3033 if (This
->bigBlockSizeBits
== 0x9)
3035 else if (This
->bigBlockSizeBits
== 0xc)
3039 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3044 * If the block read failed, the file is probably new.
3049 * Initialize for all unknown fields.
3051 memset(headerBigBlock
, 0, HEADER_SIZE
);
3054 * Initialize the magic number.
3056 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3060 * Write the information to the header.
3062 StorageUtl_WriteWord(
3064 OFFSET_MINORVERSION
,
3067 StorageUtl_WriteWord(
3069 OFFSET_MAJORVERSION
,
3072 StorageUtl_WriteWord(
3074 OFFSET_BYTEORDERMARKER
,
3077 StorageUtl_WriteWord(
3079 OFFSET_BIGBLOCKSIZEBITS
,
3080 This
->bigBlockSizeBits
);
3082 StorageUtl_WriteWord(
3084 OFFSET_SMALLBLOCKSIZEBITS
,
3085 This
->smallBlockSizeBits
);
3087 if (major_version
>= 4)
3089 if (This
->rootBlockChain
)
3090 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3092 /* This file is being created, and it will start out with one block. */
3096 /* This field must be 0 in versions older than 4 */
3099 StorageUtl_WriteDWord(
3101 OFFSET_DIRSECTORCOUNT
,
3104 StorageUtl_WriteDWord(
3106 OFFSET_BBDEPOTCOUNT
,
3107 This
->bigBlockDepotCount
);
3109 StorageUtl_WriteDWord(
3111 OFFSET_ROOTSTARTBLOCK
,
3112 This
->rootStartBlock
);
3114 StorageUtl_WriteDWord(
3116 OFFSET_TRANSACTIONSIG
,
3117 This
->transactionSig
);
3119 StorageUtl_WriteDWord(
3121 OFFSET_SMALLBLOCKLIMIT
,
3122 This
->smallBlockLimit
);
3124 StorageUtl_WriteDWord(
3126 OFFSET_SBDEPOTSTART
,
3127 This
->smallBlockDepotStart
);
3129 StorageUtl_WriteDWord(
3131 OFFSET_SBDEPOTCOUNT
,
3132 This
->smallBlockDepotChain
?
3133 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3135 StorageUtl_WriteDWord(
3137 OFFSET_EXTBBDEPOTSTART
,
3138 This
->extBigBlockDepotStart
);
3140 StorageUtl_WriteDWord(
3142 OFFSET_EXTBBDEPOTCOUNT
,
3143 This
->extBigBlockDepotCount
);
3145 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3147 StorageUtl_WriteDWord(
3149 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3150 (This
->bigBlockDepotStart
[index
]));
3154 * Write the big block back to the file.
3156 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3160 /************************************************************************
3161 * StorageImpl implementation : DirEntry methods
3162 ***********************************************************************/
3164 /******************************************************************************
3165 * StorageImpl_ReadRawDirEntry
3167 * This method will read the raw data from a directory entry in the file.
3169 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3171 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3173 ULARGE_INTEGER offset
;
3177 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3179 hr
= BlockChainStream_ReadAt(
3180 This
->rootBlockChain
,
3186 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3187 return STG_E_READFAULT
;
3192 /******************************************************************************
3193 * StorageImpl_WriteRawDirEntry
3195 * This method will write the raw data from a directory entry in the file.
3197 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3199 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3201 ULARGE_INTEGER offset
;
3204 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3206 return BlockChainStream_WriteAt(
3207 This
->rootBlockChain
,
3214 /***************************************************************************
3218 * Mark a directory entry in the file as free.
3220 static HRESULT
StorageImpl_DestroyDirEntry(
3221 StorageBaseImpl
*base
,
3224 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3225 StorageImpl
*storage
= (StorageImpl
*)base
;
3227 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3229 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3232 /******************************************************************************
3235 * Update raw directory entry data from the fields in newData.
3237 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3239 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3241 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3244 buffer
+ OFFSET_PS_NAME
,
3246 DIRENTRY_NAME_BUFFER_LEN
);
3248 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3250 StorageUtl_WriteWord(
3252 OFFSET_PS_NAMELENGTH
,
3253 newData
->sizeOfNameString
);
3255 StorageUtl_WriteDWord(
3257 OFFSET_PS_LEFTCHILD
,
3258 newData
->leftChild
);
3260 StorageUtl_WriteDWord(
3262 OFFSET_PS_RIGHTCHILD
,
3263 newData
->rightChild
);
3265 StorageUtl_WriteDWord(
3268 newData
->dirRootEntry
);
3270 StorageUtl_WriteGUID(
3275 StorageUtl_WriteDWord(
3278 newData
->ctime
.dwLowDateTime
);
3280 StorageUtl_WriteDWord(
3282 OFFSET_PS_CTIMEHIGH
,
3283 newData
->ctime
.dwHighDateTime
);
3285 StorageUtl_WriteDWord(
3288 newData
->mtime
.dwLowDateTime
);
3290 StorageUtl_WriteDWord(
3292 OFFSET_PS_MTIMEHIGH
,
3293 newData
->ctime
.dwHighDateTime
);
3295 StorageUtl_WriteDWord(
3297 OFFSET_PS_STARTBLOCK
,
3298 newData
->startingBlock
);
3300 StorageUtl_WriteDWord(
3303 newData
->size
.u
.LowPart
);
3305 StorageUtl_WriteDWord(
3307 OFFSET_PS_SIZE_HIGH
,
3308 newData
->size
.u
.HighPart
);
3311 /***************************************************************************
3315 * Reserve a directory entry in the file and initialize it.
3317 static HRESULT
StorageImpl_CreateDirEntry(
3318 StorageBaseImpl
*base
,
3319 const DirEntry
*newData
,
3322 StorageImpl
*storage
= (StorageImpl
*)base
;
3323 ULONG currentEntryIndex
= 0;
3324 ULONG newEntryIndex
= DIRENTRY_NULL
;
3326 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3327 WORD sizeOfNameString
;
3331 hr
= StorageImpl_ReadRawDirEntry(storage
,
3337 StorageUtl_ReadWord(
3339 OFFSET_PS_NAMELENGTH
,
3342 if (sizeOfNameString
== 0)
3345 * The entry exists and is available, we found it.
3347 newEntryIndex
= currentEntryIndex
;
3353 * We exhausted the directory entries, we will create more space below
3355 newEntryIndex
= currentEntryIndex
;
3357 currentEntryIndex
++;
3359 } while (newEntryIndex
== DIRENTRY_NULL
);
3362 * grow the directory stream
3366 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3367 ULARGE_INTEGER newSize
;
3369 ULONG lastEntry
= 0;
3370 ULONG blockCount
= 0;
3373 * obtain the new count of blocks in the directory stream
3375 blockCount
= BlockChainStream_GetCount(
3376 storage
->rootBlockChain
)+1;
3379 * initialize the size used by the directory stream
3381 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3384 * add a block to the directory stream
3386 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3389 * memset the empty entry in order to initialize the unused newly
3392 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3397 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3400 entryIndex
= newEntryIndex
+ 1;
3401 entryIndex
< lastEntry
;
3404 StorageImpl_WriteRawDirEntry(
3410 StorageImpl_SaveFileHeader(storage
);
3413 UpdateRawDirEntry(currentData
, newData
);
3415 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3418 *index
= newEntryIndex
;
3423 /******************************************************************************
3424 * StorageImpl_ReadDirEntry
3426 * This method will read the specified directory entry.
3428 static HRESULT
StorageImpl_ReadDirEntry(
3433 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3436 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3438 if (SUCCEEDED(readRes
))
3440 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3443 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3444 DIRENTRY_NAME_BUFFER_LEN
);
3445 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3447 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3449 StorageUtl_ReadWord(
3451 OFFSET_PS_NAMELENGTH
,
3452 &buffer
->sizeOfNameString
);
3454 StorageUtl_ReadDWord(
3456 OFFSET_PS_LEFTCHILD
,
3457 &buffer
->leftChild
);
3459 StorageUtl_ReadDWord(
3461 OFFSET_PS_RIGHTCHILD
,
3462 &buffer
->rightChild
);
3464 StorageUtl_ReadDWord(
3467 &buffer
->dirRootEntry
);
3469 StorageUtl_ReadGUID(
3474 StorageUtl_ReadDWord(
3477 &buffer
->ctime
.dwLowDateTime
);
3479 StorageUtl_ReadDWord(
3481 OFFSET_PS_CTIMEHIGH
,
3482 &buffer
->ctime
.dwHighDateTime
);
3484 StorageUtl_ReadDWord(
3487 &buffer
->mtime
.dwLowDateTime
);
3489 StorageUtl_ReadDWord(
3491 OFFSET_PS_MTIMEHIGH
,
3492 &buffer
->mtime
.dwHighDateTime
);
3494 StorageUtl_ReadDWord(
3496 OFFSET_PS_STARTBLOCK
,
3497 &buffer
->startingBlock
);
3499 StorageUtl_ReadDWord(
3502 &buffer
->size
.u
.LowPart
);
3504 if (This
->bigBlockSize
< 4096)
3506 /* Version 3 files may have junk in the high part of size. */
3507 buffer
->size
.u
.HighPart
= 0;
3511 StorageUtl_ReadDWord(
3513 OFFSET_PS_SIZE_HIGH
,
3514 &buffer
->size
.u
.HighPart
);
3521 /*********************************************************************
3522 * Write the specified directory entry to the file
3524 static HRESULT
StorageImpl_WriteDirEntry(
3527 const DirEntry
* buffer
)
3529 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3531 UpdateRawDirEntry(currentEntry
, buffer
);
3533 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3537 /************************************************************************
3538 * StorageImpl implementation : Block methods
3539 ***********************************************************************/
3541 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3543 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3546 static HRESULT
StorageImpl_ReadBigBlock(
3552 ULARGE_INTEGER ulOffset
;
3556 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3558 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3560 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3562 /* File ends during this block; fill the rest with 0's. */
3563 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3566 if (out_read
) *out_read
= read
;
3571 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3577 ULARGE_INTEGER ulOffset
;
3581 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3582 ulOffset
.QuadPart
+= offset
;
3584 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3585 *value
= lendian32toh(tmp
);
3586 return (read
== sizeof(DWORD
));
3589 static BOOL
StorageImpl_WriteBigBlock(
3594 ULARGE_INTEGER ulOffset
;
3597 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3599 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3600 return (wrote
== This
->bigBlockSize
);
3603 static BOOL
StorageImpl_WriteDWordToBigBlock(
3609 ULARGE_INTEGER ulOffset
;
3612 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3613 ulOffset
.QuadPart
+= offset
;
3615 value
= htole32(value
);
3616 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3617 return (wrote
== sizeof(DWORD
));
3620 /******************************************************************************
3621 * Storage32Impl_SmallBlocksToBigBlocks
3623 * This method will convert a small block chain to a big block chain.
3624 * The small block chain will be destroyed.
3626 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3628 SmallBlockChainStream
** ppsbChain
)
3630 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3631 ULARGE_INTEGER size
, offset
;
3632 ULONG cbRead
, cbWritten
;
3633 ULARGE_INTEGER cbTotalRead
;
3634 DirRef streamEntryRef
;
3635 HRESULT resWrite
= S_OK
;
3637 DirEntry streamEntry
;
3639 BlockChainStream
*bbTempChain
= NULL
;
3640 BlockChainStream
*bigBlockChain
= NULL
;
3643 * Create a temporary big block chain that doesn't have
3644 * an associated directory entry. This temporary chain will be
3645 * used to copy data from small blocks to big blocks.
3647 bbTempChain
= BlockChainStream_Construct(This
,
3650 if(!bbTempChain
) return NULL
;
3652 * Grow the big block chain.
3654 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3655 BlockChainStream_SetSize(bbTempChain
, size
);
3658 * Copy the contents of the small block chain to the big block chain
3659 * by small block size increments.
3661 offset
.u
.LowPart
= 0;
3662 offset
.u
.HighPart
= 0;
3663 cbTotalRead
.QuadPart
= 0;
3665 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3668 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3670 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3673 if (FAILED(resRead
))
3678 cbTotalRead
.QuadPart
+= cbRead
;
3680 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3686 if (FAILED(resWrite
))
3689 offset
.u
.LowPart
+= cbRead
;
3693 resRead
= STG_E_READFAULT
;
3696 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3697 HeapFree(GetProcessHeap(),0,buffer
);
3699 size
.u
.HighPart
= 0;
3702 if (FAILED(resRead
) || FAILED(resWrite
))
3704 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3705 BlockChainStream_SetSize(bbTempChain
, size
);
3706 BlockChainStream_Destroy(bbTempChain
);
3711 * Destroy the small block chain.
3713 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3714 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3715 SmallBlockChainStream_Destroy(*ppsbChain
);
3719 * Change the directory entry. This chain is now a big block chain
3720 * and it doesn't reside in the small blocks chain anymore.
3722 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3724 streamEntry
.startingBlock
= bbHeadOfChain
;
3726 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3729 * Destroy the temporary entryless big block chain.
3730 * Create a new big block chain associated with this entry.
3732 BlockChainStream_Destroy(bbTempChain
);
3733 bigBlockChain
= BlockChainStream_Construct(This
,
3737 return bigBlockChain
;
3740 /******************************************************************************
3741 * Storage32Impl_BigBlocksToSmallBlocks
3743 * This method will convert a big block chain to a small block chain.
3744 * The big block chain will be destroyed on success.
3746 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3748 BlockChainStream
** ppbbChain
,
3749 ULARGE_INTEGER newSize
)
3751 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3752 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3753 DirRef streamEntryRef
;
3754 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3755 DirEntry streamEntry
;
3757 SmallBlockChainStream
* sbTempChain
;
3759 TRACE("%p %p\n", This
, ppbbChain
);
3761 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3767 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3768 size
= BlockChainStream_GetSize(*ppbbChain
);
3769 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3771 offset
.u
.HighPart
= 0;
3772 offset
.u
.LowPart
= 0;
3773 cbTotalRead
.QuadPart
= 0;
3774 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3775 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3777 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3778 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3786 cbTotalRead
.QuadPart
+= cbRead
;
3788 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3789 cbRead
, buffer
, &cbWritten
);
3791 if(FAILED(resWrite
))
3794 offset
.u
.LowPart
+= cbRead
;
3798 resRead
= STG_E_READFAULT
;
3802 HeapFree(GetProcessHeap(), 0, buffer
);
3804 size
.u
.HighPart
= 0;
3807 if(FAILED(resRead
) || FAILED(resWrite
))
3809 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3810 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3811 SmallBlockChainStream_Destroy(sbTempChain
);
3815 /* destroy the original big block chain */
3816 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3817 BlockChainStream_SetSize(*ppbbChain
, size
);
3818 BlockChainStream_Destroy(*ppbbChain
);
3821 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3822 streamEntry
.startingBlock
= sbHeadOfChain
;
3823 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3825 SmallBlockChainStream_Destroy(sbTempChain
);
3826 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3829 /******************************************************************************
3830 * Storage32Impl_AddBlockDepot
3832 * This will create a depot block, essentially it is a block initialized
3835 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3837 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3838 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3839 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3840 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3843 * Initialize blocks as free
3845 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3847 /* Reserve the range lock sector */
3848 if (depotIndex
== rangeLockDepot
)
3850 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3853 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3856 /******************************************************************************
3857 * Storage32Impl_GetExtDepotBlock
3859 * Returns the index of the block that corresponds to the specified depot
3860 * index. This method is only for depot indexes equal or greater than
3861 * COUNT_BBDEPOTINHEADER.
3863 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3865 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3866 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3867 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3868 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3869 ULONG blockIndex
= BLOCK_UNUSED
;
3870 ULONG extBlockIndex
;
3871 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3872 int index
, num_blocks
;
3874 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3876 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3877 return BLOCK_UNUSED
;
3879 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3881 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3883 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3885 num_blocks
= This
->bigBlockSize
/ 4;
3887 for (index
= 0; index
< num_blocks
; index
++)
3889 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3890 This
->extBlockDepotCached
[index
] = blockIndex
;
3893 This
->indexExtBlockDepotCached
= extBlockCount
;
3896 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3901 /******************************************************************************
3902 * Storage32Impl_SetExtDepotBlock
3904 * Associates the specified block index to the specified depot index.
3905 * This method is only for depot indexes equal or greater than
3906 * COUNT_BBDEPOTINHEADER.
3908 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3910 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3911 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3912 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3913 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3914 ULONG extBlockIndex
;
3916 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3918 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3920 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3922 if (extBlockIndex
!= BLOCK_UNUSED
)
3924 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3925 extBlockOffset
* sizeof(ULONG
),
3929 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3931 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3935 /******************************************************************************
3936 * Storage32Impl_AddExtBlockDepot
3938 * Creates an extended depot block.
3940 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3942 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3943 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3944 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3945 ULONG index
= BLOCK_UNUSED
;
3946 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3947 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3948 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3950 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3951 blocksPerDepotBlock
;
3953 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3956 * The first extended block.
3958 This
->extBigBlockDepotStart
= index
;
3963 * Find the last existing extended block.
3965 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3968 * Add the new extended block to the chain.
3970 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3975 * Initialize this block.
3977 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3978 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3980 /* Add the block to our cache. */
3981 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3983 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3984 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3986 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3987 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3989 This
->extBigBlockDepotLocations
= new_cache
;
3990 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3992 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3997 /************************************************************************
3998 * StorageImpl_GetNextBlockInChain
4000 * This method will retrieve the block index of the next big block in
4003 * Params: This - Pointer to the Storage object.
4004 * blockIndex - Index of the block to retrieve the chain
4006 * nextBlockIndex - receives the return value.
4008 * Returns: This method returns the index of the next block in the chain.
4009 * It will return the constants:
4010 * BLOCK_SPECIAL - If the block given was not part of a
4012 * BLOCK_END_OF_CHAIN - If the block given was the last in
4014 * BLOCK_UNUSED - If the block given was not past of a chain
4016 * BLOCK_EXTBBDEPOT - This block is part of the extended
4019 * See Windows documentation for more details on IStorage methods.
4021 static HRESULT
StorageImpl_GetNextBlockInChain(
4024 ULONG
* nextBlockIndex
)
4026 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4027 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4028 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4029 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4031 ULONG depotBlockIndexPos
;
4032 int index
, num_blocks
;
4034 *nextBlockIndex
= BLOCK_SPECIAL
;
4036 if(depotBlockCount
>= This
->bigBlockDepotCount
)
4038 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
4039 This
->bigBlockDepotCount
);
4040 return STG_E_READFAULT
;
4044 * Cache the currently accessed depot block.
4046 if (depotBlockCount
!= This
->indexBlockDepotCached
)
4048 This
->indexBlockDepotCached
= depotBlockCount
;
4050 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4052 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4057 * We have to look in the extended depot.
4059 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4062 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4065 return STG_E_READFAULT
;
4067 num_blocks
= This
->bigBlockSize
/ 4;
4069 for (index
= 0; index
< num_blocks
; index
++)
4071 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4072 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4076 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4081 /******************************************************************************
4082 * Storage32Impl_GetNextExtendedBlock
4084 * Given an extended block this method will return the next extended block.
4087 * The last ULONG of an extended block is the block index of the next
4088 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4092 * - The index of the next extended block
4093 * - BLOCK_UNUSED: there is no next extended block.
4094 * - Any other return values denotes failure.
4096 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4098 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4099 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4101 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4104 return nextBlockIndex
;
4107 /******************************************************************************
4108 * StorageImpl_SetNextBlockInChain
4110 * This method will write the index of the specified block's next block
4111 * in the big block depot.
4113 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4116 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4117 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4118 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4121 static void StorageImpl_SetNextBlockInChain(
4126 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4127 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4128 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4129 ULONG depotBlockIndexPos
;
4131 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4132 assert(blockIndex
!= nextBlock
);
4134 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4135 /* This should never happen (storage file format spec forbids it), but
4136 * older versions of Wine may have generated broken files. We don't want to
4137 * assert and potentially lose data, but we do want to know if this ever
4138 * happens in a newly-created file. */
4139 ERR("Using range lock page\n");
4141 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4143 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4148 * We have to look in the extended depot.
4150 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4153 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4156 * Update the cached block depot, if necessary.
4158 if (depotBlockCount
== This
->indexBlockDepotCached
)
4160 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4164 /******************************************************************************
4165 * StorageImpl_GetNextFreeBigBlock
4167 * Returns the index of the next free big block.
4168 * If the big block depot is filled, this method will enlarge it.
4171 static ULONG
StorageImpl_GetNextFreeBigBlock(
4174 ULONG depotBlockIndexPos
;
4175 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4176 ULONG depotBlockOffset
;
4177 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4178 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4180 ULONG freeBlock
= BLOCK_UNUSED
;
4182 ULARGE_INTEGER neededSize
;
4185 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4186 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4189 * Scan the entire big block depot until we find a block marked free
4191 while (nextBlockIndex
!= BLOCK_UNUSED
)
4193 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4195 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4198 * Grow the primary depot.
4200 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4202 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4205 * Add a block depot.
4207 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4208 This
->bigBlockDepotCount
++;
4209 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4212 * Flag it as a block depot.
4214 StorageImpl_SetNextBlockInChain(This
,
4218 /* Save new header information.
4220 StorageImpl_SaveFileHeader(This
);
4225 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4227 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4230 * Grow the extended depot.
4232 ULONG extIndex
= BLOCK_UNUSED
;
4233 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4234 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4236 if (extBlockOffset
== 0)
4238 /* We need an extended block.
4240 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4241 This
->extBigBlockDepotCount
++;
4242 depotBlockIndexPos
= extIndex
+ 1;
4245 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4248 * Add a block depot and mark it in the extended block.
4250 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4251 This
->bigBlockDepotCount
++;
4252 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4254 /* Flag the block depot.
4256 StorageImpl_SetNextBlockInChain(This
,
4260 /* If necessary, flag the extended depot block.
4262 if (extIndex
!= BLOCK_UNUSED
)
4263 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4265 /* Save header information.
4267 StorageImpl_SaveFileHeader(This
);
4271 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4275 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4276 ( nextBlockIndex
!= BLOCK_UNUSED
))
4278 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4280 if (nextBlockIndex
== BLOCK_UNUSED
)
4282 freeBlock
= (depotIndex
* blocksPerDepot
) +
4283 (depotBlockOffset
/sizeof(ULONG
));
4286 depotBlockOffset
+= sizeof(ULONG
);
4291 depotBlockOffset
= 0;
4295 * make sure that the block physically exists before using it
4297 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
4299 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4301 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4302 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4304 This
->prevFreeBlock
= freeBlock
;
4309 /******************************************************************************
4310 * StorageImpl_FreeBigBlock
4312 * This method will flag the specified block as free in the big block depot.
4314 static void StorageImpl_FreeBigBlock(
4318 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4320 if (blockIndex
< This
->prevFreeBlock
)
4321 This
->prevFreeBlock
= blockIndex
;
4325 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4326 DirRef index
, const DirEntry
*data
)
4328 StorageImpl
*This
= (StorageImpl
*)base
;
4329 return StorageImpl_WriteDirEntry(This
, index
, data
);
4332 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4333 DirRef index
, DirEntry
*data
)
4335 StorageImpl
*This
= (StorageImpl
*)base
;
4336 return StorageImpl_ReadDirEntry(This
, index
, data
);
4339 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4343 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4345 if (!This
->blockChainCache
[i
])
4347 return &This
->blockChainCache
[i
];
4351 i
= This
->blockChainToEvict
;
4353 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4354 This
->blockChainCache
[i
] = NULL
;
4356 This
->blockChainToEvict
++;
4357 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4358 This
->blockChainToEvict
= 0;
4360 return &This
->blockChainCache
[i
];
4363 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4366 int i
, free_index
=-1;
4368 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4370 if (!This
->blockChainCache
[i
])
4372 if (free_index
== -1) free_index
= i
;
4374 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4376 return &This
->blockChainCache
[i
];
4380 if (free_index
== -1)
4382 free_index
= This
->blockChainToEvict
;
4384 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4385 This
->blockChainCache
[free_index
] = NULL
;
4387 This
->blockChainToEvict
++;
4388 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4389 This
->blockChainToEvict
= 0;
4392 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4393 return &This
->blockChainCache
[free_index
];
4396 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4400 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4402 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4404 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4405 This
->blockChainCache
[i
] = NULL
;
4411 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4412 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4414 StorageImpl
*This
= (StorageImpl
*)base
;
4419 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4420 if (FAILED(hr
)) return hr
;
4422 if (data
.size
.QuadPart
== 0)
4428 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4430 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4437 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4439 SmallBlockChainStream
*stream
;
4441 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4442 if (!stream
) return E_OUTOFMEMORY
;
4444 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4446 SmallBlockChainStream_Destroy(stream
);
4452 BlockChainStream
*stream
= NULL
;
4454 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4455 if (!stream
) return E_OUTOFMEMORY
;
4457 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4463 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4464 ULARGE_INTEGER newsize
)
4466 StorageImpl
*This
= (StorageImpl
*)base
;
4469 SmallBlockChainStream
*smallblock
=NULL
;
4470 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4472 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4473 if (FAILED(hr
)) return hr
;
4475 /* In simple mode keep the stream size above the small block limit */
4476 if (This
->base
.openFlags
& STGM_SIMPLE
)
4477 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4479 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4482 /* Create a block chain object of the appropriate type */
4483 if (data
.size
.QuadPart
== 0)
4485 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4487 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4488 if (!smallblock
) return E_OUTOFMEMORY
;
4492 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4493 bigblock
= *pbigblock
;
4494 if (!bigblock
) return E_OUTOFMEMORY
;
4497 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4499 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4500 if (!smallblock
) return E_OUTOFMEMORY
;
4504 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4505 bigblock
= *pbigblock
;
4506 if (!bigblock
) return E_OUTOFMEMORY
;
4509 /* Change the block chain type if necessary. */
4510 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4512 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4515 SmallBlockChainStream_Destroy(smallblock
);
4519 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4520 *pbigblock
= bigblock
;
4522 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4524 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4529 /* Set the size of the block chain. */
4532 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4533 SmallBlockChainStream_Destroy(smallblock
);
4537 BlockChainStream_SetSize(bigblock
, newsize
);
4540 /* Set the size in the directory entry. */
4541 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4544 data
.size
= newsize
;
4546 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4551 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4552 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4554 StorageImpl
*This
= (StorageImpl
*)base
;
4557 ULARGE_INTEGER newSize
;
4559 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4560 if (FAILED(hr
)) return hr
;
4562 /* Grow the stream if necessary */
4563 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4565 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4567 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4571 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4572 if (FAILED(hr
)) return hr
;
4575 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4577 SmallBlockChainStream
*stream
;
4579 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4580 if (!stream
) return E_OUTOFMEMORY
;
4582 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4584 SmallBlockChainStream_Destroy(stream
);
4590 BlockChainStream
*stream
;
4592 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4593 if (!stream
) return E_OUTOFMEMORY
;
4595 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4599 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4602 StorageImpl
*This
= (StorageImpl
*)base
;
4603 DirEntry dst_data
, src_data
;
4606 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4609 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4613 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4614 dst_data
.startingBlock
= src_data
.startingBlock
;
4615 dst_data
.size
= src_data
.size
;
4617 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4623 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4626 DirEntry currentEntry
;
4627 DirRef currentEntryRef
;
4628 BlockChainStream
*blockChainStream
;
4632 ULARGE_INTEGER size
;
4633 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4635 /* Discard any existing data. */
4637 ILockBytes_SetSize(This
->lockBytes
, size
);
4640 * Initialize all header variables:
4641 * - The big block depot consists of one block and it is at block 0
4642 * - The directory table starts at block 1
4643 * - There is no small block depot
4645 memset( This
->bigBlockDepotStart
,
4647 sizeof(This
->bigBlockDepotStart
));
4649 This
->bigBlockDepotCount
= 1;
4650 This
->bigBlockDepotStart
[0] = 0;
4651 This
->rootStartBlock
= 1;
4652 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4653 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4654 if (This
->bigBlockSize
== 4096)
4655 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4657 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4658 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4659 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4660 This
->extBigBlockDepotCount
= 0;
4662 StorageImpl_SaveFileHeader(This
);
4665 * Add one block for the big block depot and one block for the directory table
4667 size
.u
.HighPart
= 0;
4668 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4669 ILockBytes_SetSize(This
->lockBytes
, size
);
4672 * Initialize the big block depot
4674 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4675 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4676 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4677 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4682 * Load the header for the file.
4684 hr
= StorageImpl_LoadFileHeader(This
);
4693 * There is no block depot cached yet.
4695 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4696 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4699 * Start searching for free blocks with block 0.
4701 This
->prevFreeBlock
= 0;
4703 This
->firstFreeSmallBlock
= 0;
4705 /* Read the extended big block depot locations. */
4706 if (This
->extBigBlockDepotCount
!= 0)
4708 ULONG current_block
= This
->extBigBlockDepotStart
;
4709 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4712 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4713 if (!This
->extBigBlockDepotLocations
)
4715 return E_OUTOFMEMORY
;
4718 This
->extBigBlockDepotLocationsSize
= cache_size
;
4720 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4722 if (current_block
== BLOCK_END_OF_CHAIN
)
4724 WARN("File has too few extended big block depot blocks.\n");
4725 return STG_E_DOCFILECORRUPT
;
4727 This
->extBigBlockDepotLocations
[i
] = current_block
;
4728 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4733 This
->extBigBlockDepotLocations
= NULL
;
4734 This
->extBigBlockDepotLocationsSize
= 0;
4738 * Create the block chain abstractions.
4740 if(!(blockChainStream
=
4741 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4743 return STG_E_READFAULT
;
4746 BlockChainStream_Destroy(This
->rootBlockChain
);
4747 This
->rootBlockChain
= blockChainStream
;
4749 if(!(blockChainStream
=
4750 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4753 return STG_E_READFAULT
;
4756 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4757 This
->smallBlockDepotChain
= blockChainStream
;
4760 * Write the root storage entry (memory only)
4764 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4767 * Initialize the directory table
4769 memset(&rootEntry
, 0, sizeof(rootEntry
));
4770 lstrcpyW(rootEntry
.name
, rootentryW
);
4771 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
4772 rootEntry
.stgType
= STGTY_ROOT
;
4773 rootEntry
.leftChild
= DIRENTRY_NULL
;
4774 rootEntry
.rightChild
= DIRENTRY_NULL
;
4775 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4776 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4777 rootEntry
.size
.u
.HighPart
= 0;
4778 rootEntry
.size
.u
.LowPart
= 0;
4780 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4784 * Find the ID of the root storage.
4786 currentEntryRef
= 0;
4790 hr
= StorageImpl_ReadDirEntry(
4797 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4798 (currentEntry
.stgType
== STGTY_ROOT
) )
4800 This
->base
.storageDirEntry
= currentEntryRef
;
4806 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4810 return STG_E_READFAULT
;
4814 * Create the block chain abstraction for the small block root chain.
4816 if(!(blockChainStream
=
4817 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4819 return STG_E_READFAULT
;
4822 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4823 This
->smallBlockRootChain
= blockChainStream
;
4828 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4830 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4831 This
->blockChainCache
[i
] = NULL
;
4838 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4839 ULONG
* result
, BOOL refresh
)
4841 StorageImpl
*This
= (StorageImpl
*)base
;
4843 DWORD oldTransactionSig
= This
->transactionSig
;
4847 ULARGE_INTEGER offset
;
4851 offset
.u
.HighPart
= 0;
4852 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4853 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4857 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4859 if (oldTransactionSig
!= This
->transactionSig
)
4861 /* Someone else wrote to this, so toss all cached information. */
4862 TRACE("signature changed\n");
4864 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4868 This
->transactionSig
= oldTransactionSig
;
4872 *result
= This
->transactionSig
;
4877 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4880 StorageImpl
*This
= (StorageImpl
*)base
;
4882 This
->transactionSig
= value
;
4883 StorageImpl_SaveFileHeader(This
);
4888 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4889 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4891 if ((dwLockType
& This
->locks_supported
) == 0)
4893 if (supported
) *supported
= FALSE
;
4897 if (supported
) *supported
= TRUE
;
4898 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4901 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4902 ULARGE_INTEGER cb
, DWORD dwLockType
)
4904 if ((dwLockType
& This
->locks_supported
) == 0)
4907 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4910 /* Internal function */
4911 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4912 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4916 DWORD start_time
= GetTickCount();
4917 DWORD last_sanity_check
= start_time
;
4918 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4920 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4921 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4925 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4927 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4929 DWORD current_time
= GetTickCount();
4930 if (current_time
- start_time
>= 20000)
4935 if (current_time
- last_sanity_check
>= 500)
4937 /* Any storage implementation with the file open in a
4938 * shared mode should not lock these bytes for writing. However,
4939 * some programs (LibreOffice Writer) will keep ALL bytes locked
4940 * when opening in exclusive mode. We can use a read lock to
4941 * detect this case early, and not hang a full 20 seconds.
4943 * This can collide with another attempt to open the file in
4944 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4945 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4946 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4950 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4951 hr
= STG_E_ACCESSDENIED
;
4954 last_sanity_check
= current_time
;
4957 if (delay
< 150) delay
++;
4959 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4964 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4966 StorageImpl
*This
= (StorageImpl
*)base
;
4968 ULARGE_INTEGER offset
, cb
;
4972 /* Synchronous grab of second priority range, the commit lock, and the
4973 * lock-checking lock. */
4974 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4975 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4979 offset
.QuadPart
= RANGELOCK_COMMIT
;
4983 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4988 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4990 StorageImpl
*This
= (StorageImpl
*)base
;
4992 ULARGE_INTEGER offset
, cb
;
4996 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4997 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
5001 offset
.QuadPart
= RANGELOCK_COMMIT
;
5005 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5010 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5012 StorageImpl
*This
= (StorageImpl
*) iface
;
5016 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
5018 *result
= statstg
.pwcsName
;
5023 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
5024 ULONG end
, HRESULT fail_hr
)
5027 ULARGE_INTEGER offset
, cb
;
5029 offset
.QuadPart
= start
;
5030 cb
.QuadPart
= 1 + end
- start
;
5032 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5033 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5041 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
5045 ULARGE_INTEGER offset
, cb
;
5049 for (i
=start
; i
<=end
; i
++)
5051 offset
.QuadPart
= i
;
5052 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5053 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
5059 for (j
= 0; j
< ARRAY_SIZE(This
->locked_bytes
); j
++)
5061 if (This
->locked_bytes
[j
] == 0)
5063 This
->locked_bytes
[j
] = i
;
5072 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5075 ULARGE_INTEGER offset
;
5077 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5080 if (openFlags
& STGM_NOSNAPSHOT
)
5082 /* STGM_NOSNAPSHOT implies deny write */
5083 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5084 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5087 /* Wrap all other locking inside a single lock so we can check ranges safely */
5088 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5090 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5092 /* If the ILockBytes doesn't support locking that's ok. */
5093 if (!supported
) return S_OK
;
5094 else if (FAILED(hr
)) return hr
;
5098 /* First check for any conflicting locks. */
5099 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5100 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5102 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5103 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5105 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5106 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5108 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5109 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5111 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5112 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5114 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5116 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5119 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5122 /* Then grab our locks. */
5123 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5125 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5127 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5130 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5131 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5133 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5134 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5136 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5137 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5139 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5140 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5142 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5143 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5145 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5147 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5152 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5154 StorageImpl
*This
= (StorageImpl
*)storage
;
5157 TRACE("(%p)\n", This
);
5159 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5162 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5165 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5167 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5168 if (This
->blockChainCache
[i
])
5169 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5172 hr
= ILockBytes_Flush(This
->lockBytes
);
5177 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5179 StorageImpl
*This
= (StorageImpl
*) iface
;
5181 StorageBaseImpl_DeleteAll(&This
->base
);
5183 This
->base
.reverted
= TRUE
;
5186 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5188 StorageImpl
*This
= (StorageImpl
*) iface
;
5190 TRACE("(%p)\n", This
);
5192 StorageImpl_Flush(iface
);
5194 StorageImpl_Invalidate(iface
);
5196 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5198 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5199 BlockChainStream_Destroy(This
->rootBlockChain
);
5200 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5202 for (i
= 0; i
< BLOCKCHAIN_CACHE_SIZE
; i
++)
5203 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5205 for (i
= 0; i
< ARRAY_SIZE(This
->locked_bytes
); i
++)
5207 ULARGE_INTEGER offset
, cb
;
5209 if (This
->locked_bytes
[i
] != 0)
5211 offset
.QuadPart
= This
->locked_bytes
[i
];
5212 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5216 if (This
->lockBytes
)
5217 ILockBytes_Release(This
->lockBytes
);
5218 HeapFree(GetProcessHeap(), 0, This
);
5222 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5224 StorageImpl_Destroy
,
5225 StorageImpl_Invalidate
,
5227 StorageImpl_GetFilename
,
5228 StorageImpl_CreateDirEntry
,
5229 StorageImpl_BaseWriteDirEntry
,
5230 StorageImpl_BaseReadDirEntry
,
5231 StorageImpl_DestroyDirEntry
,
5232 StorageImpl_StreamReadAt
,
5233 StorageImpl_StreamWriteAt
,
5234 StorageImpl_StreamSetSize
,
5235 StorageImpl_StreamLink
,
5236 StorageImpl_GetTransactionSig
,
5237 StorageImpl_SetTransactionSig
,
5238 StorageImpl_LockTransaction
,
5239 StorageImpl_UnlockTransaction
5244 * Virtual function table for the IStorageBaseImpl class.
5246 static const IStorageVtbl StorageImpl_Vtbl
=
5248 StorageBaseImpl_QueryInterface
,
5249 StorageBaseImpl_AddRef
,
5250 StorageBaseImpl_Release
,
5251 StorageBaseImpl_CreateStream
,
5252 StorageBaseImpl_OpenStream
,
5253 StorageBaseImpl_CreateStorage
,
5254 StorageBaseImpl_OpenStorage
,
5255 StorageBaseImpl_CopyTo
,
5256 StorageBaseImpl_MoveElementTo
,
5257 StorageBaseImpl_Commit
,
5258 StorageBaseImpl_Revert
,
5259 StorageBaseImpl_EnumElements
,
5260 StorageBaseImpl_DestroyElement
,
5261 StorageBaseImpl_RenameElement
,
5262 StorageBaseImpl_SetElementTimes
,
5263 StorageBaseImpl_SetClass
,
5264 StorageBaseImpl_SetStateBits
,
5265 StorageBaseImpl_Stat
5268 static HRESULT
StorageImpl_Construct(
5276 StorageImpl
** result
)
5282 if ( FAILED( validateSTGM(openFlags
) ))
5283 return STG_E_INVALIDFLAG
;
5285 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5287 return E_OUTOFMEMORY
;
5289 memset(This
, 0, sizeof(StorageImpl
));
5291 list_init(&This
->base
.strmHead
);
5293 list_init(&This
->base
.storageHead
);
5295 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5296 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5297 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5298 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5299 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5301 This
->base
.create
= create
;
5303 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5304 This
->base
.lockingrole
= SWMR_Writer
;
5305 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5306 This
->base
.lockingrole
= SWMR_Reader
;
5308 This
->base
.lockingrole
= SWMR_None
;
5310 This
->base
.reverted
= FALSE
;
5313 * Initialize the big block cache.
5315 This
->bigBlockSize
= sector_size
;
5316 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5318 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5321 This
->lockBytes
= pLkbyt
;
5322 ILockBytes_AddRef(pLkbyt
);
5326 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5330 This
->locks_supported
= stat
.grfLocksSupported
;
5332 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5333 This
->locks_supported
&= ~WINE_LOCK_READ
;
5335 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5339 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5343 IStorage_Release(&This
->base
.IStorage_iface
);
5348 StorageImpl_Flush(&This
->base
);
5356 /************************************************************************
5357 * StorageInternalImpl implementation
5358 ***********************************************************************/
5360 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5362 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5364 if (!This
->base
.reverted
)
5366 TRACE("Storage invalidated (stg=%p)\n", This
);
5368 This
->base
.reverted
= TRUE
;
5370 This
->parentStorage
= NULL
;
5372 StorageBaseImpl_DeleteAll(&This
->base
);
5374 list_remove(&This
->ParentListEntry
);
5378 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5380 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5382 StorageInternalImpl_Invalidate(&This
->base
);
5384 HeapFree(GetProcessHeap(), 0, This
);
5387 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5389 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5391 return StorageBaseImpl_Flush(This
->parentStorage
);
5394 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5396 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5398 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5401 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5402 const DirEntry
*newData
, DirRef
*index
)
5404 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5406 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5410 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5411 DirRef index
, const DirEntry
*data
)
5413 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5415 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5419 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5420 DirRef index
, DirEntry
*data
)
5422 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5424 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5428 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5431 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5433 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5437 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5438 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5440 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5442 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5443 index
, offset
, size
, buffer
, bytesRead
);
5446 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5447 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5449 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5451 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5452 index
, offset
, size
, buffer
, bytesWritten
);
5455 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5456 DirRef index
, ULARGE_INTEGER newsize
)
5458 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5460 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5464 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5465 DirRef dst
, DirRef src
)
5467 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5469 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5473 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5474 ULONG
* result
, BOOL refresh
)
5479 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5485 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5490 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5495 /******************************************************************************
5497 ** StorageInternalImpl_Commit
5500 static HRESULT WINAPI
StorageInternalImpl_Commit(
5502 DWORD grfCommitFlags
) /* [in] */
5504 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5505 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5506 return StorageBaseImpl_Flush(This
);
5509 /******************************************************************************
5511 ** StorageInternalImpl_Revert
5514 static HRESULT WINAPI
StorageInternalImpl_Revert(
5517 FIXME("(%p): stub\n", iface
);
5522 * Virtual function table for the StorageInternalImpl class.
5524 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5526 StorageBaseImpl_QueryInterface
,
5527 StorageBaseImpl_AddRef
,
5528 StorageBaseImpl_Release
,
5529 StorageBaseImpl_CreateStream
,
5530 StorageBaseImpl_OpenStream
,
5531 StorageBaseImpl_CreateStorage
,
5532 StorageBaseImpl_OpenStorage
,
5533 StorageBaseImpl_CopyTo
,
5534 StorageBaseImpl_MoveElementTo
,
5535 StorageInternalImpl_Commit
,
5536 StorageInternalImpl_Revert
,
5537 StorageBaseImpl_EnumElements
,
5538 StorageBaseImpl_DestroyElement
,
5539 StorageBaseImpl_RenameElement
,
5540 StorageBaseImpl_SetElementTimes
,
5541 StorageBaseImpl_SetClass
,
5542 StorageBaseImpl_SetStateBits
,
5543 StorageBaseImpl_Stat
5546 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5548 StorageInternalImpl_Destroy
,
5549 StorageInternalImpl_Invalidate
,
5550 StorageInternalImpl_Flush
,
5551 StorageInternalImpl_GetFilename
,
5552 StorageInternalImpl_CreateDirEntry
,
5553 StorageInternalImpl_WriteDirEntry
,
5554 StorageInternalImpl_ReadDirEntry
,
5555 StorageInternalImpl_DestroyDirEntry
,
5556 StorageInternalImpl_StreamReadAt
,
5557 StorageInternalImpl_StreamWriteAt
,
5558 StorageInternalImpl_StreamSetSize
,
5559 StorageInternalImpl_StreamLink
,
5560 StorageInternalImpl_GetTransactionSig
,
5561 StorageInternalImpl_SetTransactionSig
,
5562 StorageInternalImpl_LockTransaction
,
5563 StorageInternalImpl_UnlockTransaction
5566 static StorageInternalImpl
* StorageInternalImpl_Construct(
5567 StorageBaseImpl
* parentStorage
,
5569 DirRef storageDirEntry
)
5571 StorageInternalImpl
* newStorage
;
5573 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5577 list_init(&newStorage
->base
.strmHead
);
5579 list_init(&newStorage
->base
.storageHead
);
5582 * Initialize the virtual function table.
5584 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5585 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5586 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5587 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5589 newStorage
->base
.reverted
= FALSE
;
5591 newStorage
->base
.ref
= 1;
5593 newStorage
->parentStorage
= parentStorage
;
5596 * Keep a reference to the directory entry of this storage
5598 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5600 newStorage
->base
.create
= FALSE
;
5609 /************************************************************************
5610 * TransactedSnapshotImpl implementation
5611 ***********************************************************************/
5613 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5615 DirRef result
=This
->firstFreeEntry
;
5617 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5620 if (result
== This
->entries_size
)
5622 ULONG new_size
= This
->entries_size
* 2;
5623 TransactedDirEntry
*new_entries
;
5625 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5626 if (!new_entries
) return DIRENTRY_NULL
;
5628 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5629 HeapFree(GetProcessHeap(), 0, This
->entries
);
5631 This
->entries
= new_entries
;
5632 This
->entries_size
= new_size
;
5635 This
->entries
[result
].inuse
= TRUE
;
5637 This
->firstFreeEntry
= result
+1;
5642 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5643 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5645 DirRef stubEntryRef
;
5646 TransactedDirEntry
*entry
;
5648 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5650 if (stubEntryRef
!= DIRENTRY_NULL
)
5652 entry
= &This
->entries
[stubEntryRef
];
5654 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5656 entry
->read
= FALSE
;
5659 return stubEntryRef
;
5662 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5663 TransactedSnapshotImpl
*This
, DirRef entry
)
5668 if (!This
->entries
[entry
].read
)
5670 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5671 This
->entries
[entry
].transactedParentEntry
,
5674 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5676 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5678 if (data
.leftChild
== DIRENTRY_NULL
)
5682 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5684 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5686 if (data
.rightChild
== DIRENTRY_NULL
)
5690 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5692 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5694 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5700 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5701 This
->entries
[entry
].read
= TRUE
;
5708 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5709 TransactedSnapshotImpl
*This
, DirRef entry
)
5713 if (!This
->entries
[entry
].stream_dirty
)
5715 DirEntry new_entrydata
;
5717 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5718 new_entrydata
.name
[0] = 'S';
5719 new_entrydata
.sizeOfNameString
= 1;
5720 new_entrydata
.stgType
= STGTY_STREAM
;
5721 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5722 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5723 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5724 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5726 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5727 &This
->entries
[entry
].stream_entry
);
5729 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5731 hr
= StorageBaseImpl_CopyStream(
5732 This
->scratch
, This
->entries
[entry
].stream_entry
,
5733 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5736 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5740 This
->entries
[entry
].stream_dirty
= TRUE
;
5742 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5744 /* Since this entry is modified, and we aren't using its stream data, we
5745 * no longer care about the original entry. */
5747 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5749 if (delete_ref
!= DIRENTRY_NULL
)
5750 This
->entries
[delete_ref
].deleted
= TRUE
;
5752 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5759 /* Find the first entry in a depth-first traversal. */
5760 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5761 TransactedSnapshotImpl
* This
, DirRef parent
)
5763 DirRef cursor
, prev
;
5764 TransactedDirEntry
*entry
;
5767 entry
= &This
->entries
[cursor
];
5770 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5773 cursor
= entry
->data
.leftChild
;
5774 entry
= &This
->entries
[cursor
];
5775 entry
->parent
= prev
;
5777 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5780 cursor
= entry
->data
.rightChild
;
5781 entry
= &This
->entries
[cursor
];
5782 entry
->parent
= prev
;
5784 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5787 cursor
= entry
->data
.dirRootEntry
;
5788 entry
= &This
->entries
[cursor
];
5789 entry
->parent
= prev
;
5798 /* Find the next entry in a depth-first traversal. */
5799 static DirRef
TransactedSnapshotImpl_FindNextChild(
5800 TransactedSnapshotImpl
* This
, DirRef current
)
5803 TransactedDirEntry
*parent_entry
;
5805 parent
= This
->entries
[current
].parent
;
5806 parent_entry
= &This
->entries
[parent
];
5808 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5810 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5812 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5813 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5816 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5818 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5819 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5826 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5827 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5828 TransactedSnapshotImpl
* This
, DirRef entry
)
5830 return entry
!= DIRENTRY_NULL
&&
5831 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5834 /* Destroy the entries created by CopyTree. */
5835 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5836 TransactedSnapshotImpl
* This
, DirRef stop
)
5839 TransactedDirEntry
*entry
;
5840 ULARGE_INTEGER zero
;
5844 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5847 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5849 if (cursor
== DIRENTRY_NULL
)
5852 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5854 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5856 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5858 entry
= &This
->entries
[cursor
];
5860 if (entry
->stream_dirty
)
5861 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5862 entry
->newTransactedParentEntry
, zero
);
5864 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5865 entry
->newTransactedParentEntry
);
5867 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5870 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5874 /* Make a copy of our edited tree that we can use in the parent. */
5875 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5878 TransactedDirEntry
*entry
;
5881 cursor
= This
->base
.storageDirEntry
;
5882 entry
= &This
->entries
[cursor
];
5883 entry
->parent
= DIRENTRY_NULL
;
5884 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5886 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5889 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5891 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5892 entry
= &This
->entries
[cursor
];
5894 while (cursor
!= DIRENTRY_NULL
)
5896 /* Make a copy of this entry in the transacted parent. */
5898 (!entry
->dirty
&& !entry
->stream_dirty
&&
5899 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5900 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5901 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5902 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5907 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5909 newData
.size
.QuadPart
= 0;
5910 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5912 if (newData
.leftChild
!= DIRENTRY_NULL
)
5913 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5915 if (newData
.rightChild
!= DIRENTRY_NULL
)
5916 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5918 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5919 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5921 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5922 &entry
->newTransactedParentEntry
);
5925 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5929 if (entry
->stream_dirty
)
5931 hr
= StorageBaseImpl_CopyStream(
5932 This
->transactedParent
, entry
->newTransactedParentEntry
,
5933 This
->scratch
, entry
->stream_entry
);
5935 else if (entry
->data
.size
.QuadPart
)
5937 hr
= StorageBaseImpl_StreamLink(
5938 This
->transactedParent
, entry
->newTransactedParentEntry
,
5939 entry
->transactedParentEntry
);
5944 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5945 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5950 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5951 entry
= &This
->entries
[cursor
];
5957 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5959 DWORD grfCommitFlags
) /* [in] */
5961 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5962 TransactedDirEntry
*root_entry
;
5963 DirRef i
, dir_root_ref
;
5965 ULARGE_INTEGER zero
;
5967 ULONG transactionSig
;
5971 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5973 /* Cannot commit a read-only transacted storage */
5974 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5975 return STG_E_ACCESSDENIED
;
5977 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5978 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5981 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5984 if (transactionSig
!= This
->lastTransactionSig
)
5986 ERR("file was externally modified\n");
5987 hr
= STG_E_NOTCURRENT
;
5992 This
->lastTransactionSig
= transactionSig
+1;
5993 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5996 else if (hr
== E_NOTIMPL
)
5999 if (FAILED(hr
)) goto end
;
6001 /* To prevent data loss, we create the new structure in the file before we
6002 * delete the old one, so that in case of errors the old data is intact. We
6003 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
6004 * needed in the rare situation where we have just enough free disk space to
6005 * overwrite the existing data. */
6007 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
6009 if (!root_entry
->read
)
6012 hr
= TransactedSnapshotImpl_CopyTree(This
);
6013 if (FAILED(hr
)) goto end
;
6015 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
6016 dir_root_ref
= DIRENTRY_NULL
;
6018 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
6020 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6022 /* Update the storage to use the new data in one step. */
6024 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
6025 root_entry
->transactedParentEntry
, &data
);
6029 data
.dirRootEntry
= dir_root_ref
;
6030 data
.clsid
= root_entry
->data
.clsid
;
6031 data
.ctime
= root_entry
->data
.ctime
;
6032 data
.mtime
= root_entry
->data
.mtime
;
6034 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
6035 root_entry
->transactedParentEntry
, &data
);
6038 /* Try to flush after updating the root storage, but if the flush fails, keep
6039 * going, on the theory that it'll either succeed later or the subsequent
6040 * writes will fail. */
6041 StorageBaseImpl_Flush(This
->transactedParent
);
6045 /* Destroy the old now-orphaned data. */
6046 for (i
=0; i
<This
->entries_size
; i
++)
6048 TransactedDirEntry
*entry
= &This
->entries
[i
];
6053 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
6054 entry
->transactedParentEntry
, zero
);
6055 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6056 entry
->transactedParentEntry
);
6057 memset(entry
, 0, sizeof(TransactedDirEntry
));
6058 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
6060 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
6062 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
6063 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6064 entry
->transactedParentEntry
);
6065 if (entry
->stream_dirty
)
6067 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
6068 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
6069 entry
->stream_dirty
= FALSE
;
6071 entry
->dirty
= FALSE
;
6072 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
6079 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6083 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6085 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6088 TRACE("<-- %08x\n", hr
);
6092 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6095 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6096 ULARGE_INTEGER zero
;
6099 TRACE("(%p)\n", iface
);
6101 /* Destroy the open objects. */
6102 StorageBaseImpl_DeleteAll(&This
->base
);
6104 /* Clear out the scratch file. */
6106 for (i
=0; i
<This
->entries_size
; i
++)
6108 if (This
->entries
[i
].stream_dirty
)
6110 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6113 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6117 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6119 This
->firstFreeEntry
= 0;
6120 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6125 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6127 if (!This
->reverted
)
6129 TRACE("Storage invalidated (stg=%p)\n", This
);
6131 This
->reverted
= TRUE
;
6133 StorageBaseImpl_DeleteAll(This
);
6137 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6139 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6141 IStorage_Revert(&This
->base
.IStorage_iface
);
6142 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6143 IStorage_Release(&This
->scratch
->IStorage_iface
);
6144 HeapFree(GetProcessHeap(), 0, This
->entries
);
6145 HeapFree(GetProcessHeap(), 0, This
);
6148 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6150 /* We only need to flush when committing. */
6154 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6156 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6158 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6161 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6162 const DirEntry
*newData
, DirRef
*index
)
6164 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6166 TransactedDirEntry
*new_entry
;
6168 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6169 if (new_ref
== DIRENTRY_NULL
)
6170 return E_OUTOFMEMORY
;
6172 new_entry
= &This
->entries
[new_ref
];
6174 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6175 new_entry
->read
= TRUE
;
6176 new_entry
->dirty
= TRUE
;
6177 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6181 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6186 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6187 DirRef index
, const DirEntry
*data
)
6189 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6192 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6194 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6197 TRACE("<-- %08x\n", hr
);
6201 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6203 if (index
!= This
->base
.storageDirEntry
)
6205 This
->entries
[index
].dirty
= TRUE
;
6207 if (data
->size
.QuadPart
== 0 &&
6208 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6210 /* Since this entry is modified, and we aren't using its stream data, we
6211 * no longer care about the original entry. */
6213 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6215 if (delete_ref
!= DIRENTRY_NULL
)
6216 This
->entries
[delete_ref
].deleted
= TRUE
;
6218 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6221 TRACE("<-- S_OK\n");
6225 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6226 DirRef index
, DirEntry
*data
)
6228 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6231 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6234 TRACE("<-- %08x\n", hr
);
6238 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6240 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6245 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6248 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6250 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6251 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6253 /* If we deleted this entry while it has stream data. We must have left the
6254 * data because some other entry is using it, and we need to leave the
6255 * original entry alone. */
6256 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6257 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6261 This
->entries
[index
].deleted
= TRUE
;
6267 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6268 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6270 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6272 if (This
->entries
[index
].stream_dirty
)
6274 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6275 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6277 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6279 /* This stream doesn't live in the parent, and we haven't allocated storage
6286 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6287 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6291 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6292 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6294 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6297 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6300 TRACE("<-- %08x\n", hr
);
6304 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6307 TRACE("<-- %08x\n", hr
);
6311 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6312 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6314 if (SUCCEEDED(hr
) && size
!= 0)
6315 This
->entries
[index
].data
.size
.QuadPart
= max(
6316 This
->entries
[index
].data
.size
.QuadPart
,
6317 offset
.QuadPart
+ size
);
6319 TRACE("<-- %08x\n", hr
);
6323 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6324 DirRef index
, ULARGE_INTEGER newsize
)
6326 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6329 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6332 TRACE("<-- %08x\n", hr
);
6336 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6339 if (newsize
.QuadPart
== 0)
6341 /* Destroy any parent references or entries in the scratch file. */
6342 if (This
->entries
[index
].stream_dirty
)
6344 ULARGE_INTEGER zero
;
6346 StorageBaseImpl_StreamSetSize(This
->scratch
,
6347 This
->entries
[index
].stream_entry
, zero
);
6348 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6349 This
->entries
[index
].stream_entry
);
6350 This
->entries
[index
].stream_dirty
= FALSE
;
6352 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6355 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6357 if (delete_ref
!= DIRENTRY_NULL
)
6358 This
->entries
[delete_ref
].deleted
= TRUE
;
6360 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6365 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6366 if (FAILED(hr
)) return hr
;
6368 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6369 This
->entries
[index
].stream_entry
, newsize
);
6373 This
->entries
[index
].data
.size
= newsize
;
6375 TRACE("<-- %08x\n", hr
);
6379 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6380 DirRef dst
, DirRef src
)
6382 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6384 TransactedDirEntry
*dst_entry
, *src_entry
;
6386 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6389 TRACE("<-- %08x\n", hr
);
6393 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6396 TRACE("<-- %08x\n", hr
);
6400 dst_entry
= &This
->entries
[dst
];
6401 src_entry
= &This
->entries
[src
];
6403 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6404 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6405 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6406 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6407 dst_entry
->data
.size
= src_entry
->data
.size
;
6412 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6413 ULONG
* result
, BOOL refresh
)
6418 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6424 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6429 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6434 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6436 StorageBaseImpl_QueryInterface
,
6437 StorageBaseImpl_AddRef
,
6438 StorageBaseImpl_Release
,
6439 StorageBaseImpl_CreateStream
,
6440 StorageBaseImpl_OpenStream
,
6441 StorageBaseImpl_CreateStorage
,
6442 StorageBaseImpl_OpenStorage
,
6443 StorageBaseImpl_CopyTo
,
6444 StorageBaseImpl_MoveElementTo
,
6445 TransactedSnapshotImpl_Commit
,
6446 TransactedSnapshotImpl_Revert
,
6447 StorageBaseImpl_EnumElements
,
6448 StorageBaseImpl_DestroyElement
,
6449 StorageBaseImpl_RenameElement
,
6450 StorageBaseImpl_SetElementTimes
,
6451 StorageBaseImpl_SetClass
,
6452 StorageBaseImpl_SetStateBits
,
6453 StorageBaseImpl_Stat
6456 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6458 TransactedSnapshotImpl_Destroy
,
6459 TransactedSnapshotImpl_Invalidate
,
6460 TransactedSnapshotImpl_Flush
,
6461 TransactedSnapshotImpl_GetFilename
,
6462 TransactedSnapshotImpl_CreateDirEntry
,
6463 TransactedSnapshotImpl_WriteDirEntry
,
6464 TransactedSnapshotImpl_ReadDirEntry
,
6465 TransactedSnapshotImpl_DestroyDirEntry
,
6466 TransactedSnapshotImpl_StreamReadAt
,
6467 TransactedSnapshotImpl_StreamWriteAt
,
6468 TransactedSnapshotImpl_StreamSetSize
,
6469 TransactedSnapshotImpl_StreamLink
,
6470 TransactedSnapshotImpl_GetTransactionSig
,
6471 TransactedSnapshotImpl_SetTransactionSig
,
6472 TransactedSnapshotImpl_LockTransaction
,
6473 TransactedSnapshotImpl_UnlockTransaction
6476 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6477 TransactedSnapshotImpl
** result
)
6481 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6486 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6488 /* This is OK because the property set storage functions use the IStorage functions. */
6489 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6490 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6492 list_init(&(*result
)->base
.strmHead
);
6494 list_init(&(*result
)->base
.storageHead
);
6496 (*result
)->base
.ref
= 1;
6498 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6500 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6501 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6503 /* Create a new temporary storage to act as the scratch file. */
6504 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6506 (*result
)->scratch
= impl_from_IStorage(scratch
);
6510 ULONG num_entries
= 20;
6512 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6513 (*result
)->entries_size
= num_entries
;
6514 (*result
)->firstFreeEntry
= 0;
6516 if ((*result
)->entries
)
6518 /* parentStorage already has 1 reference, which we take over here. */
6519 (*result
)->transactedParent
= parentStorage
;
6521 parentStorage
->transactedChild
= &(*result
)->base
;
6523 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6527 IStorage_Release(scratch
);
6533 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6538 return E_OUTOFMEMORY
;
6542 /************************************************************************
6543 * TransactedSharedImpl implementation
6544 ***********************************************************************/
6546 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6548 if (!This
->reverted
)
6550 TRACE("Storage invalidated (stg=%p)\n", This
);
6552 This
->reverted
= TRUE
;
6554 StorageBaseImpl_DeleteAll(This
);
6558 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6560 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6562 TransactedSharedImpl_Invalidate(&This
->base
);
6563 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6564 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6565 HeapFree(GetProcessHeap(), 0, This
);
6568 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6570 /* We only need to flush when committing. */
6574 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6576 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6578 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6581 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6582 const DirEntry
*newData
, DirRef
*index
)
6584 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6586 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6590 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6591 DirRef index
, const DirEntry
*data
)
6593 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6595 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6599 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6600 DirRef index
, DirEntry
*data
)
6602 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6604 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6608 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6611 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6613 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6617 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6618 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6620 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6622 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6623 index
, offset
, size
, buffer
, bytesRead
);
6626 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6627 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6629 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6631 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6632 index
, offset
, size
, buffer
, bytesWritten
);
6635 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6636 DirRef index
, ULARGE_INTEGER newsize
)
6638 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6640 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6644 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6645 DirRef dst
, DirRef src
)
6647 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6649 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6653 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6654 ULONG
* result
, BOOL refresh
)
6659 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6665 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6670 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6675 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6677 DWORD grfCommitFlags
) /* [in] */
6679 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6680 DirRef new_storage_ref
, prev_storage_ref
;
6681 DirEntry src_data
, dst_data
;
6683 ULONG transactionSig
;
6685 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6687 /* Cannot commit a read-only transacted storage */
6688 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6689 return STG_E_ACCESSDENIED
;
6691 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6692 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6695 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6698 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6699 hr
= STG_E_NOTCURRENT
;
6702 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6704 else if (hr
== E_NOTIMPL
)
6708 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6710 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6712 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6715 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6718 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6722 prev_storage_ref
= dst_data
.dirRootEntry
;
6723 dst_data
.dirRootEntry
= new_storage_ref
;
6724 dst_data
.clsid
= src_data
.clsid
;
6725 dst_data
.ctime
= src_data
.ctime
;
6726 dst_data
.mtime
= src_data
.mtime
;
6727 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6732 /* Try to flush after updating the root storage, but if the flush fails, keep
6733 * going, on the theory that it'll either succeed later or the subsequent
6734 * writes will fail. */
6735 StorageBaseImpl_Flush(This
->transactedParent
);
6737 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6741 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6743 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6746 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6750 This
->lastTransactionSig
= transactionSig
+1;
6753 TRACE("<-- %08x\n", hr
);
6757 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6760 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6762 TRACE("(%p)\n", iface
);
6764 /* Destroy the open objects. */
6765 StorageBaseImpl_DeleteAll(&This
->base
);
6767 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6770 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6772 StorageBaseImpl_QueryInterface
,
6773 StorageBaseImpl_AddRef
,
6774 StorageBaseImpl_Release
,
6775 StorageBaseImpl_CreateStream
,
6776 StorageBaseImpl_OpenStream
,
6777 StorageBaseImpl_CreateStorage
,
6778 StorageBaseImpl_OpenStorage
,
6779 StorageBaseImpl_CopyTo
,
6780 StorageBaseImpl_MoveElementTo
,
6781 TransactedSharedImpl_Commit
,
6782 TransactedSharedImpl_Revert
,
6783 StorageBaseImpl_EnumElements
,
6784 StorageBaseImpl_DestroyElement
,
6785 StorageBaseImpl_RenameElement
,
6786 StorageBaseImpl_SetElementTimes
,
6787 StorageBaseImpl_SetClass
,
6788 StorageBaseImpl_SetStateBits
,
6789 StorageBaseImpl_Stat
6792 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6794 TransactedSharedImpl_Destroy
,
6795 TransactedSharedImpl_Invalidate
,
6796 TransactedSharedImpl_Flush
,
6797 TransactedSharedImpl_GetFilename
,
6798 TransactedSharedImpl_CreateDirEntry
,
6799 TransactedSharedImpl_WriteDirEntry
,
6800 TransactedSharedImpl_ReadDirEntry
,
6801 TransactedSharedImpl_DestroyDirEntry
,
6802 TransactedSharedImpl_StreamReadAt
,
6803 TransactedSharedImpl_StreamWriteAt
,
6804 TransactedSharedImpl_StreamSetSize
,
6805 TransactedSharedImpl_StreamLink
,
6806 TransactedSharedImpl_GetTransactionSig
,
6807 TransactedSharedImpl_SetTransactionSig
,
6808 TransactedSharedImpl_LockTransaction
,
6809 TransactedSharedImpl_UnlockTransaction
6812 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6813 TransactedSharedImpl
** result
)
6817 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6822 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6824 /* This is OK because the property set storage functions use the IStorage functions. */
6825 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6826 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6828 list_init(&(*result
)->base
.strmHead
);
6830 list_init(&(*result
)->base
.storageHead
);
6832 (*result
)->base
.ref
= 1;
6834 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6836 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6842 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6843 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6847 stgo
.ulSectorSize
= 4096;
6848 stgo
.pwcsTemplateFile
= NULL
;
6850 /* Create a new temporary storage to act as the scratch file. */
6851 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6852 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6853 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6857 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6858 parentStorage
, parentStorage
->storageDirEntry
);
6862 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6864 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6865 (*result
)->transactedParent
= parentStorage
;
6869 IStorage_Release(scratch
);
6872 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6875 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6880 return E_OUTOFMEMORY
;
6883 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6884 BOOL toplevel
, StorageBaseImpl
** result
)
6886 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6888 if (parentStorage
->openFlags
& fixme_flags
)
6890 fixme_flags
&= ~parentStorage
->openFlags
;
6891 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6894 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6895 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6896 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6898 /* Need to create a temp file for the snapshot */
6899 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6902 return TransactedSnapshotImpl_Construct(parentStorage
,
6903 (TransactedSnapshotImpl
**)result
);
6906 static HRESULT
Storage_Construct(
6914 StorageBaseImpl
** result
)
6916 StorageImpl
*newStorage
;
6917 StorageBaseImpl
*newTransactedStorage
;
6920 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6921 if (FAILED(hr
)) goto end
;
6923 if (openFlags
& STGM_TRANSACTED
)
6925 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6927 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6929 *result
= newTransactedStorage
;
6932 *result
= &newStorage
->base
;
6939 /************************************************************************
6940 * StorageUtl helper functions
6941 ***********************************************************************/
6943 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6947 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6948 *value
= lendian16toh(tmp
);
6951 void StorageUtl_WriteWord(void *buffer
, ULONG offset
, WORD value
)
6953 value
= htole16(value
);
6954 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(WORD
));
6957 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6961 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6962 *value
= lendian32toh(tmp
);
6965 void StorageUtl_WriteDWord(void *buffer
, ULONG offset
, DWORD value
)
6967 value
= htole32(value
);
6968 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(DWORD
));
6971 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6972 ULARGE_INTEGER
* value
)
6974 #ifdef WORDS_BIGENDIAN
6977 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6978 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6979 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6981 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6985 void StorageUtl_WriteULargeInteger(void *buffer
, ULONG offset
, const ULARGE_INTEGER
*value
)
6987 #ifdef WORDS_BIGENDIAN
6990 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6991 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6992 memcpy((BYTE
*)buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6994 memcpy((BYTE
*)buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6998 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
7000 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
7001 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
7002 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
7004 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
7007 void StorageUtl_WriteGUID(void *buffer
, ULONG offset
, const GUID
* value
)
7009 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
7010 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
7011 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
7013 memcpy((BYTE
*)buffer
+ offset
+ 8, value
->Data4
, sizeof(value
->Data4
));
7016 void StorageUtl_CopyDirEntryToSTATSTG(
7017 StorageBaseImpl
* storage
,
7018 STATSTG
* destination
,
7019 const DirEntry
* source
,
7023 * The copy of the string occurs only when the flag is not set
7025 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
7027 /* Use the filename for the root storage. */
7028 destination
->pwcsName
= 0;
7029 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
7031 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
7032 (source
->name
[0] == 0) )
7034 destination
->pwcsName
= 0;
7038 destination
->pwcsName
=
7039 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
7041 lstrcpyW(destination
->pwcsName
, source
->name
);
7044 switch (source
->stgType
)
7048 destination
->type
= STGTY_STORAGE
;
7051 destination
->type
= STGTY_STREAM
;
7054 destination
->type
= STGTY_STREAM
;
7058 destination
->cbSize
= source
->size
;
7060 currentReturnStruct->mtime = {0}; TODO
7061 currentReturnStruct->ctime = {0};
7062 currentReturnStruct->atime = {0};
7064 destination
->grfMode
= 0;
7065 destination
->grfLocksSupported
= 0;
7066 destination
->clsid
= source
->clsid
;
7067 destination
->grfStateBits
= 0;
7068 destination
->reserved
= 0;
7072 /************************************************************************
7073 * BlockChainStream implementation
7074 ***********************************************************************/
7076 /******************************************************************************
7077 * BlockChainStream_GetHeadOfChain
7079 * Returns the head of this stream chain.
7080 * Some special chains don't have directory entries, their heads are kept in
7081 * This->headOfStreamPlaceHolder.
7084 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7086 DirEntry chainEntry
;
7089 if (This
->headOfStreamPlaceHolder
!= 0)
7090 return *(This
->headOfStreamPlaceHolder
);
7092 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7094 hr
= StorageImpl_ReadDirEntry(
7095 This
->parentStorage
,
7096 This
->ownerDirEntry
,
7099 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7100 return chainEntry
.startingBlock
;
7103 return BLOCK_END_OF_CHAIN
;
7106 /* Read and save the index of all blocks in this stream. */
7107 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
7109 ULONG next_sector
, next_offset
;
7111 struct BlockChainRun
*last_run
;
7113 if (This
->indexCacheLen
== 0)
7117 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7121 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7122 next_offset
= last_run
->lastOffset
+1;
7123 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7124 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7126 if (FAILED(hr
)) return hr
;
7129 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7131 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7133 /* Add the current block to the cache. */
7134 if (This
->indexCacheSize
== 0)
7136 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7137 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7138 This
->indexCacheSize
= 16;
7140 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7142 struct BlockChainRun
*new_cache
;
7145 new_size
= This
->indexCacheSize
* 2;
7146 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7147 if (!new_cache
) return E_OUTOFMEMORY
;
7148 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7150 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7151 This
->indexCache
= new_cache
;
7152 This
->indexCacheSize
= new_size
;
7155 This
->indexCacheLen
++;
7156 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7157 last_run
->firstSector
= next_sector
;
7158 last_run
->firstOffset
= next_offset
;
7161 last_run
->lastOffset
= next_offset
;
7163 /* Find the next block. */
7165 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7166 if (FAILED(hr
)) return hr
;
7169 if (This
->indexCacheLen
)
7171 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7172 This
->numBlocks
= last_run
->lastOffset
+1;
7176 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7177 This
->numBlocks
= 0;
7183 /* Locate the nth block in this stream. */
7184 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7186 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7187 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7189 if (offset
>= This
->numBlocks
)
7190 return BLOCK_END_OF_CHAIN
;
7192 while (min_run
< max_run
)
7194 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7195 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7197 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7198 max_run
= run_to_check
-1;
7200 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7202 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7203 min_run
= run_to_check
+1;
7206 /* Block is in this run. */
7207 min_run
= max_run
= run_to_check
;
7210 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7213 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7214 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7216 BlockChainBlock
*result
=NULL
;
7220 if (This
->cachedBlocks
[i
].index
== index
)
7222 *sector
= This
->cachedBlocks
[i
].sector
;
7223 *block
= &This
->cachedBlocks
[i
];
7227 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7228 if (*sector
== BLOCK_END_OF_CHAIN
)
7229 return STG_E_DOCFILECORRUPT
;
7233 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7234 result
= &This
->cachedBlocks
[0];
7235 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7236 result
= &This
->cachedBlocks
[1];
7239 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7240 if (This
->blockToEvict
== 2)
7241 This
->blockToEvict
= 0;
7246 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7247 return STG_E_WRITEFAULT
;
7248 result
->dirty
= FALSE
;
7251 result
->read
= FALSE
;
7252 result
->index
= index
;
7253 result
->sector
= *sector
;
7260 BlockChainStream
* BlockChainStream_Construct(
7261 StorageImpl
* parentStorage
,
7262 ULONG
* headOfStreamPlaceHolder
,
7265 BlockChainStream
* newStream
;
7267 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7271 newStream
->parentStorage
= parentStorage
;
7272 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7273 newStream
->ownerDirEntry
= dirEntry
;
7274 newStream
->indexCache
= NULL
;
7275 newStream
->indexCacheLen
= 0;
7276 newStream
->indexCacheSize
= 0;
7277 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7278 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7279 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7280 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7281 newStream
->blockToEvict
= 0;
7283 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7285 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7286 HeapFree(GetProcessHeap(), 0, newStream
);
7293 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7296 if (!This
) return S_OK
;
7299 if (This
->cachedBlocks
[i
].dirty
)
7301 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7302 This
->cachedBlocks
[i
].dirty
= FALSE
;
7304 return STG_E_WRITEFAULT
;
7310 void BlockChainStream_Destroy(BlockChainStream
* This
)
7314 BlockChainStream_Flush(This
);
7315 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7317 HeapFree(GetProcessHeap(), 0, This
);
7320 /******************************************************************************
7321 * BlockChainStream_Shrink
7323 * Shrinks this chain in the big block depot.
7325 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7326 ULARGE_INTEGER newSize
)
7333 * Figure out how many blocks are needed to contain the new size
7335 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7337 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7343 * Go to the new end of chain
7345 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7347 /* Mark the new end of chain */
7348 StorageImpl_SetNextBlockInChain(
7349 This
->parentStorage
,
7351 BLOCK_END_OF_CHAIN
);
7353 This
->tailIndex
= blockIndex
;
7357 if (This
->headOfStreamPlaceHolder
!= 0)
7359 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7363 DirEntry chainEntry
;
7364 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7366 StorageImpl_ReadDirEntry(
7367 This
->parentStorage
,
7368 This
->ownerDirEntry
,
7371 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7373 StorageImpl_WriteDirEntry(
7374 This
->parentStorage
,
7375 This
->ownerDirEntry
,
7379 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7382 This
->numBlocks
= numBlocks
;
7385 * Mark the extra blocks as free
7387 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7389 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7390 StorageImpl_FreeBigBlock(This
->parentStorage
,
7391 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7392 if (last_run
->lastOffset
== last_run
->firstOffset
)
7393 This
->indexCacheLen
--;
7395 last_run
->lastOffset
--;
7399 * Reset the last accessed block cache.
7403 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7405 This
->cachedBlocks
[i
].index
= 0xffffffff;
7406 This
->cachedBlocks
[i
].dirty
= FALSE
;
7413 /******************************************************************************
7414 * BlockChainStream_Enlarge
7416 * Grows this chain in the big block depot.
7418 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7419 ULARGE_INTEGER newSize
)
7421 ULONG blockIndex
, currentBlock
;
7423 ULONG oldNumBlocks
= 0;
7425 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7428 * Empty chain. Create the head.
7430 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7432 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7433 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7435 BLOCK_END_OF_CHAIN
);
7437 if (This
->headOfStreamPlaceHolder
!= 0)
7439 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7443 DirEntry chainEntry
;
7444 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7446 StorageImpl_ReadDirEntry(
7447 This
->parentStorage
,
7448 This
->ownerDirEntry
,
7451 chainEntry
.startingBlock
= blockIndex
;
7453 StorageImpl_WriteDirEntry(
7454 This
->parentStorage
,
7455 This
->ownerDirEntry
,
7459 This
->tailIndex
= blockIndex
;
7460 This
->numBlocks
= 1;
7464 * Figure out how many blocks are needed to contain this stream
7466 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7468 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7472 * Go to the current end of chain
7474 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7476 currentBlock
= blockIndex
;
7478 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7481 currentBlock
= blockIndex
;
7483 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7488 This
->tailIndex
= currentBlock
;
7491 currentBlock
= This
->tailIndex
;
7492 oldNumBlocks
= This
->numBlocks
;
7495 * Add new blocks to the chain
7497 if (oldNumBlocks
< newNumBlocks
)
7499 while (oldNumBlocks
< newNumBlocks
)
7501 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7503 StorageImpl_SetNextBlockInChain(
7504 This
->parentStorage
,
7508 StorageImpl_SetNextBlockInChain(
7509 This
->parentStorage
,
7511 BLOCK_END_OF_CHAIN
);
7513 currentBlock
= blockIndex
;
7517 This
->tailIndex
= blockIndex
;
7518 This
->numBlocks
= newNumBlocks
;
7521 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7528 /******************************************************************************
7529 * BlockChainStream_GetSize
7531 * Returns the size of this chain.
7532 * Will return the block count if this chain doesn't have a directory entry.
7534 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7536 DirEntry chainEntry
;
7538 if(This
->headOfStreamPlaceHolder
== NULL
)
7541 * This chain has a directory entry so use the size value from there.
7543 StorageImpl_ReadDirEntry(
7544 This
->parentStorage
,
7545 This
->ownerDirEntry
,
7548 return chainEntry
.size
;
7553 * this chain is a chain that does not have a directory entry, figure out the
7554 * size by making the product number of used blocks times the
7557 ULARGE_INTEGER result
;
7559 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7560 This
->parentStorage
->bigBlockSize
;
7566 /******************************************************************************
7567 * BlockChainStream_SetSize
7569 * Sets the size of this stream. The big block depot will be updated.
7570 * The file will grow if we grow the chain.
7572 * TODO: Free the actual blocks in the file when we shrink the chain.
7573 * Currently, the blocks are still in the file. So the file size
7574 * doesn't shrink even if we shrink streams.
7576 BOOL
BlockChainStream_SetSize(
7577 BlockChainStream
* This
,
7578 ULARGE_INTEGER newSize
)
7580 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7582 if (newSize
.QuadPart
== size
.QuadPart
)
7585 if (newSize
.QuadPart
< size
.QuadPart
)
7587 BlockChainStream_Shrink(This
, newSize
);
7591 BlockChainStream_Enlarge(This
, newSize
);
7597 /******************************************************************************
7598 * BlockChainStream_ReadAt
7600 * Reads a specified number of bytes from this chain at the specified offset.
7601 * bytesRead may be NULL.
7602 * Failure will be returned if the specified number of bytes has not been read.
7604 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7605 ULARGE_INTEGER offset
,
7610 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7611 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7612 ULONG bytesToReadInBuffer
;
7615 ULARGE_INTEGER stream_size
;
7617 BlockChainBlock
*cachedBlock
;
7619 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7622 * Find the first block in the stream that contains part of the buffer.
7624 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7628 stream_size
= BlockChainStream_GetSize(This
);
7629 if (stream_size
.QuadPart
> offset
.QuadPart
)
7630 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7635 * Start reading the buffer.
7637 bufferWalker
= buffer
;
7641 ULARGE_INTEGER ulOffset
;
7645 * Calculate how many bytes we can copy from this big block.
7647 bytesToReadInBuffer
=
7648 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7650 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7657 /* Not in cache, and we're going to read past the end of the block. */
7658 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7661 StorageImpl_ReadAt(This
->parentStorage
,
7664 bytesToReadInBuffer
,
7669 if (!cachedBlock
->read
)
7672 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7673 return STG_E_READFAULT
;
7675 cachedBlock
->read
= TRUE
;
7678 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7679 bytesReadAt
= bytesToReadInBuffer
;
7682 blockNoInSequence
++;
7683 bufferWalker
+= bytesReadAt
;
7684 size
-= bytesReadAt
;
7685 *bytesRead
+= bytesReadAt
;
7686 offsetInBlock
= 0; /* There is no offset on the next block */
7688 if (bytesToReadInBuffer
!= bytesReadAt
)
7695 /******************************************************************************
7696 * BlockChainStream_WriteAt
7698 * Writes the specified number of bytes to this chain at the specified offset.
7699 * Will fail if not all specified number of bytes have been written.
7701 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7702 ULARGE_INTEGER offset
,
7705 ULONG
* bytesWritten
)
7707 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7708 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7711 const BYTE
* bufferWalker
;
7713 BlockChainBlock
*cachedBlock
;
7716 bufferWalker
= buffer
;
7720 ULARGE_INTEGER ulOffset
;
7721 DWORD bytesWrittenAt
;
7724 * Calculate how many bytes we can copy to this big block.
7727 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7729 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7731 /* BlockChainStream_SetSize should have already been called to ensure we have
7732 * enough blocks in the chain to write into */
7735 ERR("not enough blocks in chain to write data\n");
7741 /* Not in cache, and we're going to write past the end of the block. */
7742 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7745 StorageImpl_WriteAt(This
->parentStorage
,
7753 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7756 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7757 return STG_E_READFAULT
;
7760 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7761 bytesWrittenAt
= bytesToWrite
;
7762 cachedBlock
->read
= TRUE
;
7763 cachedBlock
->dirty
= TRUE
;
7766 blockNoInSequence
++;
7767 bufferWalker
+= bytesWrittenAt
;
7768 size
-= bytesWrittenAt
;
7769 *bytesWritten
+= bytesWrittenAt
;
7770 offsetInBlock
= 0; /* There is no offset on the next block */
7772 if (bytesWrittenAt
!= bytesToWrite
)
7776 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7780 /************************************************************************
7781 * SmallBlockChainStream implementation
7782 ***********************************************************************/
7784 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7785 StorageImpl
* parentStorage
,
7786 ULONG
* headOfStreamPlaceHolder
,
7789 SmallBlockChainStream
* newStream
;
7791 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7793 newStream
->parentStorage
= parentStorage
;
7794 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7795 newStream
->ownerDirEntry
= dirEntry
;
7800 void SmallBlockChainStream_Destroy(
7801 SmallBlockChainStream
* This
)
7803 HeapFree(GetProcessHeap(), 0, This
);
7806 /******************************************************************************
7807 * SmallBlockChainStream_GetHeadOfChain
7809 * Returns the head of this chain of small blocks.
7811 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7812 SmallBlockChainStream
* This
)
7814 DirEntry chainEntry
;
7817 if (This
->headOfStreamPlaceHolder
!= NULL
)
7818 return *(This
->headOfStreamPlaceHolder
);
7820 if (This
->ownerDirEntry
)
7822 hr
= StorageImpl_ReadDirEntry(
7823 This
->parentStorage
,
7824 This
->ownerDirEntry
,
7827 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7828 return chainEntry
.startingBlock
;
7831 return BLOCK_END_OF_CHAIN
;
7834 /******************************************************************************
7835 * SmallBlockChainStream_GetNextBlockInChain
7837 * Returns the index of the next small block in this chain.
7840 * - BLOCK_END_OF_CHAIN: end of this chain
7841 * - BLOCK_UNUSED: small block 'blockIndex' is free
7843 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7844 SmallBlockChainStream
* This
,
7846 ULONG
* nextBlockInChain
)
7848 ULARGE_INTEGER offsetOfBlockInDepot
;
7853 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7855 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7858 * Read those bytes in the buffer from the small block file.
7860 res
= BlockChainStream_ReadAt(
7861 This
->parentStorage
->smallBlockDepotChain
,
7862 offsetOfBlockInDepot
,
7867 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7868 res
= STG_E_READFAULT
;
7872 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7879 /******************************************************************************
7880 * SmallBlockChainStream_SetNextBlockInChain
7882 * Writes the index of the next block of the specified block in the small
7884 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7885 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7887 static void SmallBlockChainStream_SetNextBlockInChain(
7888 SmallBlockChainStream
* This
,
7892 ULARGE_INTEGER offsetOfBlockInDepot
;
7896 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7898 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
7901 * Read those bytes in the buffer from the small block file.
7903 BlockChainStream_WriteAt(
7904 This
->parentStorage
->smallBlockDepotChain
,
7905 offsetOfBlockInDepot
,
7911 /******************************************************************************
7912 * SmallBlockChainStream_FreeBlock
7914 * Flag small block 'blockIndex' as free in the small block depot.
7916 static void SmallBlockChainStream_FreeBlock(
7917 SmallBlockChainStream
* This
,
7920 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7923 /******************************************************************************
7924 * SmallBlockChainStream_GetNextFreeBlock
7926 * Returns the index of a free small block. The small block depot will be
7927 * enlarged if necessary. The small block chain will also be enlarged if
7930 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7931 SmallBlockChainStream
* This
)
7933 ULARGE_INTEGER offsetOfBlockInDepot
;
7936 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7937 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7939 ULONG smallBlocksPerBigBlock
;
7941 ULONG blocksRequired
;
7942 ULARGE_INTEGER old_size
, size_required
;
7944 offsetOfBlockInDepot
.u
.HighPart
= 0;
7947 * Scan the small block depot for a free block
7949 while (nextBlockIndex
!= BLOCK_UNUSED
)
7951 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7953 res
= BlockChainStream_ReadAt(
7954 This
->parentStorage
->smallBlockDepotChain
,
7955 offsetOfBlockInDepot
,
7961 * If we run out of space for the small block depot, enlarge it
7963 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7965 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7967 if (nextBlockIndex
!= BLOCK_UNUSED
)
7973 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7975 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7976 ULARGE_INTEGER newSize
, offset
;
7979 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7980 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7983 * Initialize all the small blocks to free
7985 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7986 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7987 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7988 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7990 StorageImpl_SaveFileHeader(This
->parentStorage
);
7994 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7996 smallBlocksPerBigBlock
=
7997 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
8000 * Verify if we have to allocate big blocks to contain small blocks
8002 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
8004 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
8006 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
8008 if (size_required
.QuadPart
> old_size
.QuadPart
)
8010 BlockChainStream_SetSize(
8011 This
->parentStorage
->smallBlockRootChain
,
8014 StorageImpl_ReadDirEntry(
8015 This
->parentStorage
,
8016 This
->parentStorage
->base
.storageDirEntry
,
8019 rootEntry
.size
= size_required
;
8021 StorageImpl_WriteDirEntry(
8022 This
->parentStorage
,
8023 This
->parentStorage
->base
.storageDirEntry
,
8030 /******************************************************************************
8031 * SmallBlockChainStream_ReadAt
8033 * Reads a specified number of bytes from this chain at the specified offset.
8034 * bytesRead may be NULL.
8035 * Failure will be returned if the specified number of bytes has not been read.
8037 HRESULT
SmallBlockChainStream_ReadAt(
8038 SmallBlockChainStream
* This
,
8039 ULARGE_INTEGER offset
,
8045 ULARGE_INTEGER offsetInBigBlockFile
;
8046 ULONG blockNoInSequence
=
8047 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8049 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8050 ULONG bytesToReadInBuffer
;
8052 ULONG bytesReadFromBigBlockFile
;
8054 ULARGE_INTEGER stream_size
;
8057 * This should never happen on a small block file.
8059 assert(offset
.u
.HighPart
==0);
8063 stream_size
= SmallBlockChainStream_GetSize(This
);
8064 if (stream_size
.QuadPart
> offset
.QuadPart
)
8065 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
8070 * Find the first block in the stream that contains part of the buffer.
8072 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8074 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8076 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8079 blockNoInSequence
--;
8083 * Start reading the buffer.
8085 bufferWalker
= buffer
;
8087 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8090 * Calculate how many bytes we can copy from this small block.
8092 bytesToReadInBuffer
=
8093 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8096 * Calculate the offset of the small block in the small block file.
8098 offsetInBigBlockFile
.QuadPart
=
8099 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8101 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8104 * Read those bytes in the buffer from the small block file.
8105 * The small block has already been identified so it shouldn't fail
8106 * unless the file is corrupt.
8108 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
8109 offsetInBigBlockFile
,
8110 bytesToReadInBuffer
,
8112 &bytesReadFromBigBlockFile
);
8117 if (!bytesReadFromBigBlockFile
)
8118 return STG_E_DOCFILECORRUPT
;
8121 * Step to the next big block.
8123 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8125 return STG_E_DOCFILECORRUPT
;
8127 bufferWalker
+= bytesReadFromBigBlockFile
;
8128 size
-= bytesReadFromBigBlockFile
;
8129 *bytesRead
+= bytesReadFromBigBlockFile
;
8130 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8136 /******************************************************************************
8137 * SmallBlockChainStream_WriteAt
8139 * Writes the specified number of bytes to this chain at the specified offset.
8140 * Will fail if not all specified number of bytes have been written.
8142 HRESULT
SmallBlockChainStream_WriteAt(
8143 SmallBlockChainStream
* This
,
8144 ULARGE_INTEGER offset
,
8147 ULONG
* bytesWritten
)
8149 ULARGE_INTEGER offsetInBigBlockFile
;
8150 ULONG blockNoInSequence
=
8151 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8153 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8154 ULONG bytesToWriteInBuffer
;
8156 ULONG bytesWrittenToBigBlockFile
;
8157 const BYTE
* bufferWalker
;
8161 * This should never happen on a small block file.
8163 assert(offset
.u
.HighPart
==0);
8166 * Find the first block in the stream that contains part of the buffer.
8168 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8170 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8172 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8173 return STG_E_DOCFILECORRUPT
;
8174 blockNoInSequence
--;
8178 * Start writing the buffer.
8181 bufferWalker
= buffer
;
8182 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8185 * Calculate how many bytes we can copy to this small block.
8187 bytesToWriteInBuffer
=
8188 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8191 * Calculate the offset of the small block in the small block file.
8193 offsetInBigBlockFile
.QuadPart
=
8194 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8196 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8199 * Write those bytes in the buffer to the small block file.
8201 res
= BlockChainStream_WriteAt(
8202 This
->parentStorage
->smallBlockRootChain
,
8203 offsetInBigBlockFile
,
8204 bytesToWriteInBuffer
,
8206 &bytesWrittenToBigBlockFile
);
8211 * Step to the next big block.
8213 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8216 bufferWalker
+= bytesWrittenToBigBlockFile
;
8217 size
-= bytesWrittenToBigBlockFile
;
8218 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8219 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8222 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8225 /******************************************************************************
8226 * SmallBlockChainStream_Shrink
8228 * Shrinks this chain in the small block depot.
8230 static BOOL
SmallBlockChainStream_Shrink(
8231 SmallBlockChainStream
* This
,
8232 ULARGE_INTEGER newSize
)
8234 ULONG blockIndex
, extraBlock
;
8238 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8240 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8243 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8246 * Go to the new end of chain
8248 while (count
< numBlocks
)
8250 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8257 * If the count is 0, we have a special case, the head of the chain was
8262 DirEntry chainEntry
;
8264 StorageImpl_ReadDirEntry(This
->parentStorage
,
8265 This
->ownerDirEntry
,
8268 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8270 StorageImpl_WriteDirEntry(This
->parentStorage
,
8271 This
->ownerDirEntry
,
8275 * We start freeing the chain at the head block.
8277 extraBlock
= blockIndex
;
8281 /* Get the next block before marking the new end */
8282 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8286 /* Mark the new end of chain */
8287 SmallBlockChainStream_SetNextBlockInChain(
8290 BLOCK_END_OF_CHAIN
);
8294 * Mark the extra blocks as free
8296 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8298 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8301 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8302 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8303 extraBlock
= blockIndex
;
8309 /******************************************************************************
8310 * SmallBlockChainStream_Enlarge
8312 * Grows this chain in the small block depot.
8314 static BOOL
SmallBlockChainStream_Enlarge(
8315 SmallBlockChainStream
* This
,
8316 ULARGE_INTEGER newSize
)
8318 ULONG blockIndex
, currentBlock
;
8320 ULONG oldNumBlocks
= 0;
8322 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8325 * Empty chain. Create the head.
8327 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8329 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8330 SmallBlockChainStream_SetNextBlockInChain(
8333 BLOCK_END_OF_CHAIN
);
8335 if (This
->headOfStreamPlaceHolder
!= NULL
)
8337 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8341 DirEntry chainEntry
;
8343 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8346 chainEntry
.startingBlock
= blockIndex
;
8348 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8353 currentBlock
= blockIndex
;
8356 * Figure out how many blocks are needed to contain this stream
8358 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8360 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8364 * Go to the current end of chain
8366 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8369 currentBlock
= blockIndex
;
8370 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8375 * Add new blocks to the chain
8377 while (oldNumBlocks
< newNumBlocks
)
8379 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8380 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8382 SmallBlockChainStream_SetNextBlockInChain(
8385 BLOCK_END_OF_CHAIN
);
8387 currentBlock
= blockIndex
;
8394 /******************************************************************************
8395 * SmallBlockChainStream_SetSize
8397 * Sets the size of this stream.
8398 * The file will grow if we grow the chain.
8400 * TODO: Free the actual blocks in the file when we shrink the chain.
8401 * Currently, the blocks are still in the file. So the file size
8402 * doesn't shrink even if we shrink streams.
8404 BOOL
SmallBlockChainStream_SetSize(
8405 SmallBlockChainStream
* This
,
8406 ULARGE_INTEGER newSize
)
8408 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8410 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8413 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8415 SmallBlockChainStream_Shrink(This
, newSize
);
8419 SmallBlockChainStream_Enlarge(This
, newSize
);
8425 /******************************************************************************
8426 * SmallBlockChainStream_GetCount
8428 * Returns the number of small blocks that comprises this chain.
8429 * This is not the size of the stream as the last block may not be full!
8432 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8437 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8439 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8443 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8444 blockIndex
, &blockIndex
)))
8451 /******************************************************************************
8452 * SmallBlockChainStream_GetSize
8454 * Returns the size of this chain.
8456 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8458 DirEntry chainEntry
;
8460 if(This
->headOfStreamPlaceHolder
!= NULL
)
8462 ULARGE_INTEGER result
;
8463 result
.u
.HighPart
= 0;
8465 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8466 This
->parentStorage
->smallBlockSize
;
8471 StorageImpl_ReadDirEntry(
8472 This
->parentStorage
,
8473 This
->ownerDirEntry
,
8476 return chainEntry
.size
;
8480 /************************************************************************
8481 * Miscellaneous storage functions
8482 ***********************************************************************/
8484 static HRESULT
create_storagefile(
8488 STGOPTIONS
* pStgOptions
,
8492 StorageBaseImpl
* newStorage
= 0;
8493 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8494 HRESULT hr
= STG_E_INVALIDFLAG
;
8498 DWORD fileAttributes
;
8499 WCHAR tempFileName
[MAX_PATH
];
8502 return STG_E_INVALIDPOINTER
;
8504 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8505 return STG_E_INVALIDPARAMETER
;
8507 /* if no share mode given then DENY_NONE is the default */
8508 if (STGM_SHARE_MODE(grfMode
) == 0)
8509 grfMode
|= STGM_SHARE_DENY_NONE
;
8511 if ( FAILED( validateSTGM(grfMode
) ))
8514 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8515 switch(STGM_ACCESS_MODE(grfMode
))
8518 case STGM_READWRITE
:
8524 /* in direct mode, can only use SHARE_EXCLUSIVE */
8525 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8528 /* but in transacted mode, any share mode is valid */
8531 * Generate a unique name.
8535 WCHAR tempPath
[MAX_PATH
];
8536 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8538 memset(tempPath
, 0, sizeof(tempPath
));
8539 memset(tempFileName
, 0, sizeof(tempFileName
));
8541 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8544 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8545 pwcsName
= tempFileName
;
8548 hr
= STG_E_INSUFFICIENTMEMORY
;
8552 creationMode
= TRUNCATE_EXISTING
;
8556 creationMode
= GetCreationModeFromSTGM(grfMode
);
8560 * Interpret the STGM value grfMode
8562 shareMode
= GetShareModeFromSTGM(grfMode
);
8563 accessMode
= GetAccessModeFromSTGM(grfMode
);
8565 if (grfMode
& STGM_DELETEONRELEASE
)
8566 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8568 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8572 hFile
= CreateFileW(pwcsName
,
8580 if (hFile
== INVALID_HANDLE_VALUE
)
8582 if(GetLastError() == ERROR_FILE_EXISTS
)
8583 hr
= STG_E_FILEALREADYEXISTS
;
8590 * Allocate and initialize the new IStorage object.
8592 hr
= Storage_Construct(
8599 pStgOptions
->ulSectorSize
,
8607 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8608 IStorage_Release(&newStorage
->IStorage_iface
);
8611 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8616 /******************************************************************************
8617 * StgCreateDocfile [OLE32.@]
8618 * Creates a new compound file storage object
8621 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8622 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8623 * reserved [ ?] unused?, usually 0
8624 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8627 * S_OK if the file was successfully created
8628 * some STG_E_ value if error
8630 * if pwcsName is NULL, create file with new unique name
8631 * the function can returns
8632 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8635 HRESULT WINAPI
StgCreateDocfile(
8639 IStorage
**ppstgOpen
)
8641 STGOPTIONS stgoptions
= {1, 0, 512};
8643 TRACE("(%s, %x, %d, %p)\n",
8644 debugstr_w(pwcsName
), grfMode
,
8645 reserved
, ppstgOpen
);
8648 return STG_E_INVALIDPOINTER
;
8650 return STG_E_INVALIDPARAMETER
;
8652 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8655 /******************************************************************************
8656 * StgCreateStorageEx [OLE32.@]
8658 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8660 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8661 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8663 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8665 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8666 return STG_E_INVALIDPARAMETER
;
8669 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8671 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8672 return STG_E_INVALIDPARAMETER
;
8675 if (stgfmt
== STGFMT_FILE
)
8677 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8678 return STG_E_INVALIDPARAMETER
;
8681 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8683 STGOPTIONS defaultOptions
= {1, 0, 512};
8685 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8686 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8690 ERR("Invalid stgfmt argument\n");
8691 return STG_E_INVALIDPARAMETER
;
8694 /******************************************************************************
8695 * StgCreatePropSetStg [OLE32.@]
8697 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8698 IPropertySetStorage
**propset
)
8700 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8702 return STG_E_INVALIDPARAMETER
;
8704 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8707 /******************************************************************************
8708 * StgOpenStorageEx [OLE32.@]
8710 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8712 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8713 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8715 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8717 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8718 return STG_E_INVALIDPARAMETER
;
8724 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8725 return STG_E_INVALIDPARAMETER
;
8727 case STGFMT_STORAGE
:
8730 case STGFMT_DOCFILE
:
8731 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8733 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8734 return STG_E_INVALIDPARAMETER
;
8736 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8740 WARN("STGFMT_ANY assuming storage\n");
8744 return STG_E_INVALIDPARAMETER
;
8747 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8751 /******************************************************************************
8752 * StgOpenStorage [OLE32.@]
8754 HRESULT WINAPI
StgOpenStorage(
8755 const OLECHAR
*pwcsName
,
8756 IStorage
*pstgPriority
,
8760 IStorage
**ppstgOpen
)
8762 StorageBaseImpl
* newStorage
= 0;
8767 LPWSTR temp_name
= NULL
;
8769 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8770 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8771 snbExclude
, reserved
, ppstgOpen
);
8775 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8776 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8777 if (FAILED(hr
)) goto end
;
8778 pwcsName
= temp_name
;
8779 TRACE("using filename %s\n", debugstr_w(temp_name
));
8784 hr
= STG_E_INVALIDNAME
;
8790 hr
= STG_E_INVALIDPOINTER
;
8796 hr
= STG_E_INVALIDPARAMETER
;
8800 if (grfMode
& STGM_PRIORITY
)
8802 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8803 return STG_E_INVALIDFLAG
;
8804 if (grfMode
& STGM_DELETEONRELEASE
)
8805 return STG_E_INVALIDFUNCTION
;
8806 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8807 return STG_E_INVALIDFLAG
;
8808 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8809 grfMode
|= STGM_SHARE_DENY_NONE
;
8813 * Validate the sharing mode
8815 if (grfMode
& STGM_DIRECT_SWMR
)
8817 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8818 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8820 hr
= STG_E_INVALIDFLAG
;
8824 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8825 switch(STGM_SHARE_MODE(grfMode
))
8827 case STGM_SHARE_EXCLUSIVE
:
8828 case STGM_SHARE_DENY_WRITE
:
8831 hr
= STG_E_INVALIDFLAG
;
8835 if ( FAILED( validateSTGM(grfMode
) ) ||
8836 (grfMode
&STGM_CREATE
))
8838 hr
= STG_E_INVALIDFLAG
;
8842 /* shared reading requires transacted or single writer mode */
8843 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8844 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8845 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8847 hr
= STG_E_INVALIDFLAG
;
8852 * Interpret the STGM value grfMode
8854 shareMode
= GetShareModeFromSTGM(grfMode
);
8855 accessMode
= GetAccessModeFromSTGM(grfMode
);
8859 hFile
= CreateFileW( pwcsName
,
8864 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8867 if (hFile
==INVALID_HANDLE_VALUE
)
8869 DWORD last_error
= GetLastError();
8875 case ERROR_FILE_NOT_FOUND
:
8876 hr
= STG_E_FILENOTFOUND
;
8879 case ERROR_PATH_NOT_FOUND
:
8880 hr
= STG_E_PATHNOTFOUND
;
8883 case ERROR_ACCESS_DENIED
:
8884 case ERROR_WRITE_PROTECT
:
8885 hr
= STG_E_ACCESSDENIED
;
8888 case ERROR_SHARING_VIOLATION
:
8889 hr
= STG_E_SHAREVIOLATION
;
8900 * Refuse to open the file if it's too small to be a structured storage file
8901 * FIXME: verify the file when reading instead of here
8903 if (GetFileSize(hFile
, NULL
) < HEADER_SIZE
)
8906 hr
= STG_E_FILEALREADYEXISTS
;
8911 * Allocate and initialize the new IStorage object.
8913 hr
= Storage_Construct(
8926 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8928 if(hr
== STG_E_INVALIDHEADER
)
8929 hr
= STG_E_FILEALREADYEXISTS
;
8933 *ppstgOpen
= &newStorage
->IStorage_iface
;
8936 CoTaskMemFree(temp_name
);
8937 if (pstgPriority
) IStorage_Release(pstgPriority
);
8938 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8942 /******************************************************************************
8943 * StgCreateDocfileOnILockBytes [OLE32.@]
8945 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8949 IStorage
** ppstgOpen
)
8951 StorageBaseImpl
* newStorage
= 0;
8954 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8955 return STG_E_INVALIDPOINTER
;
8958 * Allocate and initialize the new IStorage object.
8960 hr
= Storage_Construct(
8975 *ppstgOpen
= &newStorage
->IStorage_iface
;
8980 /******************************************************************************
8981 * StgOpenStorageOnILockBytes [OLE32.@]
8983 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8985 IStorage
*pstgPriority
,
8989 IStorage
**ppstgOpen
)
8991 StorageBaseImpl
* newStorage
= 0;
8994 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8995 return STG_E_INVALIDPOINTER
;
8997 if ( FAILED( validateSTGM(grfMode
) ))
8998 return STG_E_INVALIDFLAG
;
9003 * Allocate and initialize the new IStorage object.
9005 hr
= Storage_Construct(
9020 *ppstgOpen
= &newStorage
->IStorage_iface
;
9025 /******************************************************************************
9026 * StgSetTimes [ole32.@]
9027 * StgSetTimes [OLE32.@]
9031 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
9032 FILETIME
const *patime
, FILETIME
const *pmtime
)
9034 IStorage
*stg
= NULL
;
9037 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
9039 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
9043 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
9044 IStorage_Release(stg
);
9050 /******************************************************************************
9051 * StgIsStorageILockBytes [OLE32.@]
9053 * Determines if the ILockBytes contains a storage object.
9055 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
9057 BYTE sig
[sizeof(STORAGE_magic
)];
9058 ULARGE_INTEGER offset
;
9061 offset
.u
.HighPart
= 0;
9062 offset
.u
.LowPart
= 0;
9064 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
9066 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
9072 /******************************************************************************
9073 * WriteClassStg [OLE32.@]
9075 * This method will store the specified CLSID in the specified storage object
9077 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
9080 return E_INVALIDARG
;
9083 return STG_E_INVALIDPOINTER
;
9085 return IStorage_SetClass(pStg
, rclsid
);
9088 /***********************************************************************
9089 * ReadClassStg (OLE32.@)
9091 * This method reads the CLSID previously written to a storage object with
9092 * the WriteClassStg.
9095 * pstg [I] IStorage pointer
9096 * pclsid [O] Pointer to where the CLSID is written
9100 * Failure: HRESULT code.
9102 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
9107 TRACE("(%p, %p)\n", pstg
, pclsid
);
9109 if(!pstg
|| !pclsid
)
9110 return E_INVALIDARG
;
9113 * read a STATSTG structure (contains the clsid) from the storage
9115 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9118 *pclsid
=pstatstg
.clsid
;
9123 /***********************************************************************
9124 * OleLoadFromStream (OLE32.@)
9126 * This function loads an object from stream
9128 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9132 LPPERSISTSTREAM xstm
;
9134 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9136 res
=ReadClassStm(pStm
,&clsid
);
9139 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9142 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9144 IUnknown_Release((IUnknown
*)*ppvObj
);
9147 res
=IPersistStream_Load(xstm
,pStm
);
9148 IPersistStream_Release(xstm
);
9149 /* FIXME: all refcounts ok at this point? I think they should be:
9152 * xstm : 0 (released)
9157 /***********************************************************************
9158 * OleSaveToStream (OLE32.@)
9160 * This function saves an object with the IPersistStream interface on it
9161 * to the specified stream.
9163 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9169 TRACE("(%p,%p)\n",pPStm
,pStm
);
9171 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9173 if (SUCCEEDED(res
)){
9175 res
=WriteClassStm(pStm
,&clsid
);
9179 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9182 TRACE("Finished Save\n");
9186 /*************************************************************************
9187 * STORAGE_CreateOleStream [Internal]
9189 * Creates the "\001OLE" stream in the IStorage if necessary.
9192 * storage [I] Dest storage to create the stream in
9193 * flags [I] flags to be set for newly created stream
9196 * HRESULT return value
9200 * This stream is still unknown, MS Word seems to have extra data
9201 * but since the data is stored in the OLESTREAM there should be
9202 * no need to recreate the stream. If the stream is manually
9203 * deleted it will create it with this default data.
9206 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9208 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9209 static const DWORD version_magic
= 0x02000001;
9213 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9216 struct empty_1ole_stream
{
9217 DWORD version_magic
;
9219 DWORD update_options
;
9221 DWORD mon_stream_size
;
9223 struct empty_1ole_stream stream_data
;
9225 stream_data
.version_magic
= version_magic
;
9226 stream_data
.flags
= flags
;
9227 stream_data
.update_options
= 0;
9228 stream_data
.reserved
= 0;
9229 stream_data
.mon_stream_size
= 0;
9231 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9232 IStream_Release(stream
);
9238 /* write a string to a stream, preceded by its length */
9239 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9246 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9247 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9252 str
= CoTaskMemAlloc( len
);
9253 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9254 r
= IStream_Write( stm
, str
, len
, NULL
);
9255 CoTaskMemFree( str
);
9259 /* read a string preceded by its length from a stream */
9260 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9263 DWORD len
, count
= 0;
9267 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9270 if( count
!= sizeof(len
) )
9271 return E_OUTOFMEMORY
;
9273 TRACE("%d bytes\n",len
);
9275 str
= CoTaskMemAlloc( len
);
9277 return E_OUTOFMEMORY
;
9279 r
= IStream_Read( stm
, str
, len
, &count
);
9282 CoTaskMemFree( str
);
9287 CoTaskMemFree( str
);
9288 return E_OUTOFMEMORY
;
9291 TRACE("Read string %s\n",debugstr_an(str
,len
));
9293 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9294 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9297 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9300 CoTaskMemFree( str
);
9308 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9309 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9313 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9315 static const BYTE unknown1
[12] =
9316 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9317 0xFF, 0xFF, 0xFF, 0xFF};
9318 static const BYTE unknown2
[16] =
9319 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9322 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9323 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9324 debugstr_w(szProgIDName
));
9326 /* Create a CompObj stream */
9327 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9328 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9332 /* Write CompObj Structure to stream */
9333 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9335 if( SUCCEEDED( r
) )
9336 r
= WriteClassStm( pstm
, clsid
);
9338 if( SUCCEEDED( r
) )
9339 r
= STREAM_WriteString( pstm
, lpszUserType
);
9340 if( SUCCEEDED( r
) )
9341 r
= STREAM_WriteString( pstm
, szClipName
);
9342 if( SUCCEEDED( r
) )
9343 r
= STREAM_WriteString( pstm
, szProgIDName
);
9344 if( SUCCEEDED( r
) )
9345 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9347 IStream_Release( pstm
);
9352 /***********************************************************************
9353 * WriteFmtUserTypeStg (OLE32.@)
9355 HRESULT WINAPI
WriteFmtUserTypeStg(
9356 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9360 WCHAR szwClipName
[0x40];
9362 LPWSTR wstrProgID
= NULL
;
9365 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9367 /* get the clipboard format name */
9370 n
= GetClipboardFormatNameW(cf
, szwClipName
, ARRAY_SIZE(szwClipName
));
9374 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9376 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9382 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9384 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9386 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9387 cf
? szwClipName
: NULL
, wstrProgID
);
9389 CoTaskMemFree(wstrProgID
);
9395 /******************************************************************************
9396 * ReadFmtUserTypeStg [OLE32.@]
9398 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9402 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9403 unsigned char unknown1
[12];
9404 unsigned char unknown2
[16];
9406 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9409 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9411 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9412 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9415 WARN("Failed to open stream r = %08x\n", r
);
9419 /* read the various parts of the structure */
9420 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9421 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9423 r
= ReadClassStm( stm
, &clsid
);
9427 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9431 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9435 r
= STREAM_ReadString( stm
, &szProgIDName
);
9439 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9440 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9443 /* ok, success... now we just need to store what we found */
9445 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9447 if( lplpszUserType
)
9449 *lplpszUserType
= szCLSIDName
;
9454 CoTaskMemFree( szCLSIDName
);
9455 CoTaskMemFree( szOleTypeName
);
9456 CoTaskMemFree( szProgIDName
);
9457 IStream_Release( stm
);
9462 /******************************************************************************
9463 * StgIsStorageFile [OLE32.@]
9464 * Verify if the file contains a storage object
9470 * S_OK if file has magic bytes as a storage object
9471 * S_FALSE if file is not storage
9474 StgIsStorageFile(LPCOLESTR fn
)
9480 TRACE("%s\n", debugstr_w(fn
));
9481 hf
= CreateFileW(fn
, GENERIC_READ
,
9482 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9483 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9485 if (hf
== INVALID_HANDLE_VALUE
)
9486 return STG_E_FILENOTFOUND
;
9488 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9490 WARN(" unable to read file\n");
9497 if (bytes_read
!= 8) {
9498 TRACE(" too short\n");
9502 if (!memcmp(magic
,STORAGE_magic
,8)) {
9507 TRACE(" -> Invalid header.\n");
9511 /***********************************************************************
9512 * WriteClassStm (OLE32.@)
9514 * Writes a CLSID to a stream.
9517 * pStm [I] Stream to write to.
9518 * rclsid [I] CLSID to write.
9522 * Failure: HRESULT code.
9524 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9526 TRACE("(%p,%p)\n",pStm
,rclsid
);
9528 if (!pStm
|| !rclsid
)
9529 return E_INVALIDARG
;
9531 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9534 /***********************************************************************
9535 * ReadClassStm (OLE32.@)
9537 * Reads a CLSID from a stream.
9540 * pStm [I] Stream to read from.
9541 * rclsid [O] CLSID to read.
9545 * Failure: HRESULT code.
9547 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9552 TRACE("(%p,%p)\n",pStm
,pclsid
);
9554 if (!pStm
|| !pclsid
)
9555 return E_INVALIDARG
;
9557 /* clear the output args */
9558 *pclsid
= CLSID_NULL
;
9560 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9565 if (nbByte
!= sizeof(CLSID
))
9566 return STG_E_READFAULT
;
9572 /************************************************************************
9573 * OleConvert Functions
9574 ***********************************************************************/
9576 #define OLESTREAM_ID 0x501
9577 #define OLESTREAM_MAX_STR_LEN 255
9579 /* OLESTREAM memory structure to use for Get and Put Routines */
9584 DWORD dwOleTypeNameLength
;
9585 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9586 CHAR
*pstrOleObjFileName
;
9587 DWORD dwOleObjFileNameLength
;
9588 DWORD dwMetaFileWidth
;
9589 DWORD dwMetaFileHeight
;
9590 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9593 } OLECONVERT_OLESTREAM_DATA
;
9595 /* CompObj Stream structure */
9598 BYTE byUnknown1
[12];
9600 DWORD dwCLSIDNameLength
;
9601 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9602 DWORD dwOleTypeNameLength
;
9603 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9604 DWORD dwProgIDNameLength
;
9605 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9606 BYTE byUnknown2
[16];
9607 } OLECONVERT_ISTORAGE_COMPOBJ
;
9609 /* Ole Presentation Stream structure */
9612 BYTE byUnknown1
[28];
9617 } OLECONVERT_ISTORAGE_OLEPRES
;
9620 /*************************************************************************
9621 * OLECONVERT_LoadOLE10 [Internal]
9623 * Loads the OLE10 STREAM to memory
9626 * pOleStream [I] The OLESTREAM
9627 * pData [I] Data Structure for the OLESTREAM Data
9631 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9632 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9635 * This function is used by OleConvertOLESTREAMToIStorage only.
9637 * Memory allocated for pData must be freed by the caller
9639 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStream1
)
9642 HRESULT hRes
= S_OK
;
9646 pData
->pData
= NULL
;
9647 pData
->pstrOleObjFileName
= NULL
;
9649 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9652 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9653 if(dwSize
!= sizeof(pData
->dwOleID
))
9655 hRes
= CONVERT10_E_OLESTREAM_GET
;
9657 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9659 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9670 /* Get the TypeID... more info needed for this field */
9671 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9672 if(dwSize
!= sizeof(pData
->dwTypeID
))
9674 hRes
= CONVERT10_E_OLESTREAM_GET
;
9679 if(pData
->dwTypeID
!= 0)
9681 /* Get the length of the OleTypeName */
9682 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9683 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9685 hRes
= CONVERT10_E_OLESTREAM_GET
;
9690 if(pData
->dwOleTypeNameLength
> 0)
9692 /* Get the OleTypeName */
9693 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9694 if(dwSize
!= pData
->dwOleTypeNameLength
)
9696 hRes
= CONVERT10_E_OLESTREAM_GET
;
9702 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9703 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9705 hRes
= CONVERT10_E_OLESTREAM_GET
;
9709 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9710 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9711 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9712 if(pData
->pstrOleObjFileName
)
9714 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9715 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9717 hRes
= CONVERT10_E_OLESTREAM_GET
;
9721 hRes
= CONVERT10_E_OLESTREAM_GET
;
9726 /* Get the Width of the Metafile */
9727 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9728 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9730 hRes
= CONVERT10_E_OLESTREAM_GET
;
9734 /* Get the Height of the Metafile */
9735 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9736 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9738 hRes
= CONVERT10_E_OLESTREAM_GET
;
9744 /* Get the Length of the Data */
9745 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9746 if(dwSize
!= sizeof(pData
->dwDataLength
))
9748 hRes
= CONVERT10_E_OLESTREAM_GET
;
9752 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9754 if(!bStream1
) /* if it is a second OLE stream data */
9756 pData
->dwDataLength
-= 8;
9757 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9758 if(dwSize
!= sizeof(pData
->strUnknown
))
9760 hRes
= CONVERT10_E_OLESTREAM_GET
;
9766 if(pData
->dwDataLength
> 0)
9768 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9770 /* Get Data (ex. IStorage, Metafile, or BMP) */
9773 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9774 if(dwSize
!= pData
->dwDataLength
)
9776 hRes
= CONVERT10_E_OLESTREAM_GET
;
9781 hRes
= CONVERT10_E_OLESTREAM_GET
;
9790 /*************************************************************************
9791 * OLECONVERT_SaveOLE10 [Internal]
9793 * Saves the OLE10 STREAM From memory
9796 * pData [I] Data Structure for the OLESTREAM Data
9797 * pOleStream [I] The OLESTREAM to save
9801 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9804 * This function is used by OleConvertIStorageToOLESTREAM only.
9807 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9810 HRESULT hRes
= S_OK
;
9814 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9815 if(dwSize
!= sizeof(pData
->dwOleID
))
9817 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9822 /* Set the TypeID */
9823 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9824 if(dwSize
!= sizeof(pData
->dwTypeID
))
9826 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9830 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9832 /* Set the Length of the OleTypeName */
9833 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9834 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9836 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9841 if(pData
->dwOleTypeNameLength
> 0)
9843 /* Set the OleTypeName */
9844 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9845 if(dwSize
!= pData
->dwOleTypeNameLength
)
9847 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9854 /* Set the width of the Metafile */
9855 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9856 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9858 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9864 /* Set the height of the Metafile */
9865 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9866 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9868 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9874 /* Set the length of the Data */
9875 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9876 if(dwSize
!= sizeof(pData
->dwDataLength
))
9878 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9884 if(pData
->dwDataLength
> 0)
9886 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9887 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9888 if(dwSize
!= pData
->dwDataLength
)
9890 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9898 /*************************************************************************
9899 * OLECONVERT_GetOLE20FromOLE10[Internal]
9901 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9902 * opens it, and copies the content to the dest IStorage for
9903 * OleConvertOLESTREAMToIStorage
9907 * pDestStorage [I] The IStorage to copy the data to
9908 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9909 * nBufferLength [I] The size of the buffer
9918 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9922 IStorage
*pTempStorage
;
9923 DWORD dwNumOfBytesWritten
;
9924 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9925 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9927 /* Create a temp File */
9928 GetTempPathW(MAX_PATH
, wstrTempDir
);
9929 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9930 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9932 if(hFile
!= INVALID_HANDLE_VALUE
)
9934 /* Write IStorage Data to File */
9935 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9938 /* Open and copy temp storage to the Dest Storage */
9939 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9942 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9943 IStorage_Release(pTempStorage
);
9945 DeleteFileW(wstrTempFile
);
9950 /*************************************************************************
9951 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9953 * Saves the OLE10 STREAM From memory
9956 * pStorage [I] The Src IStorage to copy
9957 * pData [I] The Dest Memory to write to.
9960 * The size in bytes allocated for pData
9963 * Memory allocated for pData must be freed by the caller
9965 * Used by OleConvertIStorageToOLESTREAM only.
9968 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9972 DWORD nDataLength
= 0;
9973 IStorage
*pTempStorage
;
9974 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9975 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9979 /* Create temp Storage */
9980 GetTempPathW(MAX_PATH
, wstrTempDir
);
9981 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9982 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9986 /* Copy Src Storage to the Temp Storage */
9987 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9988 IStorage_Release(pTempStorage
);
9990 /* Open Temp Storage as a file and copy to memory */
9991 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9992 if(hFile
!= INVALID_HANDLE_VALUE
)
9994 nDataLength
= GetFileSize(hFile
, NULL
);
9995 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9996 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9999 DeleteFileW(wstrTempFile
);
10001 return nDataLength
;
10004 /*************************************************************************
10005 * OLECONVERT_CreateCompObjStream [Internal]
10007 * Creates a "\001CompObj" is the destination IStorage if necessary.
10010 * pStorage [I] The dest IStorage to create the CompObj Stream
10012 * strOleTypeName [I] The ProgID
10016 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10019 * This function is used by OleConvertOLESTREAMToIStorage only.
10021 * The stream data is stored in the OLESTREAM and there should be
10022 * no need to recreate the stream. If the stream is manually
10023 * deleted it will attempt to create it by querying the registry.
10027 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
10030 HRESULT hStorageRes
, hRes
= S_OK
;
10031 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
10032 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10033 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
10035 static const BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10036 static const BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
10038 /* Initialize the CompObj structure */
10039 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
10040 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
10041 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
10044 /* Create a CompObj stream if it doesn't exist */
10045 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10046 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10047 if(hStorageRes
== S_OK
)
10049 /* copy the OleTypeName to the compobj struct */
10050 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
10051 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
10053 /* copy the OleTypeName to the compobj struct */
10054 /* Note: in the test made, these were Identical */
10055 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
10056 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
10058 /* Get the CLSID */
10059 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
10060 bufferW
, OLESTREAM_MAX_STR_LEN
);
10061 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
10067 /* Get the CLSID Default Name from the Registry */
10068 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
10069 if(hErr
== ERROR_SUCCESS
)
10071 char strTemp
[OLESTREAM_MAX_STR_LEN
];
10072 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
10073 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
10074 if(hErr
== ERROR_SUCCESS
)
10076 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
10082 /* Write CompObj Structure to stream */
10083 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
10085 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
10087 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
10088 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
10090 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
10092 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
10093 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
10095 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
10097 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
10098 if(IStorageCompObj
.dwProgIDNameLength
> 0)
10100 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
10102 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
10103 IStream_Release(pStream
);
10109 /*************************************************************************
10110 * OLECONVERT_CreateOlePresStream[Internal]
10112 * Creates the "\002OlePres000" Stream with the Metafile data
10115 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10116 * dwExtentX [I] Width of the Metafile
10117 * dwExtentY [I] Height of the Metafile
10118 * pData [I] Metafile data
10119 * dwDataLength [I] Size of the Metafile data
10123 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10126 * This function is used by OleConvertOLESTREAMToIStorage only.
10129 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10133 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10134 static const BYTE pOlePresStreamHeader
[] =
10136 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10137 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10138 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10139 0x00, 0x00, 0x00, 0x00
10142 static const BYTE pOlePresStreamHeaderEmpty
[] =
10144 0x00, 0x00, 0x00, 0x00,
10145 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10146 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10147 0x00, 0x00, 0x00, 0x00
10150 /* Create the OlePres000 Stream */
10151 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10152 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10157 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10159 memset(&OlePres
, 0, sizeof(OlePres
));
10160 /* Do we have any metafile data to save */
10161 if(dwDataLength
> 0)
10163 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10164 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10168 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10169 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10171 /* Set width and height of the metafile */
10172 OlePres
.dwExtentX
= dwExtentX
;
10173 OlePres
.dwExtentY
= -dwExtentY
;
10175 /* Set Data and Length */
10176 if(dwDataLength
> sizeof(METAFILEPICT16
))
10178 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10179 OlePres
.pData
= &(pData
[8]);
10181 /* Save OlePres000 Data to Stream */
10182 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10183 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10184 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10185 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10186 if(OlePres
.dwSize
> 0)
10188 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10190 IStream_Release(pStream
);
10194 /*************************************************************************
10195 * OLECONVERT_CreateOle10NativeStream [Internal]
10197 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10200 * pStorage [I] Dest storage to create the stream in
10201 * pData [I] Ole10 Native Data (ex. bmp)
10202 * dwDataLength [I] Size of the Ole10 Native Data
10208 * This function is used by OleConvertOLESTREAMToIStorage only.
10210 * Might need to verify the data and return appropriate error message
10213 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10217 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10219 /* Create the Ole10Native Stream */
10220 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10221 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10225 /* Write info to stream */
10226 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10227 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10228 IStream_Release(pStream
);
10233 /*************************************************************************
10234 * OLECONVERT_GetOLE10ProgID [Internal]
10236 * Finds the ProgID (or OleTypeID) from the IStorage
10239 * pStorage [I] The Src IStorage to get the ProgID
10240 * strProgID [I] the ProgID string to get
10241 * dwSize [I] the size of the string
10245 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10248 * This function is used by OleConvertIStorageToOLESTREAM only.
10252 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10256 LARGE_INTEGER iSeekPos
;
10257 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10258 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10260 /* Open the CompObj Stream */
10261 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10262 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10266 /*Get the OleType from the CompObj Stream */
10267 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10268 iSeekPos
.u
.HighPart
= 0;
10270 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10271 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10272 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10273 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10274 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10275 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10276 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10278 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10281 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10283 IStream_Release(pStream
);
10288 LPOLESTR wstrProgID
;
10290 /* Get the OleType from the registry */
10291 REFCLSID clsid
= &(stat
.clsid
);
10292 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10293 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10296 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10297 CoTaskMemFree(wstrProgID
);
10304 /*************************************************************************
10305 * OLECONVERT_GetOle10PresData [Internal]
10307 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10310 * pStorage [I] Src IStroage
10311 * pOleStream [I] Dest OleStream Mem Struct
10317 * This function is used by OleConvertIStorageToOLESTREAM only.
10319 * Memory allocated for pData must be freed by the caller
10323 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10328 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10330 /* Initialize Default data for OLESTREAM */
10331 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10332 pOleStreamData
[0].dwTypeID
= 2;
10333 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10334 pOleStreamData
[1].dwTypeID
= 0;
10335 pOleStreamData
[0].dwMetaFileWidth
= 0;
10336 pOleStreamData
[0].dwMetaFileHeight
= 0;
10337 pOleStreamData
[0].pData
= NULL
;
10338 pOleStreamData
[1].pData
= NULL
;
10340 /* Open Ole10Native Stream */
10341 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10342 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10346 /* Read Size and Data */
10347 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10348 if(pOleStreamData
->dwDataLength
> 0)
10350 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10351 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10353 IStream_Release(pStream
);
10359 /*************************************************************************
10360 * OLECONVERT_GetOle20PresData[Internal]
10362 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10365 * pStorage [I] Src IStroage
10366 * pOleStreamData [I] Dest OleStream Mem Struct
10372 * This function is used by OleConvertIStorageToOLESTREAM only.
10374 * Memory allocated for pData must be freed by the caller
10376 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10380 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10381 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10383 /* Initialize Default data for OLESTREAM */
10384 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10385 pOleStreamData
[0].dwTypeID
= 2;
10386 pOleStreamData
[0].dwMetaFileWidth
= 0;
10387 pOleStreamData
[0].dwMetaFileHeight
= 0;
10388 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10389 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10390 pOleStreamData
[1].dwTypeID
= 0;
10391 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10392 pOleStreamData
[1].strOleTypeName
[0] = 0;
10393 pOleStreamData
[1].dwMetaFileWidth
= 0;
10394 pOleStreamData
[1].dwMetaFileHeight
= 0;
10395 pOleStreamData
[1].pData
= NULL
;
10396 pOleStreamData
[1].dwDataLength
= 0;
10399 /* Open OlePress000 stream */
10400 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10401 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10404 LARGE_INTEGER iSeekPos
;
10405 METAFILEPICT16 MetaFilePict
;
10406 static const char strMetafilePictName
[] = "METAFILEPICT";
10408 /* Set the TypeID for a Metafile */
10409 pOleStreamData
[1].dwTypeID
= 5;
10411 /* Set the OleTypeName to Metafile */
10412 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10413 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10415 iSeekPos
.u
.HighPart
= 0;
10416 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10418 /* Get Presentation Data */
10419 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10420 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10421 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10422 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10424 /*Set width and Height */
10425 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10426 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10427 if(olePress
.dwSize
> 0)
10430 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10432 /* Set MetaFilePict struct */
10433 MetaFilePict
.mm
= 8;
10434 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10435 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10436 MetaFilePict
.hMF
= 0;
10438 /* Get Metafile Data */
10439 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10440 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10441 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10443 IStream_Release(pStream
);
10447 /*************************************************************************
10448 * OleConvertOLESTREAMToIStorage [OLE32.@]
10450 * Read info on MSDN
10453 * DVTARGETDEVICE parameter is not handled
10454 * Still unsure of some mem fields for OLE 10 Stream
10455 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10456 * and "\001OLE" streams
10459 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10460 LPOLESTREAM pOleStream
,
10462 const DVTARGETDEVICE
* ptd
)
10466 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10468 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10470 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10474 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10477 if(pstg
== NULL
|| pOleStream
== NULL
)
10479 hRes
= E_INVALIDARG
;
10484 /* Load the OLESTREAM to Memory */
10485 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10490 /* Load the OLESTREAM to Memory (part 2)*/
10491 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10497 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10499 /* Do we have the IStorage Data in the OLESTREAM */
10500 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10502 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10503 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10507 /* It must be an original OLE 1.0 source */
10508 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10513 /* It must be an original OLE 1.0 source */
10514 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10517 /* Create CompObj Stream if necessary */
10518 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10521 /*Create the Ole Stream if necessary */
10522 STORAGE_CreateOleStream(pstg
, 0);
10527 /* Free allocated memory */
10528 for(i
=0; i
< 2; i
++)
10530 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10531 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10532 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10537 /*************************************************************************
10538 * OleConvertIStorageToOLESTREAM [OLE32.@]
10540 * Read info on MSDN
10542 * Read info on MSDN
10545 * Still unsure of some mem fields for OLE 10 Stream
10546 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10547 * and "\001OLE" streams.
10550 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10552 LPOLESTREAM pOleStream
)
10555 HRESULT hRes
= S_OK
;
10557 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10558 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10560 TRACE("%p %p\n", pstg
, pOleStream
);
10562 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10564 if(pstg
== NULL
|| pOleStream
== NULL
)
10566 hRes
= E_INVALIDARG
;
10570 /* Get the ProgID */
10571 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10572 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10576 /* Was it originally Ole10 */
10577 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10580 IStream_Release(pStream
);
10581 /* Get Presentation Data for Ole10Native */
10582 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10586 /* Get Presentation Data (OLE20) */
10587 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10590 /* Save OLESTREAM */
10591 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10594 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10599 /* Free allocated memory */
10600 for(i
=0; i
< 2; i
++)
10602 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10608 enum stream_1ole_flags
{
10609 OleStream_LinkedObject
= 0x00000001,
10610 OleStream_Convert
= 0x00000004
10613 /***********************************************************************
10614 * GetConvertStg (OLE32.@)
10616 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10618 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10619 static const DWORD version_magic
= 0x02000001;
10624 TRACE("%p\n", stg
);
10626 if (!stg
) return E_INVALIDARG
;
10628 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10629 if (FAILED(hr
)) return hr
;
10631 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10632 IStream_Release(stream
);
10633 if (FAILED(hr
)) return hr
;
10635 if (header
[0] != version_magic
)
10637 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10641 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10644 /***********************************************************************
10645 * SetConvertStg (OLE32.@)
10647 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10649 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10650 DWORD flags
= convert
? OleStream_Convert
: 0;
10655 TRACE("(%p, %d)\n", storage
, convert
);
10657 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10660 if (hr
!= STG_E_FILENOTFOUND
)
10663 return STORAGE_CreateOleStream(storage
, flags
);
10666 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10669 IStream_Release(stream
);
10673 /* update flag if differs */
10674 if ((header
[1] ^ flags
) & OleStream_Convert
)
10676 LARGE_INTEGER pos
= {{0}};
10678 if (header
[1] & OleStream_Convert
)
10679 flags
= header
[1] & ~OleStream_Convert
;
10681 flags
= header
[1] | OleStream_Convert
;
10683 pos
.QuadPart
= sizeof(DWORD
);
10684 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10687 IStream_Release(stream
);
10691 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10694 IStream_Release(stream
);