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.
44 #include "wine/debug.h"
46 #include "storage32.h"
47 #include "ole2.h" /* For Write/ReadClassStm */
50 #include "wine/wingdi16.h"
51 #include "compobj_private.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
57 * These are signatures to detect the type of Document file.
59 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
60 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
62 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
65 /****************************************************************************
66 * StorageInternalImpl definitions.
68 * Definition of the implementation structure for the IStorage interface.
69 * This one implements the IStorage interface for storage that are
70 * inside another storage.
72 typedef struct StorageInternalImpl
74 struct StorageBaseImpl base
;
77 * Entry in the parent's stream tracking list
79 struct list ParentListEntry
;
81 StorageBaseImpl
*parentStorage
;
82 } StorageInternalImpl
;
84 static const IStorageVtbl StorageInternalImpl_Vtbl
;
85 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
*,DWORD
,DirRef
);
87 typedef struct TransactedDirEntry
89 /* If applicable, a reference to the original DirEntry in the transacted
90 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
91 DirRef transactedParentEntry
;
93 /* True if this entry is being used. */
96 /* True if data is up to date. */
99 /* True if this entry has been modified. */
102 /* True if this entry's stream has been modified. */
105 /* True if this entry has been deleted in the transacted storage, but the
106 * delete has not yet been committed. */
109 /* If this entry's stream has been modified, a reference to where the stream
110 * is stored in the snapshot file. */
113 /* This directory entry's data, including any changes that have been made. */
116 /* A reference to the parent of this node. This is only valid while we are
117 * committing changes. */
120 /* A reference to a newly-created entry in the transacted parent. This is
121 * always equal to transactedParentEntry except when committing changes. */
122 DirRef newTransactedParentEntry
;
123 } TransactedDirEntry
;
126 /****************************************************************************
127 * Transacted storage object.
129 typedef struct TransactedSnapshotImpl
131 struct StorageBaseImpl base
;
134 * Modified streams are temporarily saved to the scratch file.
136 StorageBaseImpl
*scratch
;
138 /* The directory structure is kept here, so that we can track how these
139 * entries relate to those in the parent storage. */
140 TransactedDirEntry
*entries
;
142 ULONG firstFreeEntry
;
145 * Changes are committed to the transacted parent.
147 StorageBaseImpl
*transactedParent
;
149 /* The transaction signature from when we last committed */
150 ULONG lastTransactionSig
;
151 } TransactedSnapshotImpl
;
153 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
154 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*,BOOL
,StorageBaseImpl
**);
156 typedef struct TransactedSharedImpl
158 struct StorageBaseImpl base
;
161 * Snapshot and uncommitted changes go here.
163 TransactedSnapshotImpl
*scratch
;
166 * Changes are committed to the transacted parent.
168 StorageBaseImpl
*transactedParent
;
170 /* The transaction signature from when we last committed */
171 ULONG lastTransactionSig
;
172 } TransactedSharedImpl
;
175 /****************************************************************************
176 * BlockChainStream definitions.
178 * The BlockChainStream class is a utility class that is used to create an
179 * abstraction of the big block chains in the storage file.
184 /* This represents a range of blocks that happen reside in consecutive sectors. */
190 typedef struct BlockChainBlock
196 BYTE data
[MAX_BIG_BLOCK_SIZE
];
199 struct BlockChainStream
201 StorageImpl
* parentStorage
;
202 ULONG
* headOfStreamPlaceHolder
;
203 DirRef ownerDirEntry
;
204 struct BlockChainRun
* indexCache
;
206 ULONG indexCacheSize
;
207 BlockChainBlock cachedBlocks
[2];
213 /* Returns the number of blocks that comprises this chain.
214 * This is not the size of the stream as the last block may not be full!
216 static inline ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
218 return This
->numBlocks
;
221 static BlockChainStream
* BlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
222 static void BlockChainStream_Destroy(BlockChainStream
*);
223 static HRESULT
BlockChainStream_ReadAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
224 static HRESULT
BlockChainStream_WriteAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
225 static HRESULT
BlockChainStream_Flush(BlockChainStream
*);
226 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
*);
227 static BOOL
BlockChainStream_SetSize(BlockChainStream
*,ULARGE_INTEGER
);
230 /****************************************************************************
231 * SmallBlockChainStream definitions.
233 * The SmallBlockChainStream class is a utility class that is used to create an
234 * abstraction of the small block chains in the storage file.
237 struct SmallBlockChainStream
239 StorageImpl
* parentStorage
;
240 DirRef ownerDirEntry
;
241 ULONG
* headOfStreamPlaceHolder
;
244 static SmallBlockChainStream
* SmallBlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
245 static void SmallBlockChainStream_Destroy(SmallBlockChainStream
*);
246 static HRESULT
SmallBlockChainStream_ReadAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
247 static HRESULT
SmallBlockChainStream_WriteAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
248 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
*);
249 static BOOL
SmallBlockChainStream_SetSize(SmallBlockChainStream
*,ULARGE_INTEGER
);
252 /************************************************************************
254 ***********************************************************************/
256 /************************************************************************
257 * This method validates an STGM parameter that can contain the values below
259 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
260 * The stgm values contained in 0xffff0000 are bitmasks.
262 * STGM_DIRECT 0x00000000
263 * STGM_TRANSACTED 0x00010000
264 * STGM_SIMPLE 0x08000000
266 * STGM_READ 0x00000000
267 * STGM_WRITE 0x00000001
268 * STGM_READWRITE 0x00000002
270 * STGM_SHARE_DENY_NONE 0x00000040
271 * STGM_SHARE_DENY_READ 0x00000030
272 * STGM_SHARE_DENY_WRITE 0x00000020
273 * STGM_SHARE_EXCLUSIVE 0x00000010
275 * STGM_PRIORITY 0x00040000
276 * STGM_DELETEONRELEASE 0x04000000
278 * STGM_CREATE 0x00001000
279 * STGM_CONVERT 0x00020000
280 * STGM_FAILIFTHERE 0x00000000
282 * STGM_NOSCRATCH 0x00100000
283 * STGM_NOSNAPSHOT 0x00200000
285 static HRESULT
validateSTGM(DWORD stgm
)
287 DWORD access
= STGM_ACCESS_MODE(stgm
);
288 DWORD share
= STGM_SHARE_MODE(stgm
);
289 DWORD create
= STGM_CREATE_MODE(stgm
);
291 if (stgm
&~STGM_KNOWN_FLAGS
)
293 ERR("unknown flags %#lx\n", stgm
);
309 case STGM_SHARE_DENY_NONE
:
310 case STGM_SHARE_DENY_READ
:
311 case STGM_SHARE_DENY_WRITE
:
312 case STGM_SHARE_EXCLUSIVE
:
315 if (!(stgm
& STGM_TRANSACTED
))
325 case STGM_FAILIFTHERE
:
332 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
334 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
338 * STGM_CREATE | STGM_CONVERT
339 * if both are false, STGM_FAILIFTHERE is set to TRUE
341 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
345 * STGM_NOSCRATCH requires STGM_TRANSACTED
347 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
351 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
352 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
354 if ( (stgm
& STGM_NOSNAPSHOT
) &&
355 (!(stgm
& STGM_TRANSACTED
) ||
356 share
== STGM_SHARE_EXCLUSIVE
||
357 share
== STGM_SHARE_DENY_WRITE
) )
363 /************************************************************************
364 * GetShareModeFromSTGM
366 * This method will return a share mode flag from a STGM value.
367 * The STGM value is assumed valid.
369 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
371 switch (STGM_SHARE_MODE(stgm
))
374 assert(stgm
& STGM_TRANSACTED
);
376 case STGM_SHARE_DENY_NONE
:
377 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
378 case STGM_SHARE_DENY_READ
:
379 return FILE_SHARE_WRITE
;
380 case STGM_SHARE_DENY_WRITE
:
381 case STGM_SHARE_EXCLUSIVE
:
382 return FILE_SHARE_READ
;
384 ERR("Invalid share mode!\n");
389 /************************************************************************
390 * GetAccessModeFromSTGM
392 * This method will return an access mode flag from a STGM value.
393 * The STGM value is assumed valid.
395 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
397 switch (STGM_ACCESS_MODE(stgm
))
403 return GENERIC_READ
| GENERIC_WRITE
;
405 ERR("Invalid access mode!\n");
410 /************************************************************************
411 * GetCreationModeFromSTGM
413 * This method will return a creation mode flag from a STGM value.
414 * The STGM value is assumed valid.
416 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
418 switch(STGM_CREATE_MODE(stgm
))
421 return CREATE_ALWAYS
;
423 FIXME("STGM_CONVERT not implemented!\n");
425 case STGM_FAILIFTHERE
:
428 ERR("Invalid create mode!\n");
434 /************************************************************************
435 * IDirectWriterLock implementation
436 ***********************************************************************/
438 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
440 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
443 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
445 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
446 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
449 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
451 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
452 return IStorage_AddRef(&This
->IStorage_iface
);
455 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
457 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
458 return IStorage_Release(&This
->IStorage_iface
);
461 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
463 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
464 FIXME("%p, %ld: stub\n", This
, timeout
);
468 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
470 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
471 FIXME("(%p): stub\n", This
);
475 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
477 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
478 FIXME("(%p): stub\n", This
);
482 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
484 directwriterlock_QueryInterface
,
485 directwriterlock_AddRef
,
486 directwriterlock_Release
,
487 directwriterlock_WaitForWriteAccess
,
488 directwriterlock_ReleaseWriteAccess
,
489 directwriterlock_HaveWriteAccess
493 /************************************************************************
494 * StorageBaseImpl implementation : Tree helper functions
495 ***********************************************************************/
497 /****************************************************************************
501 * Case insensitive comparison of DirEntry.name by first considering
504 * Returns <0 when name1 < name2
505 * >0 when name1 > name2
506 * 0 when name1 == name2
508 static LONG
entryNameCmp(
509 const OLECHAR
*name1
,
510 const OLECHAR
*name2
)
512 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
514 while (diff
== 0 && *name1
!= 0)
517 * We compare the string themselves only when they are of the same length
519 diff
= towupper(*name1
++) - towupper(*name2
++);
525 /****************************************************************************
529 * Find and read the element of a storage with the given name.
531 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
532 const OLECHAR
*name
, DirEntry
*data
)
536 /* Read the storage entry to find the root of the tree. */
537 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
539 currentEntry
= data
->dirRootEntry
;
541 while (currentEntry
!= DIRENTRY_NULL
)
545 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
547 cmp
= entryNameCmp(name
, data
->name
);
554 currentEntry
= data
->leftChild
;
557 currentEntry
= data
->rightChild
;
563 /****************************************************************************
567 * Find and read the binary tree parent of the element with the given name.
569 * If there is no such element, find a place where it could be inserted and
570 * return STG_E_FILENOTFOUND.
572 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
573 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
579 /* Read the storage entry to find the root of the tree. */
580 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
582 *parentEntry
= storageEntry
;
583 *relation
= DIRENTRY_RELATION_DIR
;
585 childEntry
= parentData
->dirRootEntry
;
587 while (childEntry
!= DIRENTRY_NULL
)
591 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
593 cmp
= entryNameCmp(childName
, childData
.name
);
601 *parentData
= childData
;
602 *parentEntry
= childEntry
;
603 *relation
= DIRENTRY_RELATION_PREVIOUS
;
605 childEntry
= parentData
->leftChild
;
610 *parentData
= childData
;
611 *parentEntry
= childEntry
;
612 *relation
= DIRENTRY_RELATION_NEXT
;
614 childEntry
= parentData
->rightChild
;
618 if (childEntry
== DIRENTRY_NULL
)
619 return STG_E_FILENOTFOUND
;
624 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
628 case DIRENTRY_RELATION_PREVIOUS
:
629 entry
->leftChild
= new_target
;
631 case DIRENTRY_RELATION_NEXT
:
632 entry
->rightChild
= new_target
;
634 case DIRENTRY_RELATION_DIR
:
635 entry
->dirRootEntry
= new_target
;
642 /****************************************************************************
646 * Add a directory entry to a storage
648 static HRESULT
insertIntoTree(
649 StorageBaseImpl
*This
,
650 DirRef parentStorageIndex
,
651 DirRef newEntryIndex
)
653 DirEntry currentEntry
;
657 * Read the inserted entry
659 StorageBaseImpl_ReadDirEntry(This
,
664 * Read the storage entry
666 StorageBaseImpl_ReadDirEntry(This
,
670 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
673 * The root storage contains some element, therefore, start the research
674 * for the appropriate location.
677 DirRef current
, next
, previous
, currentEntryId
;
680 * Keep a reference to the root of the storage's element tree
682 currentEntryId
= currentEntry
.dirRootEntry
;
687 StorageBaseImpl_ReadDirEntry(This
,
688 currentEntry
.dirRootEntry
,
691 previous
= currentEntry
.leftChild
;
692 next
= currentEntry
.rightChild
;
693 current
= currentEntryId
;
697 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
701 if (previous
!= DIRENTRY_NULL
)
703 StorageBaseImpl_ReadDirEntry(This
,
710 currentEntry
.leftChild
= newEntryIndex
;
711 StorageBaseImpl_WriteDirEntry(This
,
719 if (next
!= DIRENTRY_NULL
)
721 StorageBaseImpl_ReadDirEntry(This
,
728 currentEntry
.rightChild
= newEntryIndex
;
729 StorageBaseImpl_WriteDirEntry(This
,
738 * Trying to insert an item with the same name in the
741 return STG_E_FILEALREADYEXISTS
;
744 previous
= currentEntry
.leftChild
;
745 next
= currentEntry
.rightChild
;
751 * The storage is empty, make the new entry the root of its element tree
753 currentEntry
.dirRootEntry
= newEntryIndex
;
754 StorageBaseImpl_WriteDirEntry(This
,
762 /*************************************************************************
766 * This method removes a directory entry from its parent storage tree without
767 * freeing any resources attached to it.
769 static HRESULT
removeFromTree(
770 StorageBaseImpl
*This
,
771 DirRef parentStorageIndex
,
774 DirEntry entryToDelete
;
775 DirEntry parentEntry
;
776 DirRef parentEntryRef
;
777 ULONG typeOfRelation
;
780 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
786 * Find the element that links to the one we want to delete.
788 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
789 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
794 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
797 * Replace the deleted entry with its left child
799 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
801 hr
= StorageBaseImpl_WriteDirEntry(
810 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
813 * We need to reinsert the right child somewhere. We already know it and
814 * its children are greater than everything in the left tree, so we
815 * insert it at the rightmost point in the left tree.
817 DirRef newRightChildParent
= entryToDelete
.leftChild
;
818 DirEntry newRightChildParentEntry
;
822 hr
= StorageBaseImpl_ReadDirEntry(
825 &newRightChildParentEntry
);
831 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
832 newRightChildParent
= newRightChildParentEntry
.rightChild
;
833 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
835 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
837 hr
= StorageBaseImpl_WriteDirEntry(
840 &newRightChildParentEntry
);
850 * Replace the deleted entry with its right child
852 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
854 hr
= StorageBaseImpl_WriteDirEntry(
868 /************************************************************************
869 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
870 ***********************************************************************/
873 * IEnumSTATSTGImpl definitions.
875 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
876 * This class allows iterating through the content of a storage and finding
877 * specific items inside it.
879 struct IEnumSTATSTGImpl
881 IEnumSTATSTG IEnumSTATSTG_iface
;
883 LONG ref
; /* Reference count */
884 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
885 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
887 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
890 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
892 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
895 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
897 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
898 HeapFree(GetProcessHeap(), 0, This
);
901 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
906 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
908 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
915 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
916 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
918 *ppvObject
= &This
->IEnumSTATSTG_iface
;
919 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
920 TRACE("<-- %p\n", *ppvObject
);
924 TRACE("<-- E_NOINTERFACE\n");
925 return E_NOINTERFACE
;
928 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
931 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
932 return InterlockedIncrement(&This
->ref
);
935 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
938 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
942 newRef
= InterlockedDecrement(&This
->ref
);
946 IEnumSTATSTGImpl_Destroy(This
);
952 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
953 IEnumSTATSTGImpl
* This
,
956 DirRef result
= DIRENTRY_NULL
;
960 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
962 TRACE("%p,%p\n", This
, ref
);
964 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
965 This
->parentStorage
->storageDirEntry
, &entry
);
966 searchNode
= entry
.dirRootEntry
;
968 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
970 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
974 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
978 searchNode
= entry
.rightChild
;
983 memcpy(result_name
, entry
.name
, sizeof(result_name
));
984 searchNode
= entry
.leftChild
;
992 if (result
!= DIRENTRY_NULL
)
993 memcpy(This
->name
, result_name
, sizeof(result_name
));
996 TRACE("<-- %#lx\n", hr
);
1000 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
1001 IEnumSTATSTG
* iface
,
1004 ULONG
* pceltFetched
)
1006 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1008 DirEntry currentEntry
;
1009 STATSTG
* currentReturnStruct
= rgelt
;
1010 ULONG objectFetched
= 0;
1011 DirRef currentSearchNode
;
1014 TRACE("%p, %lu, %p, %p.\n", iface
, celt
, rgelt
, pceltFetched
);
1016 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
1017 return E_INVALIDARG
;
1019 if (This
->parentStorage
->reverted
)
1021 TRACE("<-- STG_E_REVERTED\n");
1022 return STG_E_REVERTED
;
1026 * To avoid the special case, get another pointer to a ULONG value if
1027 * the caller didn't supply one.
1029 if (pceltFetched
==0)
1030 pceltFetched
= &objectFetched
;
1033 * Start the iteration, we will iterate until we hit the end of the
1034 * linked list or until we hit the number of items to iterate through
1038 while ( *pceltFetched
< celt
)
1040 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1042 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1044 memset(currentReturnStruct
, 0, sizeof(*currentReturnStruct
));
1049 * Read the entry from the storage.
1051 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
1054 if (FAILED(hr
)) break;
1057 * Copy the information to the return buffer.
1059 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
1060 currentReturnStruct
,
1065 * Step to the next item in the iteration
1068 currentReturnStruct
++;
1071 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
1074 TRACE("<-- %#lx (asked %lu, got %lu)\n", hr
, celt
, *pceltFetched
);
1079 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
1080 IEnumSTATSTG
* iface
,
1083 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1085 ULONG objectFetched
= 0;
1086 DirRef currentSearchNode
;
1089 TRACE("%p, %lu.\n", iface
, celt
);
1091 if (This
->parentStorage
->reverted
)
1093 TRACE("<-- STG_E_REVERTED\n");
1094 return STG_E_REVERTED
;
1097 while ( (objectFetched
< celt
) )
1099 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1101 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1107 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
1110 TRACE("<-- %#lx\n", hr
);
1114 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
1115 IEnumSTATSTG
* iface
)
1117 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1119 TRACE("%p\n", iface
);
1121 if (This
->parentStorage
->reverted
)
1123 TRACE("<-- STG_E_REVERTED\n");
1124 return STG_E_REVERTED
;
1132 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
*,DirRef
);
1134 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
1135 IEnumSTATSTG
* iface
,
1136 IEnumSTATSTG
** ppenum
)
1138 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1139 IEnumSTATSTGImpl
* newClone
;
1141 TRACE("%p,%p\n", iface
, ppenum
);
1143 if (This
->parentStorage
->reverted
)
1145 TRACE("<-- STG_E_REVERTED\n");
1146 return STG_E_REVERTED
;
1150 return E_INVALIDARG
;
1152 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
1153 This
->storageDirEntry
);
1157 return E_OUTOFMEMORY
;
1161 * The new clone enumeration must point to the same current node as
1164 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
1166 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
1172 * Virtual function table for the IEnumSTATSTGImpl class.
1174 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
1176 IEnumSTATSTGImpl_QueryInterface
,
1177 IEnumSTATSTGImpl_AddRef
,
1178 IEnumSTATSTGImpl_Release
,
1179 IEnumSTATSTGImpl_Next
,
1180 IEnumSTATSTGImpl_Skip
,
1181 IEnumSTATSTGImpl_Reset
,
1182 IEnumSTATSTGImpl_Clone
1185 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
1186 StorageBaseImpl
* parentStorage
,
1187 DirRef storageDirEntry
)
1189 IEnumSTATSTGImpl
* newEnumeration
;
1191 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
1195 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
1196 newEnumeration
->ref
= 1;
1197 newEnumeration
->name
[0] = 0;
1200 * We want to nail-down the reference to the storage in case the
1201 * enumeration out-lives the storage in the client application.
1203 newEnumeration
->parentStorage
= parentStorage
;
1204 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
1206 newEnumeration
->storageDirEntry
= storageDirEntry
;
1209 return newEnumeration
;
1213 /************************************************************************
1214 * StorageBaseImpl implementation
1215 ***********************************************************************/
1217 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
1219 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
1222 /************************************************************************
1223 * StorageBaseImpl_QueryInterface (IUnknown)
1225 * This method implements the common QueryInterface for all IStorage
1226 * implementations contained in this file.
1228 * See Windows documentation for more details on IUnknown methods.
1230 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
1235 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1237 TRACE("%p,%s,%p\n", iface
, debugstr_guid(riid
), ppvObject
);
1240 return E_INVALIDARG
;
1244 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
1245 IsEqualGUID(&IID_IStorage
, riid
))
1247 *ppvObject
= &This
->IStorage_iface
;
1249 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
1251 *ppvObject
= &This
->IPropertySetStorage_iface
;
1253 /* locking interface is reported for writer only */
1254 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
1256 *ppvObject
= &This
->IDirectWriterLock_iface
;
1260 TRACE("<-- E_NOINTERFACE\n");
1261 return E_NOINTERFACE
;
1264 IStorage_AddRef(iface
);
1265 TRACE("<-- %p\n", *ppvObject
);
1269 /************************************************************************
1270 * StorageBaseImpl_AddRef (IUnknown)
1272 * This method implements the common AddRef for all IStorage
1273 * implementations contained in this file.
1275 * See Windows documentation for more details on IUnknown methods.
1277 static ULONG WINAPI
StorageBaseImpl_AddRef(
1280 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1281 ULONG ref
= InterlockedIncrement(&This
->ref
);
1283 TRACE("%p, refcount %lu.\n", iface
, ref
);
1288 /************************************************************************
1289 * StorageBaseImpl_Release (IUnknown)
1291 * This method implements the common Release for all IStorage
1292 * implementations contained in this file.
1294 * See Windows documentation for more details on IUnknown methods.
1296 static ULONG WINAPI
StorageBaseImpl_Release(
1299 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1301 ULONG ref
= InterlockedDecrement(&This
->ref
);
1303 TRACE("%p, refcount %lu.\n", iface
, ref
);
1308 * Since we are using a system of base-classes, we want to call the
1309 * destructor of the appropriate derived class. To do this, we are
1310 * using virtual functions to implement the destructor.
1312 StorageBaseImpl_Destroy(This
);
1318 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1319 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1320 SNB snbExclude
, IStorage
*pstgDest
);
1322 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1323 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1324 SNB snbExclude
, IStorage
*pstgDest
)
1330 IStream
*pstrChild
, *pstrTmp
;
1333 if (srcEntry
== DIRENTRY_NULL
)
1336 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1343 WCHAR
**snb
= snbExclude
;
1345 while ( *snb
!= NULL
&& !skip
)
1347 if ( wcscmp(data
.name
, *snb
) == 0 )
1355 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1358 * create a new storage in destination storage
1360 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1361 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1366 * if it already exist, don't create a new one use this one
1368 if (hr
== STG_E_FILEALREADYEXISTS
)
1370 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1371 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1372 NULL
, 0, &pstgTmp
);
1377 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1378 skip_stream
, NULL
, pstgTmp
);
1380 IStorage_Release(pstgTmp
);
1383 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1386 * create a new stream in destination storage. If the stream already
1387 * exist, it will be deleted and a new one will be created.
1389 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1390 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1394 * open child stream storage. This operation must succeed even if the
1395 * stream is already open, so we use internal functions to do it.
1399 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1403 pstrChild
= &streamimpl
->IStream_iface
;
1405 IStream_AddRef(pstrChild
);
1417 * Get the size of the source stream
1419 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1422 * Set the size of the destination stream.
1424 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1429 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1432 IStream_Release( pstrChild
);
1435 IStream_Release( pstrTmp
);
1441 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1442 skip_stream
, snbExclude
, pstgDest
);
1445 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1446 skip_stream
, snbExclude
, pstgDest
);
1448 TRACE("<-- %#lx\n", hr
);
1452 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1454 StgStreamImpl
*strm
;
1456 TRACE("%p, %ld.\n", stg
, streamEntry
);
1458 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1460 if (strm
->dirEntry
== streamEntry
)
1469 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1471 StorageInternalImpl
*childstg
;
1473 TRACE("%p, %ld.\n", stg
, storageEntry
);
1475 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1477 if (childstg
->base
.storageDirEntry
== storageEntry
)
1486 /************************************************************************
1487 * StorageBaseImpl_OpenStream (IStorage)
1489 * This method will open the specified stream object from the current storage.
1491 * See Windows documentation for more details on IStorage methods.
1493 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
1495 const OLECHAR
* pwcsName
, /* [string][in] */
1496 void* reserved1
, /* [unique][in] */
1497 DWORD grfMode
, /* [in] */
1498 DWORD reserved2
, /* [in] */
1499 IStream
** ppstm
) /* [out] */
1501 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1502 StgStreamImpl
* newStream
;
1503 DirEntry currentEntry
;
1504 DirRef streamEntryRef
;
1505 HRESULT res
= STG_E_UNKNOWN
;
1507 TRACE("%p, %s, %p, %#lx, %ld, %p.\n", iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1509 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1517 if ( FAILED( validateSTGM(grfMode
) ) ||
1518 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1520 res
= STG_E_INVALIDFLAG
;
1527 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1529 res
= STG_E_INVALIDFUNCTION
;
1535 res
= STG_E_REVERTED
;
1540 * Check that we're compatible with the parent's storage mode, but
1541 * only if we are not in transacted mode
1543 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1544 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1546 res
= STG_E_INVALIDFLAG
;
1552 * Search for the element with the given name
1554 streamEntryRef
= findElement(
1556 This
->storageDirEntry
,
1561 * If it was found, construct the stream object and return a pointer to it.
1563 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1564 (currentEntry
.stgType
==STGTY_STREAM
) )
1566 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1568 /* A single stream cannot be opened a second time. */
1569 res
= STG_E_ACCESSDENIED
;
1573 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1577 newStream
->grfMode
= grfMode
;
1578 *ppstm
= &newStream
->IStream_iface
;
1580 IStream_AddRef(*ppstm
);
1586 res
= E_OUTOFMEMORY
;
1590 res
= STG_E_FILENOTFOUND
;
1594 TRACE("<-- IStream %p\n", *ppstm
);
1595 TRACE("<-- %#lx\n", res
);
1599 /************************************************************************
1600 * StorageBaseImpl_OpenStorage (IStorage)
1602 * This method will open a new storage object from the current storage.
1604 * See Windows documentation for more details on IStorage methods.
1606 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1608 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1609 IStorage
* pstgPriority
, /* [unique][in] */
1610 DWORD grfMode
, /* [in] */
1611 SNB snbExclude
, /* [unique][in] */
1612 DWORD reserved
, /* [in] */
1613 IStorage
** ppstg
) /* [out] */
1615 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1616 StorageInternalImpl
* newStorage
;
1617 StorageBaseImpl
* newTransactedStorage
;
1618 DirEntry currentEntry
;
1619 DirRef storageEntryRef
;
1620 HRESULT res
= STG_E_UNKNOWN
;
1622 TRACE("%p, %s, %p, %#lx, %p, %ld, %p.\n", iface
, debugstr_w(pwcsName
), pstgPriority
,
1623 grfMode
, snbExclude
, reserved
, ppstg
);
1625 if ((pwcsName
==NULL
) || (ppstg
==0) )
1631 if (This
->openFlags
& STGM_SIMPLE
)
1633 res
= STG_E_INVALIDFUNCTION
;
1638 if (snbExclude
!= NULL
)
1640 res
= STG_E_INVALIDPARAMETER
;
1644 if ( FAILED( validateSTGM(grfMode
) ))
1646 res
= STG_E_INVALIDFLAG
;
1653 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1654 (grfMode
& STGM_DELETEONRELEASE
) ||
1655 (grfMode
& STGM_PRIORITY
) )
1657 res
= STG_E_INVALIDFUNCTION
;
1662 return STG_E_REVERTED
;
1665 * Check that we're compatible with the parent's storage mode,
1666 * but only if we are not transacted
1668 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1669 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1671 res
= STG_E_ACCESSDENIED
;
1678 storageEntryRef
= findElement(
1680 This
->storageDirEntry
,
1684 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1685 (currentEntry
.stgType
==STGTY_STORAGE
) )
1687 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1689 /* A single storage cannot be opened a second time. */
1690 res
= STG_E_ACCESSDENIED
;
1694 newStorage
= StorageInternalImpl_Construct(
1699 if (newStorage
!= 0)
1701 if (grfMode
& STGM_TRANSACTED
)
1703 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1707 HeapFree(GetProcessHeap(), 0, newStorage
);
1711 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1715 *ppstg
= &newStorage
->base
.IStorage_iface
;
1718 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1724 res
= STG_E_INSUFFICIENTMEMORY
;
1728 res
= STG_E_FILENOTFOUND
;
1731 TRACE("<-- %#lx\n", res
);
1735 /************************************************************************
1736 * StorageBaseImpl_EnumElements (IStorage)
1738 * This method will create an enumerator object that can be used to
1739 * retrieve information about all the elements in the storage object.
1741 * See Windows documentation for more details on IStorage methods.
1743 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1745 DWORD reserved1
, /* [in] */
1746 void* reserved2
, /* [size_is][unique][in] */
1747 DWORD reserved3
, /* [in] */
1748 IEnumSTATSTG
** ppenum
) /* [out] */
1750 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1751 IEnumSTATSTGImpl
* newEnum
;
1753 TRACE("%p, %ld, %p, %ld, %p.\n", iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1756 return E_INVALIDARG
;
1759 return STG_E_REVERTED
;
1761 newEnum
= IEnumSTATSTGImpl_Construct(
1763 This
->storageDirEntry
);
1767 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1771 return E_OUTOFMEMORY
;
1774 /************************************************************************
1775 * StorageBaseImpl_Stat (IStorage)
1777 * This method will retrieve information about this storage object.
1779 * See Windows documentation for more details on IStorage methods.
1781 static HRESULT WINAPI
StorageBaseImpl_Stat(
1783 STATSTG
* pstatstg
, /* [out] */
1784 DWORD grfStatFlag
) /* [in] */
1786 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1787 DirEntry currentEntry
;
1788 HRESULT res
= STG_E_UNKNOWN
;
1790 TRACE("%p, %p, %#lx.\n", iface
, pstatstg
, grfStatFlag
);
1800 res
= STG_E_REVERTED
;
1804 res
= StorageBaseImpl_ReadDirEntry(
1806 This
->storageDirEntry
,
1811 StorageUtl_CopyDirEntryToSTATSTG(
1817 pstatstg
->grfMode
= This
->openFlags
;
1818 pstatstg
->grfStateBits
= This
->stateBits
;
1824 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.LowPart
, pstatstg
->cbSize
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
1826 TRACE("<-- %#lx\n", res
);
1830 /************************************************************************
1831 * StorageBaseImpl_RenameElement (IStorage)
1833 * This method will rename the specified element.
1835 * See Windows documentation for more details on IStorage methods.
1837 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1839 const OLECHAR
* pwcsOldName
, /* [in] */
1840 const OLECHAR
* pwcsNewName
) /* [in] */
1842 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1843 DirEntry currentEntry
;
1844 DirRef currentEntryRef
;
1846 TRACE("(%p, %s, %s)\n",
1847 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1850 return STG_E_REVERTED
;
1852 currentEntryRef
= findElement(This
,
1853 This
->storageDirEntry
,
1857 if (currentEntryRef
!= DIRENTRY_NULL
)
1860 * There is already an element with the new name
1862 return STG_E_FILEALREADYEXISTS
;
1866 * Search for the old element name
1868 currentEntryRef
= findElement(This
,
1869 This
->storageDirEntry
,
1873 if (currentEntryRef
!= DIRENTRY_NULL
)
1875 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1876 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1878 WARN("Element is already open; cannot rename.\n");
1879 return STG_E_ACCESSDENIED
;
1882 /* Remove the element from its current position in the tree */
1883 removeFromTree(This
, This
->storageDirEntry
,
1886 /* Change the name of the element */
1887 lstrcpyW(currentEntry
.name
, pwcsNewName
);
1889 /* Delete any sibling links */
1890 currentEntry
.leftChild
= DIRENTRY_NULL
;
1891 currentEntry
.rightChild
= DIRENTRY_NULL
;
1893 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1896 /* Insert the element in a new position in the tree */
1897 insertIntoTree(This
, This
->storageDirEntry
,
1903 * There is no element with the old name
1905 return STG_E_FILENOTFOUND
;
1908 return StorageBaseImpl_Flush(This
);
1911 /************************************************************************
1912 * StorageBaseImpl_CreateStream (IStorage)
1914 * This method will create a stream object within this storage
1916 * See Windows documentation for more details on IStorage methods.
1918 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1920 const OLECHAR
* pwcsName
, /* [string][in] */
1921 DWORD grfMode
, /* [in] */
1922 DWORD reserved1
, /* [in] */
1923 DWORD reserved2
, /* [in] */
1924 IStream
** ppstm
) /* [out] */
1926 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1927 StgStreamImpl
* newStream
;
1928 DirEntry currentEntry
, newStreamEntry
;
1929 DirRef currentEntryRef
, newStreamEntryRef
;
1932 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface
, debugstr_w(pwcsName
), grfMode
, reserved1
, reserved2
, ppstm
);
1935 return STG_E_INVALIDPOINTER
;
1938 return STG_E_INVALIDNAME
;
1940 if (reserved1
|| reserved2
)
1941 return STG_E_INVALIDPARAMETER
;
1943 if ( FAILED( validateSTGM(grfMode
) ))
1944 return STG_E_INVALIDFLAG
;
1946 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1947 return STG_E_INVALIDFLAG
;
1950 return STG_E_REVERTED
;
1955 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1956 (grfMode
& STGM_TRANSACTED
))
1957 return STG_E_INVALIDFUNCTION
;
1960 * Don't worry about permissions in transacted mode, as we can always write
1961 * changes; we just can't always commit them.
1963 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1964 /* Can't create a stream on read-only storage */
1965 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1966 return STG_E_ACCESSDENIED
;
1968 /* Can't create a stream with greater access than the parent. */
1969 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1970 return STG_E_ACCESSDENIED
;
1973 if(This
->openFlags
& STGM_SIMPLE
)
1974 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1978 currentEntryRef
= findElement(This
,
1979 This
->storageDirEntry
,
1983 if (currentEntryRef
!= DIRENTRY_NULL
)
1986 * An element with this name already exists
1988 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1990 IStorage_DestroyElement(iface
, pwcsName
);
1993 return STG_E_FILEALREADYEXISTS
;
1997 * memset the empty entry
1999 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
2001 newStreamEntry
.sizeOfNameString
=
2002 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
2004 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2005 return STG_E_INVALIDNAME
;
2007 lstrcpyW(newStreamEntry
.name
, pwcsName
);
2009 newStreamEntry
.stgType
= STGTY_STREAM
;
2010 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2011 newStreamEntry
.size
.LowPart
= 0;
2012 newStreamEntry
.size
.HighPart
= 0;
2014 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
2015 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
2016 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
2018 /* call CoFileTime to get the current time
2019 newStreamEntry.ctime
2020 newStreamEntry.mtime
2023 /* newStreamEntry.clsid */
2026 * Create an entry with the new data
2028 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
2033 * Insert the new entry in the parent storage's tree.
2035 hr
= insertIntoTree(
2037 This
->storageDirEntry
,
2041 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
2046 * Open the stream to return it.
2048 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2052 *ppstm
= &newStream
->IStream_iface
;
2053 IStream_AddRef(*ppstm
);
2057 return STG_E_INSUFFICIENTMEMORY
;
2060 return StorageBaseImpl_Flush(This
);
2063 /************************************************************************
2064 * StorageBaseImpl_SetClass (IStorage)
2066 * This method will write the specified CLSID in the directory entry of this
2069 * See Windows documentation for more details on IStorage methods.
2071 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2073 REFCLSID clsid
) /* [in] */
2075 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2077 DirEntry currentEntry
;
2079 TRACE("(%p, %s)\n", iface
, wine_dbgstr_guid(clsid
));
2082 return STG_E_REVERTED
;
2084 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2085 This
->storageDirEntry
,
2087 if (SUCCEEDED(hRes
))
2089 currentEntry
.clsid
= *clsid
;
2091 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2092 This
->storageDirEntry
,
2096 if (SUCCEEDED(hRes
))
2097 hRes
= StorageBaseImpl_Flush(This
);
2102 /************************************************************************
2103 * StorageBaseImpl_CreateStorage (IStorage)
2105 * This method will create the storage object within the provided storage.
2107 * See Windows documentation for more details on IStorage methods.
2109 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2111 const OLECHAR
*pwcsName
, /* [string][in] */
2112 DWORD grfMode
, /* [in] */
2113 DWORD reserved1
, /* [in] */
2114 DWORD reserved2
, /* [in] */
2115 IStorage
**ppstg
) /* [out] */
2117 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2119 DirEntry currentEntry
;
2121 DirRef currentEntryRef
;
2125 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface
, debugstr_w(pwcsName
), grfMode
,
2126 reserved1
, reserved2
, ppstg
);
2129 return STG_E_INVALIDPOINTER
;
2131 if (This
->openFlags
& STGM_SIMPLE
)
2133 return STG_E_INVALIDFUNCTION
;
2137 return STG_E_INVALIDNAME
;
2141 if ( FAILED( validateSTGM(grfMode
) ) ||
2142 (grfMode
& STGM_DELETEONRELEASE
) )
2144 WARN("bad grfMode: %#lx\n", grfMode
);
2145 return STG_E_INVALIDFLAG
;
2149 return STG_E_REVERTED
;
2152 * Check that we're compatible with the parent's storage mode
2154 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2155 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2157 WARN("access denied\n");
2158 return STG_E_ACCESSDENIED
;
2161 currentEntryRef
= findElement(This
,
2162 This
->storageDirEntry
,
2166 if (currentEntryRef
!= DIRENTRY_NULL
)
2169 * An element with this name already exists
2171 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2172 ((This
->openFlags
& STGM_TRANSACTED
) ||
2173 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2175 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2181 WARN("file already exists\n");
2182 return STG_E_FILEALREADYEXISTS
;
2185 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2186 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2188 WARN("read-only storage\n");
2189 return STG_E_ACCESSDENIED
;
2192 memset(&newEntry
, 0, sizeof(DirEntry
));
2194 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2196 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2198 FIXME("name too long\n");
2199 return STG_E_INVALIDNAME
;
2202 lstrcpyW(newEntry
.name
, pwcsName
);
2204 newEntry
.stgType
= STGTY_STORAGE
;
2205 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2206 newEntry
.size
.LowPart
= 0;
2207 newEntry
.size
.HighPart
= 0;
2209 newEntry
.leftChild
= DIRENTRY_NULL
;
2210 newEntry
.rightChild
= DIRENTRY_NULL
;
2211 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2213 /* call CoFileTime to get the current time
2218 /* newEntry.clsid */
2221 * Create a new directory entry for the storage
2223 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2228 * Insert the new directory entry into the parent storage's tree
2230 hr
= insertIntoTree(
2232 This
->storageDirEntry
,
2236 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2241 * Open it to get a pointer to return.
2243 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2245 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2251 hr
= StorageBaseImpl_Flush(This
);
2256 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2257 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2258 SNB snbExclude
, IStorage
*pstgDest
)
2263 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2266 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2269 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2270 skip_stream
, snbExclude
, pstgDest
);
2272 TRACE("<-- %#lx\n", hr
);
2276 /*************************************************************************
2279 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2281 DWORD ciidExclude
, /* [in] */
2282 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2283 SNB snbExclude
, /* [unique][in] */
2284 IStorage
* pstgDest
) /* [unique][in] */
2286 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2288 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2291 TRACE("%p, %ld, %p, %p, %p.\n", iface
, ciidExclude
, rgiidExclude
, snbExclude
, pstgDest
);
2293 if ( pstgDest
== 0 )
2294 return STG_E_INVALIDPOINTER
;
2296 for(i
= 0; i
< ciidExclude
; ++i
)
2298 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2299 skip_storage
= TRUE
;
2300 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2303 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2308 /* Give up early if it looks like this would be infinitely recursive.
2309 * Oddly enough, this includes some cases that aren't really recursive, like
2310 * copying to a transacted child. */
2311 IStorage
*pstgDestAncestor
= pstgDest
;
2312 IStorage
*pstgDestAncestorChild
= NULL
;
2314 /* Go up the chain from the destination until we find the source storage. */
2315 while (pstgDestAncestor
!= iface
) {
2316 pstgDestAncestorChild
= pstgDest
;
2318 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2320 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2322 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2324 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2326 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2328 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2334 if (pstgDestAncestor
== iface
)
2338 if (pstgDestAncestorChild
&& snbExclude
)
2340 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2342 WCHAR
**snb
= snbExclude
;
2344 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2346 while ( *snb
!= NULL
&& fail
)
2348 if ( wcscmp(data
.name
, *snb
) == 0 )
2355 return STG_E_ACCESSDENIED
;
2359 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2360 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2363 /*************************************************************************
2364 * MoveElementTo (IStorage)
2366 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2368 const OLECHAR
*pwcsName
, /* [string][in] */
2369 IStorage
*pstgDest
, /* [unique][in] */
2370 const OLECHAR
*pwcsNewName
,/* [string][in] */
2371 DWORD grfFlags
) /* [in] */
2373 FIXME("%p, %s, %p, %s, %#lx: stub\n", iface
, debugstr_w(pwcsName
), pstgDest
,
2374 debugstr_w(pwcsNewName
), grfFlags
);
2378 /*************************************************************************
2381 * Ensures that any changes made to a storage object open in transacted mode
2382 * are reflected in the parent storage
2384 * In a non-transacted mode, this ensures all cached writes are completed.
2386 static HRESULT WINAPI
StorageBaseImpl_Commit(
2388 DWORD grfCommitFlags
)/* [in] */
2390 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2391 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
2392 return StorageBaseImpl_Flush(This
);
2395 /*************************************************************************
2398 * Discard all changes that have been made since the last commit operation
2400 static HRESULT WINAPI
StorageBaseImpl_Revert(
2403 TRACE("(%p)\n", iface
);
2407 /*********************************************************************
2409 * Internal helper function for StorageBaseImpl_DestroyElement()
2411 * Delete the contents of a storage entry.
2414 static HRESULT
deleteStorageContents(
2415 StorageBaseImpl
*parentStorage
,
2416 DirRef indexToDelete
,
2417 DirEntry entryDataToDelete
)
2419 IEnumSTATSTG
*elements
= 0;
2420 IStorage
*childStorage
= 0;
2421 STATSTG currentElement
;
2423 HRESULT destroyHr
= S_OK
;
2424 StorageInternalImpl
*stg
, *stg2
;
2426 TRACE("%p, %ld.\n", parentStorage
, indexToDelete
);
2428 /* Invalidate any open storage objects. */
2429 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2431 if (stg
->base
.storageDirEntry
== indexToDelete
)
2433 StorageBaseImpl_Invalidate(&stg
->base
);
2438 * Open the storage and enumerate it
2440 hr
= IStorage_OpenStorage(
2441 &parentStorage
->IStorage_iface
,
2442 entryDataToDelete
.name
,
2444 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2451 TRACE("<-- %#lx\n", hr
);
2456 * Enumerate the elements
2458 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2461 IStorage_Release(childStorage
);
2462 TRACE("<-- %#lx\n", hr
);
2469 * Obtain the next element
2471 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2474 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2476 CoTaskMemFree(currentElement
.pwcsName
);
2480 * We need to Reset the enumeration every time because we delete elements
2481 * and the enumeration could be invalid
2483 IEnumSTATSTG_Reset(elements
);
2485 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2487 IStorage_Release(childStorage
);
2488 IEnumSTATSTG_Release(elements
);
2490 TRACE("%#lx\n", hr
);
2494 /*********************************************************************
2496 * Internal helper function for StorageBaseImpl_DestroyElement()
2498 * Perform the deletion of a stream's data
2501 static HRESULT
deleteStreamContents(
2502 StorageBaseImpl
*parentStorage
,
2503 DirRef indexToDelete
,
2504 DirEntry entryDataToDelete
)
2508 ULARGE_INTEGER size
;
2509 StgStreamImpl
*strm
, *strm2
;
2511 /* Invalidate any open stream objects. */
2512 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2514 if (strm
->dirEntry
== indexToDelete
)
2516 TRACE("Stream deleted %p\n", strm
);
2517 strm
->parentStorage
= NULL
;
2518 list_remove(&strm
->StrmListEntry
);
2525 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2526 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2530 TRACE("<-- %#lx\n", hr
);
2537 hr
= IStream_SetSize(pis
, size
);
2541 TRACE("<-- %#lx\n", hr
);
2546 * Release the stream object.
2548 IStream_Release(pis
);
2549 TRACE("<-- %#lx\n", hr
);
2553 /*************************************************************************
2554 * DestroyElement (IStorage)
2556 * Strategy: This implementation is built this way for simplicity not for speed.
2557 * I always delete the topmost element of the enumeration and adjust
2558 * the deleted element pointer all the time. This takes longer to
2559 * do but allows reinvoking DestroyElement whenever we encounter a
2560 * storage object. The optimisation resides in the usage of another
2561 * enumeration strategy that would give all the leaves of a storage
2562 * first. (postfix order)
2564 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2566 const OLECHAR
*pwcsName
)/* [string][in] */
2568 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2571 DirEntry entryToDelete
;
2572 DirRef entryToDeleteRef
;
2575 iface
, debugstr_w(pwcsName
));
2578 return STG_E_INVALIDPOINTER
;
2581 return STG_E_REVERTED
;
2583 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2584 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2585 return STG_E_ACCESSDENIED
;
2587 entryToDeleteRef
= findElement(
2589 This
->storageDirEntry
,
2593 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2595 TRACE("<-- STG_E_FILENOTFOUND\n");
2596 return STG_E_FILENOTFOUND
;
2599 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2601 hr
= deleteStorageContents(
2606 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2608 hr
= deleteStreamContents(
2616 TRACE("<-- %#lx\n", hr
);
2621 * Remove the entry from its parent storage
2623 hr
= removeFromTree(
2625 This
->storageDirEntry
,
2629 * Invalidate the entry
2632 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2635 hr
= StorageBaseImpl_Flush(This
);
2637 TRACE("<-- %#lx\n", hr
);
2641 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2643 struct list
*cur
, *cur2
;
2644 StgStreamImpl
*strm
=NULL
;
2645 StorageInternalImpl
*childstg
=NULL
;
2647 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2648 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2649 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2650 strm
->parentStorage
= NULL
;
2654 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2655 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2656 StorageBaseImpl_Invalidate( &childstg
->base
);
2659 if (stg
->transactedChild
)
2661 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2663 stg
->transactedChild
= NULL
;
2667 /******************************************************************************
2668 * SetElementTimes (IStorage)
2670 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2672 const OLECHAR
*pwcsName
,/* [string][in] */
2673 const FILETIME
*pctime
, /* [in] */
2674 const FILETIME
*patime
, /* [in] */
2675 const FILETIME
*pmtime
) /* [in] */
2677 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2681 /******************************************************************************
2682 * SetStateBits (IStorage)
2684 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2686 DWORD grfStateBits
,/* [in] */
2687 DWORD grfMask
) /* [in] */
2689 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2692 return STG_E_REVERTED
;
2694 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2698 /******************************************************************************
2699 * Internal stream list handlers
2702 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2704 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2705 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2708 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2710 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2711 list_remove(&(strm
->StrmListEntry
));
2714 static HRESULT
StorageBaseImpl_CopyStream(
2715 StorageBaseImpl
*dst
, DirRef dst_entry
,
2716 StorageBaseImpl
*src
, DirRef src_entry
)
2721 ULARGE_INTEGER bytes_copied
;
2722 ULONG bytestocopy
, bytesread
, byteswritten
;
2724 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2728 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2730 bytes_copied
.QuadPart
= 0;
2731 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2733 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2735 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2737 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2740 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2741 data
, &byteswritten
);
2744 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2745 bytes_copied
.QuadPart
+= byteswritten
;
2753 static HRESULT
StorageBaseImpl_DupStorageTree(
2754 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2755 StorageBaseImpl
*src
, DirRef src_entry
)
2759 BOOL has_stream
=FALSE
;
2761 if (src_entry
== DIRENTRY_NULL
)
2763 *dst_entry
= DIRENTRY_NULL
;
2767 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2770 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2771 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2772 data
.size
.QuadPart
= 0;
2774 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2778 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2781 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2784 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2786 if (SUCCEEDED(hr
) && has_stream
)
2787 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2792 static HRESULT
StorageBaseImpl_CopyStorageTree(
2793 StorageBaseImpl
*dst
, DirRef dst_entry
,
2794 StorageBaseImpl
*src
, DirRef src_entry
)
2797 DirEntry src_data
, dst_data
;
2798 DirRef new_root_entry
;
2800 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2804 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2809 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2810 dst_data
.clsid
= src_data
.clsid
;
2811 dst_data
.ctime
= src_data
.ctime
;
2812 dst_data
.mtime
= src_data
.mtime
;
2813 dst_data
.dirRootEntry
= new_root_entry
;
2817 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2822 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2826 ULARGE_INTEGER zero
;
2828 if (entry
== DIRENTRY_NULL
)
2833 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2835 if (SUCCEEDED(hr
) && include_siblings
)
2836 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2838 if (SUCCEEDED(hr
) && include_siblings
)
2839 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2842 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2844 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2845 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2848 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2854 /************************************************************************
2855 * StorageImpl implementation
2856 ***********************************************************************/
2858 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2859 ULARGE_INTEGER offset
,
2864 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2867 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2868 ULARGE_INTEGER offset
,
2871 ULONG
* bytesWritten
)
2873 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2876 /******************************************************************************
2877 * StorageImpl_LoadFileHeader
2879 * This method will read in the file header
2881 static HRESULT
StorageImpl_LoadFileHeader(
2885 BYTE headerBigBlock
[HEADER_SIZE
];
2887 ULARGE_INTEGER offset
;
2892 * Get a pointer to the big block of data containing the header.
2894 offset
.HighPart
= 0;
2896 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2897 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2898 hr
= STG_E_FILENOTFOUND
;
2901 * Extract the information from the header.
2906 * Check for the "magic number" signature and return an error if it is not
2909 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2911 return STG_E_OLDFORMAT
;
2914 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2916 return STG_E_INVALIDHEADER
;
2919 StorageUtl_ReadWord(
2921 OFFSET_BIGBLOCKSIZEBITS
,
2922 &This
->bigBlockSizeBits
);
2924 StorageUtl_ReadWord(
2926 OFFSET_SMALLBLOCKSIZEBITS
,
2927 &This
->smallBlockSizeBits
);
2929 StorageUtl_ReadDWord(
2931 OFFSET_BBDEPOTCOUNT
,
2932 &This
->bigBlockDepotCount
);
2934 StorageUtl_ReadDWord(
2936 OFFSET_ROOTSTARTBLOCK
,
2937 &This
->rootStartBlock
);
2939 StorageUtl_ReadDWord(
2941 OFFSET_TRANSACTIONSIG
,
2942 &This
->transactionSig
);
2944 StorageUtl_ReadDWord(
2946 OFFSET_SMALLBLOCKLIMIT
,
2947 &This
->smallBlockLimit
);
2949 StorageUtl_ReadDWord(
2951 OFFSET_SBDEPOTSTART
,
2952 &This
->smallBlockDepotStart
);
2954 StorageUtl_ReadDWord(
2956 OFFSET_EXTBBDEPOTSTART
,
2957 &This
->extBigBlockDepotStart
);
2959 StorageUtl_ReadDWord(
2961 OFFSET_EXTBBDEPOTCOUNT
,
2962 &This
->extBigBlockDepotCount
);
2964 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2966 StorageUtl_ReadDWord(
2968 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2969 &(This
->bigBlockDepotStart
[index
]));
2973 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2975 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2976 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2979 * Right now, the code is making some assumptions about the size of the
2980 * blocks, just make sure they are what we're expecting.
2982 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2983 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2984 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2986 FIXME("Broken OLE storage file? bigblock=%#lx, smallblock=%#lx, sblimit=%#lx\n",
2987 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
2988 hr
= STG_E_INVALIDHEADER
;
2997 /******************************************************************************
2998 * StorageImpl_SaveFileHeader
3000 * This method will save to the file the header
3002 static void StorageImpl_SaveFileHeader(
3005 BYTE headerBigBlock
[HEADER_SIZE
];
3007 ULARGE_INTEGER offset
;
3008 DWORD bytes_written
;
3009 DWORD major_version
, dirsectorcount
;
3011 if (This
->bigBlockSizeBits
== 0x9)
3013 else if (This
->bigBlockSizeBits
== 0xc)
3017 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3021 memset(headerBigBlock
, 0, HEADER_SIZE
);
3022 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3025 * Write the information to the header.
3027 StorageUtl_WriteWord(
3029 OFFSET_MINORVERSION
,
3032 StorageUtl_WriteWord(
3034 OFFSET_MAJORVERSION
,
3037 StorageUtl_WriteWord(
3039 OFFSET_BYTEORDERMARKER
,
3042 StorageUtl_WriteWord(
3044 OFFSET_BIGBLOCKSIZEBITS
,
3045 This
->bigBlockSizeBits
);
3047 StorageUtl_WriteWord(
3049 OFFSET_SMALLBLOCKSIZEBITS
,
3050 This
->smallBlockSizeBits
);
3052 if (major_version
>= 4)
3054 if (This
->rootBlockChain
)
3055 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3057 /* This file is being created, and it will start out with one block. */
3061 /* This field must be 0 in versions older than 4 */
3064 StorageUtl_WriteDWord(
3066 OFFSET_DIRSECTORCOUNT
,
3069 StorageUtl_WriteDWord(
3071 OFFSET_BBDEPOTCOUNT
,
3072 This
->bigBlockDepotCount
);
3074 StorageUtl_WriteDWord(
3076 OFFSET_ROOTSTARTBLOCK
,
3077 This
->rootStartBlock
);
3079 StorageUtl_WriteDWord(
3081 OFFSET_TRANSACTIONSIG
,
3082 This
->transactionSig
);
3084 StorageUtl_WriteDWord(
3086 OFFSET_SMALLBLOCKLIMIT
,
3087 This
->smallBlockLimit
);
3089 StorageUtl_WriteDWord(
3091 OFFSET_SBDEPOTSTART
,
3092 This
->smallBlockDepotStart
);
3094 StorageUtl_WriteDWord(
3096 OFFSET_SBDEPOTCOUNT
,
3097 This
->smallBlockDepotChain
?
3098 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3100 StorageUtl_WriteDWord(
3102 OFFSET_EXTBBDEPOTSTART
,
3103 This
->extBigBlockDepotStart
);
3105 StorageUtl_WriteDWord(
3107 OFFSET_EXTBBDEPOTCOUNT
,
3108 This
->extBigBlockDepotCount
);
3110 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3112 StorageUtl_WriteDWord(
3114 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3115 (This
->bigBlockDepotStart
[index
]));
3118 offset
.QuadPart
= 0;
3119 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3123 /************************************************************************
3124 * StorageImpl implementation : DirEntry methods
3125 ***********************************************************************/
3127 /******************************************************************************
3128 * StorageImpl_ReadRawDirEntry
3130 * This method will read the raw data from a directory entry in the file.
3132 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3134 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3136 ULARGE_INTEGER offset
;
3140 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3142 hr
= BlockChainStream_ReadAt(
3143 This
->rootBlockChain
,
3149 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3150 return STG_E_READFAULT
;
3155 /******************************************************************************
3156 * StorageImpl_WriteRawDirEntry
3158 * This method will write the raw data from a directory entry in the file.
3160 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3162 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3164 ULARGE_INTEGER offset
;
3167 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3169 return BlockChainStream_WriteAt(
3170 This
->rootBlockChain
,
3177 /***************************************************************************
3181 * Mark a directory entry in the file as free.
3183 static HRESULT
StorageImpl_DestroyDirEntry(
3184 StorageBaseImpl
*base
,
3187 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3188 StorageImpl
*storage
= (StorageImpl
*)base
;
3190 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3192 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3195 /******************************************************************************
3198 * Update raw directory entry data from the fields in newData.
3200 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3202 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3204 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3207 buffer
+ OFFSET_PS_NAME
,
3209 DIRENTRY_NAME_BUFFER_LEN
);
3211 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3213 StorageUtl_WriteWord(
3215 OFFSET_PS_NAMELENGTH
,
3216 newData
->sizeOfNameString
);
3218 StorageUtl_WriteDWord(
3220 OFFSET_PS_LEFTCHILD
,
3221 newData
->leftChild
);
3223 StorageUtl_WriteDWord(
3225 OFFSET_PS_RIGHTCHILD
,
3226 newData
->rightChild
);
3228 StorageUtl_WriteDWord(
3231 newData
->dirRootEntry
);
3233 StorageUtl_WriteGUID(
3238 StorageUtl_WriteDWord(
3241 newData
->ctime
.dwLowDateTime
);
3243 StorageUtl_WriteDWord(
3245 OFFSET_PS_CTIMEHIGH
,
3246 newData
->ctime
.dwHighDateTime
);
3248 StorageUtl_WriteDWord(
3251 newData
->mtime
.dwLowDateTime
);
3253 StorageUtl_WriteDWord(
3255 OFFSET_PS_MTIMEHIGH
,
3256 newData
->ctime
.dwHighDateTime
);
3258 StorageUtl_WriteDWord(
3260 OFFSET_PS_STARTBLOCK
,
3261 newData
->startingBlock
);
3263 StorageUtl_WriteDWord(
3266 newData
->size
.LowPart
);
3268 StorageUtl_WriteDWord(
3270 OFFSET_PS_SIZE_HIGH
,
3271 newData
->size
.HighPart
);
3274 /***************************************************************************
3278 * Reserve a directory entry in the file and initialize it.
3280 static HRESULT
StorageImpl_CreateDirEntry(
3281 StorageBaseImpl
*base
,
3282 const DirEntry
*newData
,
3285 StorageImpl
*storage
= (StorageImpl
*)base
;
3286 ULONG currentEntryIndex
= 0;
3287 ULONG newEntryIndex
= DIRENTRY_NULL
;
3289 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3290 WORD sizeOfNameString
;
3294 hr
= StorageImpl_ReadRawDirEntry(storage
,
3300 StorageUtl_ReadWord(
3302 OFFSET_PS_NAMELENGTH
,
3305 if (sizeOfNameString
== 0)
3308 * The entry exists and is available, we found it.
3310 newEntryIndex
= currentEntryIndex
;
3316 * We exhausted the directory entries, we will create more space below
3318 newEntryIndex
= currentEntryIndex
;
3320 currentEntryIndex
++;
3322 } while (newEntryIndex
== DIRENTRY_NULL
);
3325 * grow the directory stream
3329 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3330 ULARGE_INTEGER newSize
;
3332 ULONG lastEntry
= 0;
3333 ULONG blockCount
= 0;
3336 * obtain the new count of blocks in the directory stream
3338 blockCount
= BlockChainStream_GetCount(
3339 storage
->rootBlockChain
)+1;
3342 * initialize the size used by the directory stream
3344 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3347 * add a block to the directory stream
3349 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3352 * memset the empty entry in order to initialize the unused newly
3355 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3360 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3363 entryIndex
= newEntryIndex
+ 1;
3364 entryIndex
< lastEntry
;
3367 StorageImpl_WriteRawDirEntry(
3373 StorageImpl_SaveFileHeader(storage
);
3376 UpdateRawDirEntry(currentData
, newData
);
3378 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3381 *index
= newEntryIndex
;
3386 /******************************************************************************
3387 * StorageImpl_ReadDirEntry
3389 * This method will read the specified directory entry.
3391 static HRESULT
StorageImpl_ReadDirEntry(
3396 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3399 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3401 if (SUCCEEDED(readRes
))
3403 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3406 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3407 DIRENTRY_NAME_BUFFER_LEN
);
3408 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3410 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3412 StorageUtl_ReadWord(
3414 OFFSET_PS_NAMELENGTH
,
3415 &buffer
->sizeOfNameString
);
3417 StorageUtl_ReadDWord(
3419 OFFSET_PS_LEFTCHILD
,
3420 &buffer
->leftChild
);
3422 StorageUtl_ReadDWord(
3424 OFFSET_PS_RIGHTCHILD
,
3425 &buffer
->rightChild
);
3427 StorageUtl_ReadDWord(
3430 &buffer
->dirRootEntry
);
3432 StorageUtl_ReadGUID(
3437 StorageUtl_ReadDWord(
3440 &buffer
->ctime
.dwLowDateTime
);
3442 StorageUtl_ReadDWord(
3444 OFFSET_PS_CTIMEHIGH
,
3445 &buffer
->ctime
.dwHighDateTime
);
3447 StorageUtl_ReadDWord(
3450 &buffer
->mtime
.dwLowDateTime
);
3452 StorageUtl_ReadDWord(
3454 OFFSET_PS_MTIMEHIGH
,
3455 &buffer
->mtime
.dwHighDateTime
);
3457 StorageUtl_ReadDWord(
3459 OFFSET_PS_STARTBLOCK
,
3460 &buffer
->startingBlock
);
3462 StorageUtl_ReadDWord(
3465 &buffer
->size
.LowPart
);
3467 if (This
->bigBlockSize
< 4096)
3469 /* Version 3 files may have junk in the high part of size. */
3470 buffer
->size
.HighPart
= 0;
3474 StorageUtl_ReadDWord(
3476 OFFSET_PS_SIZE_HIGH
,
3477 &buffer
->size
.HighPart
);
3484 /*********************************************************************
3485 * Write the specified directory entry to the file
3487 static HRESULT
StorageImpl_WriteDirEntry(
3490 const DirEntry
* buffer
)
3492 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3494 UpdateRawDirEntry(currentEntry
, buffer
);
3496 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3500 /************************************************************************
3501 * StorageImpl implementation : Block methods
3502 ***********************************************************************/
3504 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3506 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3509 static HRESULT
StorageImpl_ReadBigBlock(
3515 ULARGE_INTEGER ulOffset
;
3519 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3521 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3523 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3525 /* File ends during this block; fill the rest with 0's. */
3526 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3529 if (out_read
) *out_read
= read
;
3534 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3540 ULARGE_INTEGER ulOffset
;
3544 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3545 ulOffset
.QuadPart
+= offset
;
3547 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3548 *value
= lendian32toh(tmp
);
3549 return (read
== sizeof(DWORD
));
3552 static BOOL
StorageImpl_WriteBigBlock(
3557 ULARGE_INTEGER ulOffset
;
3560 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3562 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3563 return (wrote
== This
->bigBlockSize
);
3566 static BOOL
StorageImpl_WriteDWordToBigBlock(
3572 ULARGE_INTEGER ulOffset
;
3575 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3576 ulOffset
.QuadPart
+= offset
;
3578 value
= htole32(value
);
3579 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3580 return (wrote
== sizeof(DWORD
));
3583 /******************************************************************************
3584 * Storage32Impl_SmallBlocksToBigBlocks
3586 * This method will convert a small block chain to a big block chain.
3587 * The small block chain will be destroyed.
3589 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3591 SmallBlockChainStream
** ppsbChain
)
3593 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3594 ULARGE_INTEGER size
, offset
;
3595 ULONG cbRead
, cbWritten
;
3596 ULARGE_INTEGER cbTotalRead
;
3597 DirRef streamEntryRef
;
3598 HRESULT resWrite
= S_OK
;
3600 DirEntry streamEntry
;
3602 BlockChainStream
*bbTempChain
= NULL
;
3603 BlockChainStream
*bigBlockChain
= NULL
;
3606 * Create a temporary big block chain that doesn't have
3607 * an associated directory entry. This temporary chain will be
3608 * used to copy data from small blocks to big blocks.
3610 bbTempChain
= BlockChainStream_Construct(This
,
3613 if(!bbTempChain
) return NULL
;
3615 * Grow the big block chain.
3617 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3618 BlockChainStream_SetSize(bbTempChain
, size
);
3621 * Copy the contents of the small block chain to the big block chain
3622 * by small block size increments.
3625 offset
.HighPart
= 0;
3626 cbTotalRead
.QuadPart
= 0;
3628 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3631 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3633 min(This
->smallBlockSize
, size
.LowPart
- offset
.LowPart
),
3636 if (FAILED(resRead
))
3641 cbTotalRead
.QuadPart
+= cbRead
;
3643 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3649 if (FAILED(resWrite
))
3652 offset
.LowPart
+= cbRead
;
3656 resRead
= STG_E_READFAULT
;
3659 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3660 HeapFree(GetProcessHeap(),0,buffer
);
3665 if (FAILED(resRead
) || FAILED(resWrite
))
3667 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead
, resWrite
);
3668 BlockChainStream_SetSize(bbTempChain
, size
);
3669 BlockChainStream_Destroy(bbTempChain
);
3674 * Destroy the small block chain.
3676 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3677 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3678 SmallBlockChainStream_Destroy(*ppsbChain
);
3682 * Change the directory entry. This chain is now a big block chain
3683 * and it doesn't reside in the small blocks chain anymore.
3685 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3687 streamEntry
.startingBlock
= bbHeadOfChain
;
3689 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3692 * Destroy the temporary entryless big block chain.
3693 * Create a new big block chain associated with this entry.
3695 BlockChainStream_Destroy(bbTempChain
);
3696 bigBlockChain
= BlockChainStream_Construct(This
,
3700 return bigBlockChain
;
3703 /******************************************************************************
3704 * Storage32Impl_BigBlocksToSmallBlocks
3706 * This method will convert a big block chain to a small block chain.
3707 * The big block chain will be destroyed on success.
3709 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3711 BlockChainStream
** ppbbChain
,
3712 ULARGE_INTEGER newSize
)
3714 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3715 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3716 DirRef streamEntryRef
;
3717 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3718 DirEntry streamEntry
;
3720 SmallBlockChainStream
* sbTempChain
;
3722 TRACE("%p %p\n", This
, ppbbChain
);
3724 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3730 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3731 size
= BlockChainStream_GetSize(*ppbbChain
);
3732 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3734 offset
.HighPart
= 0;
3736 cbTotalRead
.QuadPart
= 0;
3737 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3738 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3740 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3741 min(This
->bigBlockSize
, size
.LowPart
- offset
.LowPart
),
3749 cbTotalRead
.QuadPart
+= cbRead
;
3751 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3752 cbRead
, buffer
, &cbWritten
);
3754 if(FAILED(resWrite
))
3757 offset
.LowPart
+= cbRead
;
3761 resRead
= STG_E_READFAULT
;
3765 HeapFree(GetProcessHeap(), 0, buffer
);
3770 if(FAILED(resRead
) || FAILED(resWrite
))
3772 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead
, resWrite
);
3773 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3774 SmallBlockChainStream_Destroy(sbTempChain
);
3778 /* destroy the original big block chain */
3779 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3780 BlockChainStream_SetSize(*ppbbChain
, size
);
3781 BlockChainStream_Destroy(*ppbbChain
);
3784 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3785 streamEntry
.startingBlock
= sbHeadOfChain
;
3786 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3788 SmallBlockChainStream_Destroy(sbTempChain
);
3789 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3792 /******************************************************************************
3793 * Storage32Impl_AddBlockDepot
3795 * This will create a depot block, essentially it is a block initialized
3798 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3800 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3801 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3802 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3803 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3806 * Initialize blocks as free
3808 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3810 /* Reserve the range lock sector */
3811 if (depotIndex
== rangeLockDepot
)
3813 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3816 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3819 /******************************************************************************
3820 * Storage32Impl_GetExtDepotBlock
3822 * Returns the index of the block that corresponds to the specified depot
3823 * index. This method is only for depot indexes equal or greater than
3824 * COUNT_BBDEPOTINHEADER.
3826 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3828 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3829 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3830 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3831 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3832 ULONG blockIndex
= BLOCK_UNUSED
;
3833 ULONG extBlockIndex
;
3834 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3835 int index
, num_blocks
;
3837 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3839 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3840 return BLOCK_UNUSED
;
3842 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3844 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3846 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3848 num_blocks
= This
->bigBlockSize
/ 4;
3850 for (index
= 0; index
< num_blocks
; index
++)
3852 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3853 This
->extBlockDepotCached
[index
] = blockIndex
;
3856 This
->indexExtBlockDepotCached
= extBlockCount
;
3859 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3864 /******************************************************************************
3865 * Storage32Impl_SetExtDepotBlock
3867 * Associates the specified block index to the specified depot index.
3868 * This method is only for depot indexes equal or greater than
3869 * COUNT_BBDEPOTINHEADER.
3871 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3873 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3874 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3875 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3876 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3877 ULONG extBlockIndex
;
3879 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3881 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3883 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3885 if (extBlockIndex
!= BLOCK_UNUSED
)
3887 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3888 extBlockOffset
* sizeof(ULONG
),
3892 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3894 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3898 /******************************************************************************
3899 * Storage32Impl_AddExtBlockDepot
3901 * Creates an extended depot block.
3903 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3905 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3906 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3907 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3908 ULONG index
= BLOCK_UNUSED
;
3909 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3910 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3911 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3913 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3914 blocksPerDepotBlock
;
3916 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3919 * The first extended block.
3921 This
->extBigBlockDepotStart
= index
;
3926 * Find the last existing extended block.
3928 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3931 * Add the new extended block to the chain.
3933 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3938 * Initialize this block.
3940 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3941 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3943 /* Add the block to our cache. */
3944 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3946 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3947 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3949 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3950 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3952 This
->extBigBlockDepotLocations
= new_cache
;
3953 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3955 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3960 /************************************************************************
3961 * StorageImpl_GetNextBlockInChain
3963 * This method will retrieve the block index of the next big block in
3966 * Params: This - Pointer to the Storage object.
3967 * blockIndex - Index of the block to retrieve the chain
3969 * nextBlockIndex - receives the return value.
3971 * Returns: This method returns the index of the next block in the chain.
3972 * It will return the constants:
3973 * BLOCK_SPECIAL - If the block given was not part of a
3975 * BLOCK_END_OF_CHAIN - If the block given was the last in
3977 * BLOCK_UNUSED - If the block given was not past of a chain
3979 * BLOCK_EXTBBDEPOT - This block is part of the extended
3982 * See Windows documentation for more details on IStorage methods.
3984 static HRESULT
StorageImpl_GetNextBlockInChain(
3987 ULONG
* nextBlockIndex
)
3989 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3990 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3991 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3992 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3994 ULONG depotBlockIndexPos
;
3995 int index
, num_blocks
;
3997 *nextBlockIndex
= BLOCK_SPECIAL
;
3999 if(depotBlockCount
>= This
->bigBlockDepotCount
)
4001 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
, This
->bigBlockDepotCount
);
4002 return STG_E_READFAULT
;
4006 * Cache the currently accessed depot block.
4008 if (depotBlockCount
!= This
->indexBlockDepotCached
)
4010 This
->indexBlockDepotCached
= depotBlockCount
;
4012 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4014 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4019 * We have to look in the extended depot.
4021 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4024 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4027 return STG_E_READFAULT
;
4029 num_blocks
= This
->bigBlockSize
/ 4;
4031 for (index
= 0; index
< num_blocks
; index
++)
4033 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4034 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4038 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4043 /******************************************************************************
4044 * Storage32Impl_GetNextExtendedBlock
4046 * Given an extended block this method will return the next extended block.
4049 * The last ULONG of an extended block is the block index of the next
4050 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4054 * - The index of the next extended block
4055 * - BLOCK_UNUSED: there is no next extended block.
4056 * - Any other return values denotes failure.
4058 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4060 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4061 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4063 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4066 return nextBlockIndex
;
4069 /******************************************************************************
4070 * StorageImpl_SetNextBlockInChain
4072 * This method will write the index of the specified block's next block
4073 * in the big block depot.
4075 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4078 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4079 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4080 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4083 static void StorageImpl_SetNextBlockInChain(
4088 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4089 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4090 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4091 ULONG depotBlockIndexPos
;
4093 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4094 assert(blockIndex
!= nextBlock
);
4096 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4097 /* This should never happen (storage file format spec forbids it), but
4098 * older versions of Wine may have generated broken files. We don't want to
4099 * assert and potentially lose data, but we do want to know if this ever
4100 * happens in a newly-created file. */
4101 ERR("Using range lock page\n");
4103 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4105 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4110 * We have to look in the extended depot.
4112 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4115 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4118 * Update the cached block depot, if necessary.
4120 if (depotBlockCount
== This
->indexBlockDepotCached
)
4122 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4126 /******************************************************************************
4127 * StorageImpl_GetNextFreeBigBlock
4129 * Returns the index of the next free big block.
4130 * If the big block depot is filled, this method will enlarge it.
4133 static ULONG
StorageImpl_GetNextFreeBigBlock(
4134 StorageImpl
* This
, ULONG neededAddNumBlocks
)
4136 ULONG depotBlockIndexPos
;
4137 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4138 ULONG depotBlockOffset
;
4139 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4140 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4142 ULONG freeBlock
= BLOCK_UNUSED
;
4144 ULARGE_INTEGER neededSize
;
4147 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4148 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4151 * Scan the entire big block depot until we find a block marked free
4153 while (nextBlockIndex
!= BLOCK_UNUSED
)
4155 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4157 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4160 * Grow the primary depot.
4162 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4164 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4167 * Add a block depot.
4169 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4170 This
->bigBlockDepotCount
++;
4171 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4174 * Flag it as a block depot.
4176 StorageImpl_SetNextBlockInChain(This
,
4180 /* Save new header information.
4182 StorageImpl_SaveFileHeader(This
);
4187 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4189 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4192 * Grow the extended depot.
4194 ULONG extIndex
= BLOCK_UNUSED
;
4195 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4196 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4198 if (extBlockOffset
== 0)
4200 /* We need an extended block.
4202 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4203 This
->extBigBlockDepotCount
++;
4204 depotBlockIndexPos
= extIndex
+ 1;
4207 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4210 * Add a block depot and mark it in the extended block.
4212 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4213 This
->bigBlockDepotCount
++;
4214 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4216 /* Flag the block depot.
4218 StorageImpl_SetNextBlockInChain(This
,
4222 /* If necessary, flag the extended depot block.
4224 if (extIndex
!= BLOCK_UNUSED
)
4225 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4227 /* Save header information.
4229 StorageImpl_SaveFileHeader(This
);
4233 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4237 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4238 ( nextBlockIndex
!= BLOCK_UNUSED
))
4240 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4242 if (nextBlockIndex
== BLOCK_UNUSED
)
4244 freeBlock
= (depotIndex
* blocksPerDepot
) +
4245 (depotBlockOffset
/sizeof(ULONG
));
4248 depotBlockOffset
+= sizeof(ULONG
);
4253 depotBlockOffset
= 0;
4257 * make sure that the block physically exists before using it
4259 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
* neededAddNumBlocks
;
4261 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4263 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4264 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4266 This
->prevFreeBlock
= freeBlock
;
4271 /******************************************************************************
4272 * StorageImpl_FreeBigBlock
4274 * This method will flag the specified block as free in the big block depot.
4276 static void StorageImpl_FreeBigBlock(
4280 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4282 if (blockIndex
< This
->prevFreeBlock
)
4283 This
->prevFreeBlock
= blockIndex
;
4287 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4288 DirRef index
, const DirEntry
*data
)
4290 StorageImpl
*This
= (StorageImpl
*)base
;
4291 return StorageImpl_WriteDirEntry(This
, index
, data
);
4294 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4295 DirRef index
, DirEntry
*data
)
4297 StorageImpl
*This
= (StorageImpl
*)base
;
4298 return StorageImpl_ReadDirEntry(This
, index
, data
);
4301 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4305 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4307 if (!This
->blockChainCache
[i
])
4309 return &This
->blockChainCache
[i
];
4313 i
= This
->blockChainToEvict
;
4315 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4316 This
->blockChainCache
[i
] = NULL
;
4318 This
->blockChainToEvict
++;
4319 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4320 This
->blockChainToEvict
= 0;
4322 return &This
->blockChainCache
[i
];
4325 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4328 int i
, free_index
=-1;
4330 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4332 if (!This
->blockChainCache
[i
])
4334 if (free_index
== -1) free_index
= i
;
4336 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4338 return &This
->blockChainCache
[i
];
4342 if (free_index
== -1)
4344 free_index
= This
->blockChainToEvict
;
4346 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4347 This
->blockChainCache
[free_index
] = NULL
;
4349 This
->blockChainToEvict
++;
4350 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4351 This
->blockChainToEvict
= 0;
4354 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4355 return &This
->blockChainCache
[free_index
];
4358 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4362 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4364 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4366 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4367 This
->blockChainCache
[i
] = NULL
;
4373 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4374 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4376 StorageImpl
*This
= (StorageImpl
*)base
;
4381 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4382 if (FAILED(hr
)) return hr
;
4384 if (data
.size
.QuadPart
== 0)
4390 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4392 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4399 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4401 SmallBlockChainStream
*stream
;
4403 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4404 if (!stream
) return E_OUTOFMEMORY
;
4406 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4408 SmallBlockChainStream_Destroy(stream
);
4414 BlockChainStream
*stream
= NULL
;
4416 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4417 if (!stream
) return E_OUTOFMEMORY
;
4419 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4425 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4426 ULARGE_INTEGER newsize
)
4428 StorageImpl
*This
= (StorageImpl
*)base
;
4431 SmallBlockChainStream
*smallblock
=NULL
;
4432 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4434 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4435 if (FAILED(hr
)) return hr
;
4437 /* In simple mode keep the stream size above the small block limit */
4438 if (This
->base
.openFlags
& STGM_SIMPLE
)
4439 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4441 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4444 /* Create a block chain object of the appropriate type */
4445 if (data
.size
.QuadPart
== 0)
4447 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4449 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4450 if (!smallblock
) return E_OUTOFMEMORY
;
4454 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4455 bigblock
= *pbigblock
;
4456 if (!bigblock
) return E_OUTOFMEMORY
;
4459 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4461 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4462 if (!smallblock
) return E_OUTOFMEMORY
;
4466 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4467 bigblock
= *pbigblock
;
4468 if (!bigblock
) return E_OUTOFMEMORY
;
4471 /* Change the block chain type if necessary. */
4472 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4474 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4477 SmallBlockChainStream_Destroy(smallblock
);
4481 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4482 *pbigblock
= bigblock
;
4484 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4486 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4491 /* Set the size of the block chain. */
4494 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4495 SmallBlockChainStream_Destroy(smallblock
);
4499 BlockChainStream_SetSize(bigblock
, newsize
);
4502 /* Set the size in the directory entry. */
4503 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4506 data
.size
= newsize
;
4508 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4513 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4514 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4516 StorageImpl
*This
= (StorageImpl
*)base
;
4519 ULARGE_INTEGER newSize
;
4521 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4522 if (FAILED(hr
)) return hr
;
4524 /* Grow the stream if necessary */
4525 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4527 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4529 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4533 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4534 if (FAILED(hr
)) return hr
;
4537 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4539 SmallBlockChainStream
*stream
;
4541 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4542 if (!stream
) return E_OUTOFMEMORY
;
4544 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4546 SmallBlockChainStream_Destroy(stream
);
4552 BlockChainStream
*stream
;
4554 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4555 if (!stream
) return E_OUTOFMEMORY
;
4557 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4561 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4564 StorageImpl
*This
= (StorageImpl
*)base
;
4565 DirEntry dst_data
, src_data
;
4568 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4571 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4575 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4576 dst_data
.startingBlock
= src_data
.startingBlock
;
4577 dst_data
.size
= src_data
.size
;
4579 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4585 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4588 DirEntry currentEntry
;
4589 DirRef currentEntryRef
;
4590 BlockChainStream
*blockChainStream
;
4594 ULARGE_INTEGER size
;
4595 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4597 /* Discard any existing data. */
4599 ILockBytes_SetSize(This
->lockBytes
, size
);
4602 * Initialize all header variables:
4603 * - The big block depot consists of one block and it is at block 0
4604 * - The directory table starts at block 1
4605 * - There is no small block depot
4607 memset( This
->bigBlockDepotStart
,
4609 sizeof(This
->bigBlockDepotStart
));
4611 This
->bigBlockDepotCount
= 1;
4612 This
->bigBlockDepotStart
[0] = 0;
4613 This
->rootStartBlock
= 1;
4614 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4615 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4616 if (This
->bigBlockSize
== 4096)
4617 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4619 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4620 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4621 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4622 This
->extBigBlockDepotCount
= 0;
4624 StorageImpl_SaveFileHeader(This
);
4627 * Add one block for the big block depot and one block for the directory table
4630 size
.LowPart
= This
->bigBlockSize
* 3;
4631 ILockBytes_SetSize(This
->lockBytes
, size
);
4634 * Initialize the big block depot
4636 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4637 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4638 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4639 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4644 * Load the header for the file.
4646 hr
= StorageImpl_LoadFileHeader(This
);
4655 * There is no block depot cached yet.
4657 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4658 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4661 * Start searching for free blocks with block 0.
4663 This
->prevFreeBlock
= 0;
4665 This
->firstFreeSmallBlock
= 0;
4667 /* Read the extended big block depot locations. */
4668 if (This
->extBigBlockDepotCount
!= 0)
4670 ULONG current_block
= This
->extBigBlockDepotStart
;
4671 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4674 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4675 if (!This
->extBigBlockDepotLocations
)
4677 return E_OUTOFMEMORY
;
4680 This
->extBigBlockDepotLocationsSize
= cache_size
;
4682 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4684 if (current_block
== BLOCK_END_OF_CHAIN
)
4686 WARN("File has too few extended big block depot blocks.\n");
4687 return STG_E_DOCFILECORRUPT
;
4689 This
->extBigBlockDepotLocations
[i
] = current_block
;
4690 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4695 This
->extBigBlockDepotLocations
= NULL
;
4696 This
->extBigBlockDepotLocationsSize
= 0;
4700 * Create the block chain abstractions.
4702 if(!(blockChainStream
=
4703 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4705 return STG_E_READFAULT
;
4708 BlockChainStream_Destroy(This
->rootBlockChain
);
4709 This
->rootBlockChain
= blockChainStream
;
4711 if(!(blockChainStream
=
4712 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4715 return STG_E_READFAULT
;
4718 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4719 This
->smallBlockDepotChain
= blockChainStream
;
4722 * Write the root storage entry (memory only)
4728 * Initialize the directory table
4730 memset(&rootEntry
, 0, sizeof(rootEntry
));
4731 lstrcpyW(rootEntry
.name
, L
"Root Entry");
4732 rootEntry
.sizeOfNameString
= sizeof(L
"Root Entry");
4733 rootEntry
.stgType
= STGTY_ROOT
;
4734 rootEntry
.leftChild
= DIRENTRY_NULL
;
4735 rootEntry
.rightChild
= DIRENTRY_NULL
;
4736 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4737 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4738 rootEntry
.size
.HighPart
= 0;
4739 rootEntry
.size
.LowPart
= 0;
4741 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4745 * Find the ID of the root storage.
4747 currentEntryRef
= 0;
4751 hr
= StorageImpl_ReadDirEntry(
4758 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4759 (currentEntry
.stgType
== STGTY_ROOT
) )
4761 This
->base
.storageDirEntry
= currentEntryRef
;
4767 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4771 return STG_E_READFAULT
;
4775 * Create the block chain abstraction for the small block root chain.
4777 if(!(blockChainStream
=
4778 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4780 return STG_E_READFAULT
;
4783 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4784 This
->smallBlockRootChain
= blockChainStream
;
4789 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4791 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4792 This
->blockChainCache
[i
] = NULL
;
4799 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4800 ULONG
* result
, BOOL refresh
)
4802 StorageImpl
*This
= (StorageImpl
*)base
;
4804 DWORD oldTransactionSig
= This
->transactionSig
;
4808 ULARGE_INTEGER offset
;
4812 offset
.HighPart
= 0;
4813 offset
.LowPart
= OFFSET_TRANSACTIONSIG
;
4814 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4818 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4820 if (oldTransactionSig
!= This
->transactionSig
)
4822 /* Someone else wrote to this, so toss all cached information. */
4823 TRACE("signature changed\n");
4825 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4829 This
->transactionSig
= oldTransactionSig
;
4833 *result
= This
->transactionSig
;
4838 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4841 StorageImpl
*This
= (StorageImpl
*)base
;
4843 This
->transactionSig
= value
;
4844 StorageImpl_SaveFileHeader(This
);
4849 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4850 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4852 if ((dwLockType
& This
->locks_supported
) == 0)
4854 if (supported
) *supported
= FALSE
;
4858 if (supported
) *supported
= TRUE
;
4859 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4862 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4863 ULARGE_INTEGER cb
, DWORD dwLockType
)
4865 if ((dwLockType
& This
->locks_supported
) == 0)
4868 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4871 /* Internal function */
4872 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4873 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4877 DWORD start_time
= GetTickCount();
4878 DWORD last_sanity_check
= start_time
;
4879 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4881 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4882 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4886 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4888 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4890 DWORD current_time
= GetTickCount();
4891 if (current_time
- start_time
>= 20000)
4896 if (current_time
- last_sanity_check
>= 500)
4898 /* Any storage implementation with the file open in a
4899 * shared mode should not lock these bytes for writing. However,
4900 * some programs (LibreOffice Writer) will keep ALL bytes locked
4901 * when opening in exclusive mode. We can use a read lock to
4902 * detect this case early, and not hang a full 20 seconds.
4904 * This can collide with another attempt to open the file in
4905 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4906 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4907 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4911 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4912 hr
= STG_E_ACCESSDENIED
;
4915 last_sanity_check
= current_time
;
4918 if (delay
< 150) delay
++;
4920 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4925 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4927 StorageImpl
*This
= (StorageImpl
*)base
;
4929 ULARGE_INTEGER offset
, cb
;
4933 /* Synchronous grab of second priority range, the commit lock, and the
4934 * lock-checking lock. */
4935 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4936 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4940 offset
.QuadPart
= RANGELOCK_COMMIT
;
4944 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4949 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4951 StorageImpl
*This
= (StorageImpl
*)base
;
4953 ULARGE_INTEGER offset
, cb
;
4957 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4958 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4962 offset
.QuadPart
= RANGELOCK_COMMIT
;
4966 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4971 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4973 StorageImpl
*This
= (StorageImpl
*) iface
;
4977 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
4979 *result
= statstg
.pwcsName
;
4984 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
4985 ULONG end
, HRESULT fail_hr
)
4988 ULARGE_INTEGER offset
, cb
;
4990 offset
.QuadPart
= start
;
4991 cb
.QuadPart
= 1 + end
- start
;
4993 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4994 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5002 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
5006 ULARGE_INTEGER offset
, cb
;
5010 for (i
=start
; i
<=end
; i
++)
5012 offset
.QuadPart
= i
;
5013 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
5014 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
5020 for (j
= 0; j
< ARRAY_SIZE(This
->locked_bytes
); j
++)
5022 if (This
->locked_bytes
[j
] == 0)
5024 This
->locked_bytes
[j
] = i
;
5033 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5036 ULARGE_INTEGER offset
;
5038 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5041 if (openFlags
& STGM_NOSNAPSHOT
)
5043 /* STGM_NOSNAPSHOT implies deny write */
5044 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5045 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5048 /* Wrap all other locking inside a single lock so we can check ranges safely */
5049 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5051 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5053 /* If the ILockBytes doesn't support locking that's ok. */
5054 if (!supported
) return S_OK
;
5055 else if (FAILED(hr
)) return hr
;
5059 /* First check for any conflicting locks. */
5060 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5061 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5063 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5064 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5066 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5067 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5069 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5070 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5072 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5073 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5075 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5077 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5080 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5083 /* Then grab our locks. */
5084 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5086 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5088 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5091 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5092 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5094 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5095 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5097 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5098 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5100 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5101 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5103 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5104 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5106 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5108 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5113 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5115 StorageImpl
*This
= (StorageImpl
*)storage
;
5118 TRACE("(%p)\n", This
);
5120 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5123 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5126 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5128 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5129 if (This
->blockChainCache
[i
])
5130 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5133 hr
= ILockBytes_Flush(This
->lockBytes
);
5138 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5140 StorageImpl
*This
= (StorageImpl
*) iface
;
5142 StorageBaseImpl_DeleteAll(&This
->base
);
5144 This
->base
.reverted
= TRUE
;
5147 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5149 StorageImpl
*This
= (StorageImpl
*) iface
;
5151 TRACE("(%p)\n", This
);
5153 StorageImpl_Flush(iface
);
5155 StorageImpl_Invalidate(iface
);
5157 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5159 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5160 BlockChainStream_Destroy(This
->rootBlockChain
);
5161 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5163 for (i
= 0; i
< BLOCKCHAIN_CACHE_SIZE
; i
++)
5164 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5166 for (i
= 0; i
< ARRAY_SIZE(This
->locked_bytes
); i
++)
5168 ULARGE_INTEGER offset
, cb
;
5170 if (This
->locked_bytes
[i
] != 0)
5172 offset
.QuadPart
= This
->locked_bytes
[i
];
5173 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5177 if (This
->lockBytes
)
5178 ILockBytes_Release(This
->lockBytes
);
5179 HeapFree(GetProcessHeap(), 0, This
);
5183 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5185 StorageImpl_Destroy
,
5186 StorageImpl_Invalidate
,
5188 StorageImpl_GetFilename
,
5189 StorageImpl_CreateDirEntry
,
5190 StorageImpl_BaseWriteDirEntry
,
5191 StorageImpl_BaseReadDirEntry
,
5192 StorageImpl_DestroyDirEntry
,
5193 StorageImpl_StreamReadAt
,
5194 StorageImpl_StreamWriteAt
,
5195 StorageImpl_StreamSetSize
,
5196 StorageImpl_StreamLink
,
5197 StorageImpl_GetTransactionSig
,
5198 StorageImpl_SetTransactionSig
,
5199 StorageImpl_LockTransaction
,
5200 StorageImpl_UnlockTransaction
5205 * Virtual function table for the IStorageBaseImpl class.
5207 static const IStorageVtbl StorageImpl_Vtbl
=
5209 StorageBaseImpl_QueryInterface
,
5210 StorageBaseImpl_AddRef
,
5211 StorageBaseImpl_Release
,
5212 StorageBaseImpl_CreateStream
,
5213 StorageBaseImpl_OpenStream
,
5214 StorageBaseImpl_CreateStorage
,
5215 StorageBaseImpl_OpenStorage
,
5216 StorageBaseImpl_CopyTo
,
5217 StorageBaseImpl_MoveElementTo
,
5218 StorageBaseImpl_Commit
,
5219 StorageBaseImpl_Revert
,
5220 StorageBaseImpl_EnumElements
,
5221 StorageBaseImpl_DestroyElement
,
5222 StorageBaseImpl_RenameElement
,
5223 StorageBaseImpl_SetElementTimes
,
5224 StorageBaseImpl_SetClass
,
5225 StorageBaseImpl_SetStateBits
,
5226 StorageBaseImpl_Stat
5229 static HRESULT
StorageImpl_Construct(
5237 StorageImpl
** result
)
5243 if ( FAILED( validateSTGM(openFlags
) ))
5244 return STG_E_INVALIDFLAG
;
5246 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5248 return E_OUTOFMEMORY
;
5250 memset(This
, 0, sizeof(StorageImpl
));
5252 list_init(&This
->base
.strmHead
);
5254 list_init(&This
->base
.storageHead
);
5256 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5257 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5258 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5259 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5260 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5262 This
->base
.create
= create
;
5264 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5265 This
->base
.lockingrole
= SWMR_Writer
;
5266 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5267 This
->base
.lockingrole
= SWMR_Reader
;
5269 This
->base
.lockingrole
= SWMR_None
;
5271 This
->base
.reverted
= FALSE
;
5274 * Initialize the big block cache.
5276 This
->bigBlockSize
= sector_size
;
5277 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5279 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5282 This
->lockBytes
= pLkbyt
;
5283 ILockBytes_AddRef(pLkbyt
);
5287 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5291 This
->locks_supported
= stat
.grfLocksSupported
;
5293 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5294 This
->locks_supported
&= ~WINE_LOCK_READ
;
5296 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5300 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5304 IStorage_Release(&This
->base
.IStorage_iface
);
5309 StorageImpl_Flush(&This
->base
);
5317 /************************************************************************
5318 * StorageInternalImpl implementation
5319 ***********************************************************************/
5321 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5323 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5325 if (!This
->base
.reverted
)
5327 TRACE("Storage invalidated (stg=%p)\n", This
);
5329 This
->base
.reverted
= TRUE
;
5331 This
->parentStorage
= NULL
;
5333 StorageBaseImpl_DeleteAll(&This
->base
);
5335 list_remove(&This
->ParentListEntry
);
5339 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5341 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5343 StorageInternalImpl_Invalidate(&This
->base
);
5345 HeapFree(GetProcessHeap(), 0, This
);
5348 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5350 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5352 return StorageBaseImpl_Flush(This
->parentStorage
);
5355 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5357 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5359 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5362 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5363 const DirEntry
*newData
, DirRef
*index
)
5365 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5367 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5371 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5372 DirRef index
, const DirEntry
*data
)
5374 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5376 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5380 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5381 DirRef index
, DirEntry
*data
)
5383 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5385 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5389 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5392 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5394 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5398 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5399 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5401 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5403 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5404 index
, offset
, size
, buffer
, bytesRead
);
5407 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5408 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5410 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5412 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5413 index
, offset
, size
, buffer
, bytesWritten
);
5416 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5417 DirRef index
, ULARGE_INTEGER newsize
)
5419 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5421 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5425 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5426 DirRef dst
, DirRef src
)
5428 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5430 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5434 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5435 ULONG
* result
, BOOL refresh
)
5440 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5446 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5451 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5456 /******************************************************************************
5458 ** StorageInternalImpl_Commit
5461 static HRESULT WINAPI
StorageInternalImpl_Commit(
5463 DWORD grfCommitFlags
) /* [in] */
5465 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5466 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
5467 return StorageBaseImpl_Flush(This
);
5470 /******************************************************************************
5472 ** StorageInternalImpl_Revert
5475 static HRESULT WINAPI
StorageInternalImpl_Revert(
5478 FIXME("(%p): stub\n", iface
);
5483 * Virtual function table for the StorageInternalImpl class.
5485 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5487 StorageBaseImpl_QueryInterface
,
5488 StorageBaseImpl_AddRef
,
5489 StorageBaseImpl_Release
,
5490 StorageBaseImpl_CreateStream
,
5491 StorageBaseImpl_OpenStream
,
5492 StorageBaseImpl_CreateStorage
,
5493 StorageBaseImpl_OpenStorage
,
5494 StorageBaseImpl_CopyTo
,
5495 StorageBaseImpl_MoveElementTo
,
5496 StorageInternalImpl_Commit
,
5497 StorageInternalImpl_Revert
,
5498 StorageBaseImpl_EnumElements
,
5499 StorageBaseImpl_DestroyElement
,
5500 StorageBaseImpl_RenameElement
,
5501 StorageBaseImpl_SetElementTimes
,
5502 StorageBaseImpl_SetClass
,
5503 StorageBaseImpl_SetStateBits
,
5504 StorageBaseImpl_Stat
5507 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5509 StorageInternalImpl_Destroy
,
5510 StorageInternalImpl_Invalidate
,
5511 StorageInternalImpl_Flush
,
5512 StorageInternalImpl_GetFilename
,
5513 StorageInternalImpl_CreateDirEntry
,
5514 StorageInternalImpl_WriteDirEntry
,
5515 StorageInternalImpl_ReadDirEntry
,
5516 StorageInternalImpl_DestroyDirEntry
,
5517 StorageInternalImpl_StreamReadAt
,
5518 StorageInternalImpl_StreamWriteAt
,
5519 StorageInternalImpl_StreamSetSize
,
5520 StorageInternalImpl_StreamLink
,
5521 StorageInternalImpl_GetTransactionSig
,
5522 StorageInternalImpl_SetTransactionSig
,
5523 StorageInternalImpl_LockTransaction
,
5524 StorageInternalImpl_UnlockTransaction
5527 static StorageInternalImpl
* StorageInternalImpl_Construct(
5528 StorageBaseImpl
* parentStorage
,
5530 DirRef storageDirEntry
)
5532 StorageInternalImpl
* newStorage
;
5534 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5538 list_init(&newStorage
->base
.strmHead
);
5540 list_init(&newStorage
->base
.storageHead
);
5543 * Initialize the virtual function table.
5545 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5546 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5547 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5548 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5550 newStorage
->base
.reverted
= FALSE
;
5552 newStorage
->base
.ref
= 1;
5554 newStorage
->parentStorage
= parentStorage
;
5557 * Keep a reference to the directory entry of this storage
5559 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5561 newStorage
->base
.create
= FALSE
;
5570 /************************************************************************
5571 * TransactedSnapshotImpl implementation
5572 ***********************************************************************/
5574 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5576 DirRef result
=This
->firstFreeEntry
;
5578 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5581 if (result
== This
->entries_size
)
5583 ULONG new_size
= This
->entries_size
* 2;
5584 TransactedDirEntry
*new_entries
;
5586 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5587 if (!new_entries
) return DIRENTRY_NULL
;
5589 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5590 HeapFree(GetProcessHeap(), 0, This
->entries
);
5592 This
->entries
= new_entries
;
5593 This
->entries_size
= new_size
;
5596 This
->entries
[result
].inuse
= TRUE
;
5598 This
->firstFreeEntry
= result
+1;
5603 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5604 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5606 DirRef stubEntryRef
;
5607 TransactedDirEntry
*entry
;
5609 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5611 if (stubEntryRef
!= DIRENTRY_NULL
)
5613 entry
= &This
->entries
[stubEntryRef
];
5615 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5617 entry
->read
= FALSE
;
5620 return stubEntryRef
;
5623 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5624 TransactedSnapshotImpl
*This
, DirRef entry
)
5629 if (!This
->entries
[entry
].read
)
5631 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5632 This
->entries
[entry
].transactedParentEntry
,
5635 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5637 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5639 if (data
.leftChild
== DIRENTRY_NULL
)
5643 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5645 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5647 if (data
.rightChild
== DIRENTRY_NULL
)
5651 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5653 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5655 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5661 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5662 This
->entries
[entry
].read
= TRUE
;
5669 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5670 TransactedSnapshotImpl
*This
, DirRef entry
)
5674 if (!This
->entries
[entry
].stream_dirty
)
5676 DirEntry new_entrydata
;
5678 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5679 new_entrydata
.name
[0] = 'S';
5680 new_entrydata
.sizeOfNameString
= 1;
5681 new_entrydata
.stgType
= STGTY_STREAM
;
5682 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5683 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5684 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5685 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5687 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5688 &This
->entries
[entry
].stream_entry
);
5690 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5692 hr
= StorageBaseImpl_CopyStream(
5693 This
->scratch
, This
->entries
[entry
].stream_entry
,
5694 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5697 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5701 This
->entries
[entry
].stream_dirty
= TRUE
;
5703 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5705 /* Since this entry is modified, and we aren't using its stream data, we
5706 * no longer care about the original entry. */
5708 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5710 if (delete_ref
!= DIRENTRY_NULL
)
5711 This
->entries
[delete_ref
].deleted
= TRUE
;
5713 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5720 /* Find the first entry in a depth-first traversal. */
5721 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5722 TransactedSnapshotImpl
* This
, DirRef parent
)
5724 DirRef cursor
, prev
;
5725 TransactedDirEntry
*entry
;
5728 entry
= &This
->entries
[cursor
];
5731 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5734 cursor
= entry
->data
.leftChild
;
5735 entry
= &This
->entries
[cursor
];
5736 entry
->parent
= prev
;
5738 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5741 cursor
= entry
->data
.rightChild
;
5742 entry
= &This
->entries
[cursor
];
5743 entry
->parent
= prev
;
5745 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5748 cursor
= entry
->data
.dirRootEntry
;
5749 entry
= &This
->entries
[cursor
];
5750 entry
->parent
= prev
;
5759 /* Find the next entry in a depth-first traversal. */
5760 static DirRef
TransactedSnapshotImpl_FindNextChild(
5761 TransactedSnapshotImpl
* This
, DirRef current
)
5764 TransactedDirEntry
*parent_entry
;
5766 parent
= This
->entries
[current
].parent
;
5767 parent_entry
= &This
->entries
[parent
];
5769 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5771 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5773 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5774 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5777 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5779 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5780 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5787 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5788 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5789 TransactedSnapshotImpl
* This
, DirRef entry
)
5791 return entry
!= DIRENTRY_NULL
&&
5792 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5795 /* Destroy the entries created by CopyTree. */
5796 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5797 TransactedSnapshotImpl
* This
, DirRef stop
)
5800 TransactedDirEntry
*entry
;
5801 ULARGE_INTEGER zero
;
5805 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5808 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5810 if (cursor
== DIRENTRY_NULL
)
5813 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5815 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5817 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5819 entry
= &This
->entries
[cursor
];
5821 if (entry
->stream_dirty
)
5822 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5823 entry
->newTransactedParentEntry
, zero
);
5825 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5826 entry
->newTransactedParentEntry
);
5828 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5831 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5835 /* Make a copy of our edited tree that we can use in the parent. */
5836 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5839 TransactedDirEntry
*entry
;
5842 cursor
= This
->base
.storageDirEntry
;
5843 entry
= &This
->entries
[cursor
];
5844 entry
->parent
= DIRENTRY_NULL
;
5845 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5847 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5850 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5852 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5853 entry
= &This
->entries
[cursor
];
5855 while (cursor
!= DIRENTRY_NULL
)
5857 /* Make a copy of this entry in the transacted parent. */
5859 (!entry
->dirty
&& !entry
->stream_dirty
&&
5860 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5861 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5862 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5863 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5868 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5870 newData
.size
.QuadPart
= 0;
5871 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5873 if (newData
.leftChild
!= DIRENTRY_NULL
)
5874 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5876 if (newData
.rightChild
!= DIRENTRY_NULL
)
5877 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5879 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5880 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5882 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5883 &entry
->newTransactedParentEntry
);
5886 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5890 if (entry
->stream_dirty
)
5892 hr
= StorageBaseImpl_CopyStream(
5893 This
->transactedParent
, entry
->newTransactedParentEntry
,
5894 This
->scratch
, entry
->stream_entry
);
5896 else if (entry
->data
.size
.QuadPart
)
5898 hr
= StorageBaseImpl_StreamLink(
5899 This
->transactedParent
, entry
->newTransactedParentEntry
,
5900 entry
->transactedParentEntry
);
5905 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5906 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5911 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5912 entry
= &This
->entries
[cursor
];
5918 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5920 DWORD grfCommitFlags
) /* [in] */
5922 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5923 TransactedDirEntry
*root_entry
;
5924 DirRef i
, dir_root_ref
;
5926 ULARGE_INTEGER zero
;
5928 ULONG transactionSig
;
5932 TRACE("%p, %#lx.\n", iface
, grfCommitFlags
);
5934 /* Cannot commit a read-only transacted storage */
5935 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5936 return STG_E_ACCESSDENIED
;
5938 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5939 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5942 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5945 if (transactionSig
!= This
->lastTransactionSig
)
5947 ERR("file was externally modified\n");
5948 hr
= STG_E_NOTCURRENT
;
5953 This
->lastTransactionSig
= transactionSig
+1;
5954 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5957 else if (hr
== E_NOTIMPL
)
5960 if (FAILED(hr
)) goto end
;
5962 /* To prevent data loss, we create the new structure in the file before we
5963 * delete the old one, so that in case of errors the old data is intact. We
5964 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5965 * needed in the rare situation where we have just enough free disk space to
5966 * overwrite the existing data. */
5968 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5970 if (!root_entry
->read
)
5973 hr
= TransactedSnapshotImpl_CopyTree(This
);
5974 if (FAILED(hr
)) goto end
;
5976 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5977 dir_root_ref
= DIRENTRY_NULL
;
5979 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5981 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5983 /* Update the storage to use the new data in one step. */
5985 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5986 root_entry
->transactedParentEntry
, &data
);
5990 data
.dirRootEntry
= dir_root_ref
;
5991 data
.clsid
= root_entry
->data
.clsid
;
5992 data
.ctime
= root_entry
->data
.ctime
;
5993 data
.mtime
= root_entry
->data
.mtime
;
5995 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5996 root_entry
->transactedParentEntry
, &data
);
5999 /* Try to flush after updating the root storage, but if the flush fails, keep
6000 * going, on the theory that it'll either succeed later or the subsequent
6001 * writes will fail. */
6002 StorageBaseImpl_Flush(This
->transactedParent
);
6006 /* Destroy the old now-orphaned data. */
6007 for (i
=0; i
<This
->entries_size
; i
++)
6009 TransactedDirEntry
*entry
= &This
->entries
[i
];
6014 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
6015 entry
->transactedParentEntry
, zero
);
6016 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6017 entry
->transactedParentEntry
);
6018 memset(entry
, 0, sizeof(TransactedDirEntry
));
6019 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
6021 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
6023 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
6024 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6025 entry
->transactedParentEntry
);
6026 if (entry
->stream_dirty
)
6028 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
6029 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
6030 entry
->stream_dirty
= FALSE
;
6032 entry
->dirty
= FALSE
;
6033 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
6040 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6044 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6046 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6049 TRACE("<-- %#lx\n", hr
);
6053 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6056 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6057 ULARGE_INTEGER zero
;
6060 TRACE("(%p)\n", iface
);
6062 /* Destroy the open objects. */
6063 StorageBaseImpl_DeleteAll(&This
->base
);
6065 /* Clear out the scratch file. */
6067 for (i
=0; i
<This
->entries_size
; i
++)
6069 if (This
->entries
[i
].stream_dirty
)
6071 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6074 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6078 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6080 This
->firstFreeEntry
= 0;
6081 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6086 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6088 if (!This
->reverted
)
6090 TRACE("Storage invalidated (stg=%p)\n", This
);
6092 This
->reverted
= TRUE
;
6094 StorageBaseImpl_DeleteAll(This
);
6098 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6100 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6102 IStorage_Revert(&This
->base
.IStorage_iface
);
6103 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6104 IStorage_Release(&This
->scratch
->IStorage_iface
);
6105 HeapFree(GetProcessHeap(), 0, This
->entries
);
6106 HeapFree(GetProcessHeap(), 0, This
);
6109 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6111 /* We only need to flush when committing. */
6115 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6117 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6119 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6122 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6123 const DirEntry
*newData
, DirRef
*index
)
6125 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6127 TransactedDirEntry
*new_entry
;
6129 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6130 if (new_ref
== DIRENTRY_NULL
)
6131 return E_OUTOFMEMORY
;
6133 new_entry
= &This
->entries
[new_ref
];
6135 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6136 new_entry
->read
= TRUE
;
6137 new_entry
->dirty
= TRUE
;
6138 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6142 TRACE("%s l=%lx r=%lx d=%lx <-- %lx\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6147 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6148 DirRef index
, const DirEntry
*data
)
6150 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6153 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6155 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6158 TRACE("<-- %#lx\n", hr
);
6162 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6164 if (index
!= This
->base
.storageDirEntry
)
6166 This
->entries
[index
].dirty
= TRUE
;
6168 if (data
->size
.QuadPart
== 0 &&
6169 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6171 /* Since this entry is modified, and we aren't using its stream data, we
6172 * no longer care about the original entry. */
6174 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6176 if (delete_ref
!= DIRENTRY_NULL
)
6177 This
->entries
[delete_ref
].deleted
= TRUE
;
6179 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6182 TRACE("<-- S_OK\n");
6186 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6187 DirRef index
, DirEntry
*data
)
6189 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6192 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6195 TRACE("<-- %#lx\n", hr
);
6199 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6201 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6206 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6209 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6211 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6212 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6214 /* If we deleted this entry while it has stream data. We must have left the
6215 * data because some other entry is using it, and we need to leave the
6216 * original entry alone. */
6217 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6218 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6222 This
->entries
[index
].deleted
= TRUE
;
6228 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6229 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6231 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6233 if (This
->entries
[index
].stream_dirty
)
6235 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6236 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6238 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6240 /* This stream doesn't live in the parent, and we haven't allocated storage
6247 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6248 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6252 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6253 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6255 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6258 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6261 TRACE("<-- %#lx\n", hr
);
6265 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6268 TRACE("<-- %#lx\n", hr
);
6272 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6273 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6275 if (SUCCEEDED(hr
) && size
!= 0)
6276 This
->entries
[index
].data
.size
.QuadPart
= max(
6277 This
->entries
[index
].data
.size
.QuadPart
,
6278 offset
.QuadPart
+ size
);
6280 TRACE("<-- %#lx\n", hr
);
6284 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6285 DirRef index
, ULARGE_INTEGER newsize
)
6287 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6290 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6293 TRACE("<-- %#lx\n", hr
);
6297 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6300 if (newsize
.QuadPart
== 0)
6302 /* Destroy any parent references or entries in the scratch file. */
6303 if (This
->entries
[index
].stream_dirty
)
6305 ULARGE_INTEGER zero
;
6307 StorageBaseImpl_StreamSetSize(This
->scratch
,
6308 This
->entries
[index
].stream_entry
, zero
);
6309 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6310 This
->entries
[index
].stream_entry
);
6311 This
->entries
[index
].stream_dirty
= FALSE
;
6313 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6316 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6318 if (delete_ref
!= DIRENTRY_NULL
)
6319 This
->entries
[delete_ref
].deleted
= TRUE
;
6321 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6326 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6327 if (FAILED(hr
)) return hr
;
6329 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6330 This
->entries
[index
].stream_entry
, newsize
);
6334 This
->entries
[index
].data
.size
= newsize
;
6336 TRACE("<-- %#lx\n", hr
);
6340 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6341 DirRef dst
, DirRef src
)
6343 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6345 TransactedDirEntry
*dst_entry
, *src_entry
;
6347 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6350 TRACE("<-- %#lx\n", hr
);
6354 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6357 TRACE("<-- %#lx\n", hr
);
6361 dst_entry
= &This
->entries
[dst
];
6362 src_entry
= &This
->entries
[src
];
6364 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6365 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6366 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6367 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6368 dst_entry
->data
.size
= src_entry
->data
.size
;
6373 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6374 ULONG
* result
, BOOL refresh
)
6379 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6385 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6390 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6395 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6397 StorageBaseImpl_QueryInterface
,
6398 StorageBaseImpl_AddRef
,
6399 StorageBaseImpl_Release
,
6400 StorageBaseImpl_CreateStream
,
6401 StorageBaseImpl_OpenStream
,
6402 StorageBaseImpl_CreateStorage
,
6403 StorageBaseImpl_OpenStorage
,
6404 StorageBaseImpl_CopyTo
,
6405 StorageBaseImpl_MoveElementTo
,
6406 TransactedSnapshotImpl_Commit
,
6407 TransactedSnapshotImpl_Revert
,
6408 StorageBaseImpl_EnumElements
,
6409 StorageBaseImpl_DestroyElement
,
6410 StorageBaseImpl_RenameElement
,
6411 StorageBaseImpl_SetElementTimes
,
6412 StorageBaseImpl_SetClass
,
6413 StorageBaseImpl_SetStateBits
,
6414 StorageBaseImpl_Stat
6417 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6419 TransactedSnapshotImpl_Destroy
,
6420 TransactedSnapshotImpl_Invalidate
,
6421 TransactedSnapshotImpl_Flush
,
6422 TransactedSnapshotImpl_GetFilename
,
6423 TransactedSnapshotImpl_CreateDirEntry
,
6424 TransactedSnapshotImpl_WriteDirEntry
,
6425 TransactedSnapshotImpl_ReadDirEntry
,
6426 TransactedSnapshotImpl_DestroyDirEntry
,
6427 TransactedSnapshotImpl_StreamReadAt
,
6428 TransactedSnapshotImpl_StreamWriteAt
,
6429 TransactedSnapshotImpl_StreamSetSize
,
6430 TransactedSnapshotImpl_StreamLink
,
6431 TransactedSnapshotImpl_GetTransactionSig
,
6432 TransactedSnapshotImpl_SetTransactionSig
,
6433 TransactedSnapshotImpl_LockTransaction
,
6434 TransactedSnapshotImpl_UnlockTransaction
6437 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6438 TransactedSnapshotImpl
** result
)
6442 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6447 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6449 /* This is OK because the property set storage functions use the IStorage functions. */
6450 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6451 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6453 list_init(&(*result
)->base
.strmHead
);
6455 list_init(&(*result
)->base
.storageHead
);
6457 (*result
)->base
.ref
= 1;
6459 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6461 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6462 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6464 /* Create a new temporary storage to act as the scratch file. */
6465 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6467 (*result
)->scratch
= impl_from_IStorage(scratch
);
6471 ULONG num_entries
= 20;
6473 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6474 (*result
)->entries_size
= num_entries
;
6475 (*result
)->firstFreeEntry
= 0;
6477 if ((*result
)->entries
)
6479 /* parentStorage already has 1 reference, which we take over here. */
6480 (*result
)->transactedParent
= parentStorage
;
6482 parentStorage
->transactedChild
= &(*result
)->base
;
6484 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6488 IStorage_Release(scratch
);
6494 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6499 return E_OUTOFMEMORY
;
6503 /************************************************************************
6504 * TransactedSharedImpl implementation
6505 ***********************************************************************/
6507 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6509 if (!This
->reverted
)
6511 TRACE("Storage invalidated (stg=%p)\n", This
);
6513 This
->reverted
= TRUE
;
6515 StorageBaseImpl_DeleteAll(This
);
6519 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6521 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6523 TransactedSharedImpl_Invalidate(&This
->base
);
6524 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6525 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6526 HeapFree(GetProcessHeap(), 0, This
);
6529 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6531 /* We only need to flush when committing. */
6535 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6537 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6539 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6542 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6543 const DirEntry
*newData
, DirRef
*index
)
6545 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6547 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6551 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6552 DirRef index
, const DirEntry
*data
)
6554 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6556 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6560 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6561 DirRef index
, DirEntry
*data
)
6563 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6565 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6569 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6572 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6574 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6578 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6579 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6581 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6583 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6584 index
, offset
, size
, buffer
, bytesRead
);
6587 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6588 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6590 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6592 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6593 index
, offset
, size
, buffer
, bytesWritten
);
6596 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6597 DirRef index
, ULARGE_INTEGER newsize
)
6599 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6601 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6605 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6606 DirRef dst
, DirRef src
)
6608 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6610 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6614 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6615 ULONG
* result
, BOOL refresh
)
6620 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6626 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6631 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6636 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6638 DWORD grfCommitFlags
) /* [in] */
6640 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6641 DirRef new_storage_ref
, prev_storage_ref
;
6642 DirEntry src_data
, dst_data
;
6644 ULONG transactionSig
;
6646 TRACE("%p, %#lx\n", iface
, grfCommitFlags
);
6648 /* Cannot commit a read-only transacted storage */
6649 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6650 return STG_E_ACCESSDENIED
;
6652 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6653 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6656 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6659 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6660 hr
= STG_E_NOTCURRENT
;
6663 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6665 else if (hr
== E_NOTIMPL
)
6669 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6671 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6673 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6676 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6679 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6683 prev_storage_ref
= dst_data
.dirRootEntry
;
6684 dst_data
.dirRootEntry
= new_storage_ref
;
6685 dst_data
.clsid
= src_data
.clsid
;
6686 dst_data
.ctime
= src_data
.ctime
;
6687 dst_data
.mtime
= src_data
.mtime
;
6688 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6693 /* Try to flush after updating the root storage, but if the flush fails, keep
6694 * going, on the theory that it'll either succeed later or the subsequent
6695 * writes will fail. */
6696 StorageBaseImpl_Flush(This
->transactedParent
);
6698 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6702 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6704 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6707 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6711 This
->lastTransactionSig
= transactionSig
+1;
6714 TRACE("<-- %#lx\n", hr
);
6718 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6721 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6723 TRACE("(%p)\n", iface
);
6725 /* Destroy the open objects. */
6726 StorageBaseImpl_DeleteAll(&This
->base
);
6728 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6731 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6733 StorageBaseImpl_QueryInterface
,
6734 StorageBaseImpl_AddRef
,
6735 StorageBaseImpl_Release
,
6736 StorageBaseImpl_CreateStream
,
6737 StorageBaseImpl_OpenStream
,
6738 StorageBaseImpl_CreateStorage
,
6739 StorageBaseImpl_OpenStorage
,
6740 StorageBaseImpl_CopyTo
,
6741 StorageBaseImpl_MoveElementTo
,
6742 TransactedSharedImpl_Commit
,
6743 TransactedSharedImpl_Revert
,
6744 StorageBaseImpl_EnumElements
,
6745 StorageBaseImpl_DestroyElement
,
6746 StorageBaseImpl_RenameElement
,
6747 StorageBaseImpl_SetElementTimes
,
6748 StorageBaseImpl_SetClass
,
6749 StorageBaseImpl_SetStateBits
,
6750 StorageBaseImpl_Stat
6753 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6755 TransactedSharedImpl_Destroy
,
6756 TransactedSharedImpl_Invalidate
,
6757 TransactedSharedImpl_Flush
,
6758 TransactedSharedImpl_GetFilename
,
6759 TransactedSharedImpl_CreateDirEntry
,
6760 TransactedSharedImpl_WriteDirEntry
,
6761 TransactedSharedImpl_ReadDirEntry
,
6762 TransactedSharedImpl_DestroyDirEntry
,
6763 TransactedSharedImpl_StreamReadAt
,
6764 TransactedSharedImpl_StreamWriteAt
,
6765 TransactedSharedImpl_StreamSetSize
,
6766 TransactedSharedImpl_StreamLink
,
6767 TransactedSharedImpl_GetTransactionSig
,
6768 TransactedSharedImpl_SetTransactionSig
,
6769 TransactedSharedImpl_LockTransaction
,
6770 TransactedSharedImpl_UnlockTransaction
6773 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6774 TransactedSharedImpl
** result
)
6778 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6783 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6785 /* This is OK because the property set storage functions use the IStorage functions. */
6786 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6787 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6789 list_init(&(*result
)->base
.strmHead
);
6791 list_init(&(*result
)->base
.storageHead
);
6793 (*result
)->base
.ref
= 1;
6795 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6797 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6803 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6804 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6808 stgo
.ulSectorSize
= 4096;
6809 stgo
.pwcsTemplateFile
= NULL
;
6811 /* Create a new temporary storage to act as the scratch file. */
6812 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6813 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6814 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6818 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6819 parentStorage
, parentStorage
->storageDirEntry
);
6823 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6825 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6826 (*result
)->transactedParent
= parentStorage
;
6830 IStorage_Release(scratch
);
6833 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6836 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6841 return E_OUTOFMEMORY
;
6844 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6845 BOOL toplevel
, StorageBaseImpl
** result
)
6847 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6849 if (parentStorage
->openFlags
& fixme_flags
)
6851 fixme_flags
&= ~parentStorage
->openFlags
;
6852 FIXME("Unimplemented flags %lx\n", parentStorage
->openFlags
);
6855 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6856 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6857 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6859 /* Need to create a temp file for the snapshot */
6860 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6863 return TransactedSnapshotImpl_Construct(parentStorage
,
6864 (TransactedSnapshotImpl
**)result
);
6867 static HRESULT
Storage_Construct(
6875 StorageBaseImpl
** result
)
6877 StorageImpl
*newStorage
;
6878 StorageBaseImpl
*newTransactedStorage
;
6881 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6882 if (FAILED(hr
)) goto end
;
6884 if (openFlags
& STGM_TRANSACTED
)
6886 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6888 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6890 *result
= newTransactedStorage
;
6893 *result
= &newStorage
->base
;
6900 /************************************************************************
6901 * StorageUtl helper functions
6902 ***********************************************************************/
6904 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6908 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6909 *value
= lendian16toh(tmp
);
6912 void StorageUtl_WriteWord(void *buffer
, ULONG offset
, WORD value
)
6914 value
= htole16(value
);
6915 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(WORD
));
6918 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6922 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6923 *value
= lendian32toh(tmp
);
6926 void StorageUtl_WriteDWord(void *buffer
, ULONG offset
, DWORD value
)
6928 value
= htole32(value
);
6929 memcpy((BYTE
*)buffer
+ offset
, &value
, sizeof(DWORD
));
6932 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6933 ULARGE_INTEGER
* value
)
6935 #ifdef WORDS_BIGENDIAN
6938 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6939 value
->u
.LowPart
= htole32(tmp
.HighPart
);
6940 value
->u
.HighPart
= htole32(tmp
.LowPart
);
6942 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6946 void StorageUtl_WriteULargeInteger(void *buffer
, ULONG offset
, const ULARGE_INTEGER
*value
)
6948 #ifdef WORDS_BIGENDIAN
6951 tmp
.LowPart
= htole32(value
->u
.HighPart
);
6952 tmp
.HighPart
= htole32(value
->u
.LowPart
);
6953 memcpy((BYTE
*)buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6955 memcpy((BYTE
*)buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6959 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6961 StorageUtl_ReadDWord(buffer
, offset
, (DWORD
*)&value
->Data1
);
6962 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6963 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6965 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6968 void StorageUtl_WriteGUID(void *buffer
, ULONG offset
, const GUID
* value
)
6970 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6971 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6972 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6974 memcpy((BYTE
*)buffer
+ offset
+ 8, value
->Data4
, sizeof(value
->Data4
));
6977 void StorageUtl_CopyDirEntryToSTATSTG(
6978 StorageBaseImpl
* storage
,
6979 STATSTG
* destination
,
6980 const DirEntry
* source
,
6984 * The copy of the string occurs only when the flag is not set
6986 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6988 /* Use the filename for the root storage. */
6989 destination
->pwcsName
= 0;
6990 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6992 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6993 (source
->name
[0] == 0) )
6995 destination
->pwcsName
= 0;
6999 destination
->pwcsName
=
7000 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
7002 lstrcpyW(destination
->pwcsName
, source
->name
);
7005 switch (source
->stgType
)
7009 destination
->type
= STGTY_STORAGE
;
7012 destination
->type
= STGTY_STREAM
;
7015 destination
->type
= STGTY_STREAM
;
7019 destination
->cbSize
= source
->size
;
7021 currentReturnStruct->mtime = {0}; TODO
7022 currentReturnStruct->ctime = {0};
7023 currentReturnStruct->atime = {0};
7025 destination
->grfMode
= 0;
7026 destination
->grfLocksSupported
= 0;
7027 destination
->clsid
= source
->clsid
;
7028 destination
->grfStateBits
= 0;
7029 destination
->reserved
= 0;
7033 /************************************************************************
7034 * BlockChainStream implementation
7035 ***********************************************************************/
7037 /******************************************************************************
7038 * BlockChainStream_GetHeadOfChain
7040 * Returns the head of this stream chain.
7041 * Some special chains don't have directory entries, their heads are kept in
7042 * This->headOfStreamPlaceHolder.
7045 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7047 DirEntry chainEntry
;
7050 if (This
->headOfStreamPlaceHolder
!= 0)
7051 return *(This
->headOfStreamPlaceHolder
);
7053 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7055 hr
= StorageImpl_ReadDirEntry(
7056 This
->parentStorage
,
7057 This
->ownerDirEntry
,
7060 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7061 return chainEntry
.startingBlock
;
7064 return BLOCK_END_OF_CHAIN
;
7067 /* Read and save the index of all blocks in this stream. */
7068 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
7070 ULONG next_sector
, next_offset
;
7072 struct BlockChainRun
*last_run
;
7074 if (This
->indexCacheLen
== 0)
7078 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7082 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7083 next_offset
= last_run
->lastOffset
+1;
7084 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7085 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7087 if (FAILED(hr
)) return hr
;
7090 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7092 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7094 /* Add the current block to the cache. */
7095 if (This
->indexCacheSize
== 0)
7097 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7098 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7099 This
->indexCacheSize
= 16;
7101 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7103 struct BlockChainRun
*new_cache
;
7106 new_size
= This
->indexCacheSize
* 2;
7107 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7108 if (!new_cache
) return E_OUTOFMEMORY
;
7109 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7111 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7112 This
->indexCache
= new_cache
;
7113 This
->indexCacheSize
= new_size
;
7116 This
->indexCacheLen
++;
7117 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7118 last_run
->firstSector
= next_sector
;
7119 last_run
->firstOffset
= next_offset
;
7122 last_run
->lastOffset
= next_offset
;
7124 /* Find the next block. */
7126 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7127 if (FAILED(hr
)) return hr
;
7130 if (This
->indexCacheLen
)
7132 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7133 This
->numBlocks
= last_run
->lastOffset
+1;
7137 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7138 This
->numBlocks
= 0;
7144 /* Locate the nth block in this stream. */
7145 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7147 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7148 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7150 if (offset
>= This
->numBlocks
)
7151 return BLOCK_END_OF_CHAIN
;
7153 while (min_run
< max_run
)
7155 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7156 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7158 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7159 max_run
= run_to_check
-1;
7161 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7163 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7164 min_run
= run_to_check
+1;
7167 /* Block is in this run. */
7168 min_run
= max_run
= run_to_check
;
7171 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7174 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7175 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7177 BlockChainBlock
*result
=NULL
;
7181 if (This
->cachedBlocks
[i
].index
== index
)
7183 *sector
= This
->cachedBlocks
[i
].sector
;
7184 *block
= &This
->cachedBlocks
[i
];
7188 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7189 if (*sector
== BLOCK_END_OF_CHAIN
)
7190 return STG_E_DOCFILECORRUPT
;
7194 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7195 result
= &This
->cachedBlocks
[0];
7196 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7197 result
= &This
->cachedBlocks
[1];
7200 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7201 if (This
->blockToEvict
== 2)
7202 This
->blockToEvict
= 0;
7207 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7208 return STG_E_WRITEFAULT
;
7209 result
->dirty
= FALSE
;
7212 result
->read
= FALSE
;
7213 result
->index
= index
;
7214 result
->sector
= *sector
;
7221 BlockChainStream
* BlockChainStream_Construct(
7222 StorageImpl
* parentStorage
,
7223 ULONG
* headOfStreamPlaceHolder
,
7226 BlockChainStream
* newStream
;
7228 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7232 newStream
->parentStorage
= parentStorage
;
7233 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7234 newStream
->ownerDirEntry
= dirEntry
;
7235 newStream
->indexCache
= NULL
;
7236 newStream
->indexCacheLen
= 0;
7237 newStream
->indexCacheSize
= 0;
7238 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7239 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7240 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7241 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7242 newStream
->blockToEvict
= 0;
7244 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7246 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7247 HeapFree(GetProcessHeap(), 0, newStream
);
7254 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7257 if (!This
) return S_OK
;
7260 if (This
->cachedBlocks
[i
].dirty
)
7262 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7263 This
->cachedBlocks
[i
].dirty
= FALSE
;
7265 return STG_E_WRITEFAULT
;
7271 void BlockChainStream_Destroy(BlockChainStream
* This
)
7275 BlockChainStream_Flush(This
);
7276 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7278 HeapFree(GetProcessHeap(), 0, This
);
7281 /******************************************************************************
7282 * BlockChainStream_Shrink
7284 * Shrinks this chain in the big block depot.
7286 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7287 ULARGE_INTEGER newSize
)
7294 * Figure out how many blocks are needed to contain the new size
7296 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7298 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7304 * Go to the new end of chain
7306 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7308 /* Mark the new end of chain */
7309 StorageImpl_SetNextBlockInChain(
7310 This
->parentStorage
,
7312 BLOCK_END_OF_CHAIN
);
7314 This
->tailIndex
= blockIndex
;
7318 if (This
->headOfStreamPlaceHolder
!= 0)
7320 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7324 DirEntry chainEntry
;
7325 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7327 StorageImpl_ReadDirEntry(
7328 This
->parentStorage
,
7329 This
->ownerDirEntry
,
7332 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7334 StorageImpl_WriteDirEntry(
7335 This
->parentStorage
,
7336 This
->ownerDirEntry
,
7340 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7343 This
->numBlocks
= numBlocks
;
7346 * Mark the extra blocks as free
7348 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7350 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7351 StorageImpl_FreeBigBlock(This
->parentStorage
,
7352 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7353 if (last_run
->lastOffset
== last_run
->firstOffset
)
7354 This
->indexCacheLen
--;
7356 last_run
->lastOffset
--;
7360 * Reset the last accessed block cache.
7364 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7366 This
->cachedBlocks
[i
].index
= 0xffffffff;
7367 This
->cachedBlocks
[i
].dirty
= FALSE
;
7374 /******************************************************************************
7375 * BlockChainStream_Enlarge
7377 * Grows this chain in the big block depot.
7379 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7380 ULARGE_INTEGER newSize
)
7382 ULONG blockIndex
, currentBlock
;
7384 ULONG oldNumBlocks
= 0;
7386 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7389 * Empty chain. Create the head.
7391 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7393 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
, 1);
7394 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7396 BLOCK_END_OF_CHAIN
);
7398 if (This
->headOfStreamPlaceHolder
!= 0)
7400 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7404 DirEntry chainEntry
;
7405 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7407 StorageImpl_ReadDirEntry(
7408 This
->parentStorage
,
7409 This
->ownerDirEntry
,
7412 chainEntry
.startingBlock
= blockIndex
;
7414 StorageImpl_WriteDirEntry(
7415 This
->parentStorage
,
7416 This
->ownerDirEntry
,
7420 This
->tailIndex
= blockIndex
;
7421 This
->numBlocks
= 1;
7425 * Figure out how many blocks are needed to contain this stream
7427 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7429 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7433 * Go to the current end of chain
7435 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7437 currentBlock
= blockIndex
;
7439 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7442 currentBlock
= blockIndex
;
7444 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7449 This
->tailIndex
= currentBlock
;
7452 currentBlock
= This
->tailIndex
;
7453 oldNumBlocks
= This
->numBlocks
;
7456 * Add new blocks to the chain
7458 if (oldNumBlocks
< newNumBlocks
)
7460 while (oldNumBlocks
< newNumBlocks
)
7462 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
, newNumBlocks
- oldNumBlocks
);
7464 StorageImpl_SetNextBlockInChain(
7465 This
->parentStorage
,
7469 StorageImpl_SetNextBlockInChain(
7470 This
->parentStorage
,
7472 BLOCK_END_OF_CHAIN
);
7474 currentBlock
= blockIndex
;
7478 This
->tailIndex
= blockIndex
;
7479 This
->numBlocks
= newNumBlocks
;
7482 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7489 /******************************************************************************
7490 * BlockChainStream_GetSize
7492 * Returns the size of this chain.
7493 * Will return the block count if this chain doesn't have a directory entry.
7495 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7497 DirEntry chainEntry
;
7499 if(This
->headOfStreamPlaceHolder
== NULL
)
7502 * This chain has a directory entry so use the size value from there.
7504 StorageImpl_ReadDirEntry(
7505 This
->parentStorage
,
7506 This
->ownerDirEntry
,
7509 return chainEntry
.size
;
7514 * this chain is a chain that does not have a directory entry, figure out the
7515 * size by making the product number of used blocks times the
7518 ULARGE_INTEGER result
;
7520 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7521 This
->parentStorage
->bigBlockSize
;
7527 /******************************************************************************
7528 * BlockChainStream_SetSize
7530 * Sets the size of this stream. The big block depot will be updated.
7531 * The file will grow if we grow the chain.
7533 * TODO: Free the actual blocks in the file when we shrink the chain.
7534 * Currently, the blocks are still in the file. So the file size
7535 * doesn't shrink even if we shrink streams.
7537 BOOL
BlockChainStream_SetSize(
7538 BlockChainStream
* This
,
7539 ULARGE_INTEGER newSize
)
7541 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7543 if (newSize
.QuadPart
== size
.QuadPart
)
7546 if (newSize
.QuadPart
< size
.QuadPart
)
7548 BlockChainStream_Shrink(This
, newSize
);
7552 BlockChainStream_Enlarge(This
, newSize
);
7558 /******************************************************************************
7559 * BlockChainStream_ReadAt
7561 * Reads a specified number of bytes from this chain at the specified offset.
7562 * bytesRead may be NULL.
7563 * Failure will be returned if the specified number of bytes has not been read.
7565 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7566 ULARGE_INTEGER offset
,
7571 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7572 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7573 ULONG bytesToReadInBuffer
;
7576 ULARGE_INTEGER stream_size
;
7578 BlockChainBlock
*cachedBlock
;
7580 TRACE("%p, %li, %p, %lu, %p.\n",This
, offset
.LowPart
, buffer
, size
, bytesRead
);
7583 * Find the first block in the stream that contains part of the buffer.
7585 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7589 stream_size
= BlockChainStream_GetSize(This
);
7590 if (stream_size
.QuadPart
> offset
.QuadPart
)
7591 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7596 * Start reading the buffer.
7598 bufferWalker
= buffer
;
7602 ULARGE_INTEGER ulOffset
;
7606 * Calculate how many bytes we can copy from this big block.
7608 bytesToReadInBuffer
=
7609 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7611 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7618 /* Not in cache, and we're going to read past the end of the block. */
7619 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7622 StorageImpl_ReadAt(This
->parentStorage
,
7625 bytesToReadInBuffer
,
7630 if (!cachedBlock
->read
)
7633 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7634 return STG_E_READFAULT
;
7636 cachedBlock
->read
= TRUE
;
7639 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7640 bytesReadAt
= bytesToReadInBuffer
;
7643 blockNoInSequence
++;
7644 bufferWalker
+= bytesReadAt
;
7645 size
-= bytesReadAt
;
7646 *bytesRead
+= bytesReadAt
;
7647 offsetInBlock
= 0; /* There is no offset on the next block */
7649 if (bytesToReadInBuffer
!= bytesReadAt
)
7656 /******************************************************************************
7657 * BlockChainStream_WriteAt
7659 * Writes the specified number of bytes to this chain at the specified offset.
7660 * Will fail if not all specified number of bytes have been written.
7662 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7663 ULARGE_INTEGER offset
,
7666 ULONG
* bytesWritten
)
7668 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7669 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7672 const BYTE
* bufferWalker
;
7674 BlockChainBlock
*cachedBlock
;
7677 bufferWalker
= buffer
;
7681 ULARGE_INTEGER ulOffset
;
7682 DWORD bytesWrittenAt
;
7685 * Calculate how many bytes we can copy to this big block.
7688 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7690 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7692 /* BlockChainStream_SetSize should have already been called to ensure we have
7693 * enough blocks in the chain to write into */
7696 ERR("not enough blocks in chain to write data\n");
7702 /* Not in cache, and we're going to write past the end of the block. */
7703 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7706 StorageImpl_WriteAt(This
->parentStorage
,
7714 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7717 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7718 return STG_E_READFAULT
;
7721 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7722 bytesWrittenAt
= bytesToWrite
;
7723 cachedBlock
->read
= TRUE
;
7724 cachedBlock
->dirty
= TRUE
;
7727 blockNoInSequence
++;
7728 bufferWalker
+= bytesWrittenAt
;
7729 size
-= bytesWrittenAt
;
7730 *bytesWritten
+= bytesWrittenAt
;
7731 offsetInBlock
= 0; /* There is no offset on the next block */
7733 if (bytesWrittenAt
!= bytesToWrite
)
7737 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7741 /************************************************************************
7742 * SmallBlockChainStream implementation
7743 ***********************************************************************/
7745 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7746 StorageImpl
* parentStorage
,
7747 ULONG
* headOfStreamPlaceHolder
,
7750 SmallBlockChainStream
* newStream
;
7752 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7754 newStream
->parentStorage
= parentStorage
;
7755 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7756 newStream
->ownerDirEntry
= dirEntry
;
7761 void SmallBlockChainStream_Destroy(
7762 SmallBlockChainStream
* This
)
7764 HeapFree(GetProcessHeap(), 0, This
);
7767 /******************************************************************************
7768 * SmallBlockChainStream_GetHeadOfChain
7770 * Returns the head of this chain of small blocks.
7772 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7773 SmallBlockChainStream
* This
)
7775 DirEntry chainEntry
;
7778 if (This
->headOfStreamPlaceHolder
!= NULL
)
7779 return *(This
->headOfStreamPlaceHolder
);
7781 if (This
->ownerDirEntry
)
7783 hr
= StorageImpl_ReadDirEntry(
7784 This
->parentStorage
,
7785 This
->ownerDirEntry
,
7788 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7789 return chainEntry
.startingBlock
;
7792 return BLOCK_END_OF_CHAIN
;
7795 /******************************************************************************
7796 * SmallBlockChainStream_GetNextBlockInChain
7798 * Returns the index of the next small block in this chain.
7801 * - BLOCK_END_OF_CHAIN: end of this chain
7802 * - BLOCK_UNUSED: small block 'blockIndex' is free
7804 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7805 SmallBlockChainStream
* This
,
7807 ULONG
* nextBlockInChain
)
7809 ULARGE_INTEGER offsetOfBlockInDepot
;
7814 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7816 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7819 * Read those bytes in the buffer from the small block file.
7821 res
= BlockChainStream_ReadAt(
7822 This
->parentStorage
->smallBlockDepotChain
,
7823 offsetOfBlockInDepot
,
7828 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7829 res
= STG_E_READFAULT
;
7833 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7840 /******************************************************************************
7841 * SmallBlockChainStream_SetNextBlockInChain
7843 * Writes the index of the next block of the specified block in the small
7845 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7846 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7848 static void SmallBlockChainStream_SetNextBlockInChain(
7849 SmallBlockChainStream
* This
,
7853 ULARGE_INTEGER offsetOfBlockInDepot
;
7857 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7859 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
7862 * Read those bytes in the buffer from the small block file.
7864 BlockChainStream_WriteAt(
7865 This
->parentStorage
->smallBlockDepotChain
,
7866 offsetOfBlockInDepot
,
7872 /******************************************************************************
7873 * SmallBlockChainStream_FreeBlock
7875 * Flag small block 'blockIndex' as free in the small block depot.
7877 static void SmallBlockChainStream_FreeBlock(
7878 SmallBlockChainStream
* This
,
7881 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7884 /******************************************************************************
7885 * SmallBlockChainStream_GetNextFreeBlock
7887 * Returns the index of a free small block. The small block depot will be
7888 * enlarged if necessary. The small block chain will also be enlarged if
7891 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7892 SmallBlockChainStream
* This
)
7894 ULARGE_INTEGER offsetOfBlockInDepot
;
7897 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7898 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7900 ULONG smallBlocksPerBigBlock
;
7902 ULONG blocksRequired
;
7903 ULARGE_INTEGER old_size
, size_required
;
7905 offsetOfBlockInDepot
.HighPart
= 0;
7908 * Scan the small block depot for a free block
7910 while (nextBlockIndex
!= BLOCK_UNUSED
)
7912 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7914 res
= BlockChainStream_ReadAt(
7915 This
->parentStorage
->smallBlockDepotChain
,
7916 offsetOfBlockInDepot
,
7922 * If we run out of space for the small block depot, enlarge it
7924 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7926 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7928 if (nextBlockIndex
!= BLOCK_UNUSED
)
7934 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7936 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7937 ULARGE_INTEGER newSize
, offset
;
7940 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7941 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7944 * Initialize all the small blocks to free
7946 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7947 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7948 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7949 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7951 StorageImpl_SaveFileHeader(This
->parentStorage
);
7955 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7957 smallBlocksPerBigBlock
=
7958 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7961 * Verify if we have to allocate big blocks to contain small blocks
7963 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7965 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7967 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7969 if (size_required
.QuadPart
> old_size
.QuadPart
)
7971 BlockChainStream_SetSize(
7972 This
->parentStorage
->smallBlockRootChain
,
7975 StorageImpl_ReadDirEntry(
7976 This
->parentStorage
,
7977 This
->parentStorage
->base
.storageDirEntry
,
7980 rootEntry
.size
= size_required
;
7982 StorageImpl_WriteDirEntry(
7983 This
->parentStorage
,
7984 This
->parentStorage
->base
.storageDirEntry
,
7991 /******************************************************************************
7992 * SmallBlockChainStream_ReadAt
7994 * Reads a specified number of bytes from this chain at the specified offset.
7995 * bytesRead may be NULL.
7996 * Failure will be returned if the specified number of bytes has not been read.
7998 HRESULT
SmallBlockChainStream_ReadAt(
7999 SmallBlockChainStream
* This
,
8000 ULARGE_INTEGER offset
,
8006 ULARGE_INTEGER offsetInBigBlockFile
;
8007 ULONG blockNoInSequence
=
8008 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8010 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
8011 ULONG bytesToReadInBuffer
;
8013 ULONG bytesReadFromBigBlockFile
;
8015 ULARGE_INTEGER stream_size
;
8018 * This should never happen on a small block file.
8020 assert(offset
.HighPart
==0);
8024 stream_size
= SmallBlockChainStream_GetSize(This
);
8025 if (stream_size
.QuadPart
> offset
.QuadPart
)
8026 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
8031 * Find the first block in the stream that contains part of the buffer.
8033 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8035 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8037 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8040 blockNoInSequence
--;
8044 * Start reading the buffer.
8046 bufferWalker
= buffer
;
8048 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8051 * Calculate how many bytes we can copy from this small block.
8053 bytesToReadInBuffer
=
8054 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8057 * Calculate the offset of the small block in the small block file.
8059 offsetInBigBlockFile
.QuadPart
=
8060 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8062 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8065 * Read those bytes in the buffer from the small block file.
8066 * The small block has already been identified so it shouldn't fail
8067 * unless the file is corrupt.
8069 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
8070 offsetInBigBlockFile
,
8071 bytesToReadInBuffer
,
8073 &bytesReadFromBigBlockFile
);
8078 if (!bytesReadFromBigBlockFile
)
8079 return STG_E_DOCFILECORRUPT
;
8082 * Step to the next big block.
8084 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8086 return STG_E_DOCFILECORRUPT
;
8088 bufferWalker
+= bytesReadFromBigBlockFile
;
8089 size
-= bytesReadFromBigBlockFile
;
8090 *bytesRead
+= bytesReadFromBigBlockFile
;
8091 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8097 /******************************************************************************
8098 * SmallBlockChainStream_WriteAt
8100 * Writes the specified number of bytes to this chain at the specified offset.
8101 * Will fail if not all specified number of bytes have been written.
8103 HRESULT
SmallBlockChainStream_WriteAt(
8104 SmallBlockChainStream
* This
,
8105 ULARGE_INTEGER offset
,
8108 ULONG
* bytesWritten
)
8110 ULARGE_INTEGER offsetInBigBlockFile
;
8111 ULONG blockNoInSequence
=
8112 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8114 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
8115 ULONG bytesToWriteInBuffer
;
8117 ULONG bytesWrittenToBigBlockFile
;
8118 const BYTE
* bufferWalker
;
8122 * This should never happen on a small block file.
8124 assert(offset
.HighPart
==0);
8127 * Find the first block in the stream that contains part of the buffer.
8129 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8131 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8133 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8134 return STG_E_DOCFILECORRUPT
;
8135 blockNoInSequence
--;
8139 * Start writing the buffer.
8142 bufferWalker
= buffer
;
8143 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8146 * Calculate how many bytes we can copy to this small block.
8148 bytesToWriteInBuffer
=
8149 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8152 * Calculate the offset of the small block in the small block file.
8154 offsetInBigBlockFile
.QuadPart
=
8155 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8157 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8160 * Write those bytes in the buffer to the small block file.
8162 res
= BlockChainStream_WriteAt(
8163 This
->parentStorage
->smallBlockRootChain
,
8164 offsetInBigBlockFile
,
8165 bytesToWriteInBuffer
,
8167 &bytesWrittenToBigBlockFile
);
8172 * Step to the next big block.
8174 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8177 bufferWalker
+= bytesWrittenToBigBlockFile
;
8178 size
-= bytesWrittenToBigBlockFile
;
8179 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8180 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8183 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8186 /******************************************************************************
8187 * SmallBlockChainStream_Shrink
8189 * Shrinks this chain in the small block depot.
8191 static BOOL
SmallBlockChainStream_Shrink(
8192 SmallBlockChainStream
* This
,
8193 ULARGE_INTEGER newSize
)
8195 ULONG blockIndex
, extraBlock
;
8199 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8201 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8204 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8207 * Go to the new end of chain
8209 while (count
< numBlocks
)
8211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8218 * If the count is 0, we have a special case, the head of the chain was
8223 DirEntry chainEntry
;
8225 StorageImpl_ReadDirEntry(This
->parentStorage
,
8226 This
->ownerDirEntry
,
8229 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8231 StorageImpl_WriteDirEntry(This
->parentStorage
,
8232 This
->ownerDirEntry
,
8236 * We start freeing the chain at the head block.
8238 extraBlock
= blockIndex
;
8242 /* Get the next block before marking the new end */
8243 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8247 /* Mark the new end of chain */
8248 SmallBlockChainStream_SetNextBlockInChain(
8251 BLOCK_END_OF_CHAIN
);
8255 * Mark the extra blocks as free
8257 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8259 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8262 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8263 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8264 extraBlock
= blockIndex
;
8270 /******************************************************************************
8271 * SmallBlockChainStream_Enlarge
8273 * Grows this chain in the small block depot.
8275 static BOOL
SmallBlockChainStream_Enlarge(
8276 SmallBlockChainStream
* This
,
8277 ULARGE_INTEGER newSize
)
8279 ULONG blockIndex
, currentBlock
;
8281 ULONG oldNumBlocks
= 0;
8283 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8286 * Empty chain. Create the head.
8288 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8290 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8291 SmallBlockChainStream_SetNextBlockInChain(
8294 BLOCK_END_OF_CHAIN
);
8296 if (This
->headOfStreamPlaceHolder
!= NULL
)
8298 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8302 DirEntry chainEntry
;
8304 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8307 chainEntry
.startingBlock
= blockIndex
;
8309 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8314 currentBlock
= blockIndex
;
8317 * Figure out how many blocks are needed to contain this stream
8319 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8321 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8325 * Go to the current end of chain
8327 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8330 currentBlock
= blockIndex
;
8331 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8336 * Add new blocks to the chain
8338 while (oldNumBlocks
< newNumBlocks
)
8340 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8341 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8343 SmallBlockChainStream_SetNextBlockInChain(
8346 BLOCK_END_OF_CHAIN
);
8348 currentBlock
= blockIndex
;
8355 /******************************************************************************
8356 * SmallBlockChainStream_SetSize
8358 * Sets the size of this stream.
8359 * The file will grow if we grow the chain.
8361 * TODO: Free the actual blocks in the file when we shrink the chain.
8362 * Currently, the blocks are still in the file. So the file size
8363 * doesn't shrink even if we shrink streams.
8365 BOOL
SmallBlockChainStream_SetSize(
8366 SmallBlockChainStream
* This
,
8367 ULARGE_INTEGER newSize
)
8369 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8371 if (newSize
.LowPart
== size
.LowPart
)
8374 if (newSize
.LowPart
< size
.LowPart
)
8376 SmallBlockChainStream_Shrink(This
, newSize
);
8380 SmallBlockChainStream_Enlarge(This
, newSize
);
8386 /******************************************************************************
8387 * SmallBlockChainStream_GetCount
8389 * Returns the number of small blocks that comprises this chain.
8390 * This is not the size of the stream as the last block may not be full!
8393 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8398 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8400 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8404 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8405 blockIndex
, &blockIndex
)))
8412 /******************************************************************************
8413 * SmallBlockChainStream_GetSize
8415 * Returns the size of this chain.
8417 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8419 DirEntry chainEntry
;
8421 if(This
->headOfStreamPlaceHolder
!= NULL
)
8423 ULARGE_INTEGER result
;
8424 result
.HighPart
= 0;
8426 result
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8427 This
->parentStorage
->smallBlockSize
;
8432 StorageImpl_ReadDirEntry(
8433 This
->parentStorage
,
8434 This
->ownerDirEntry
,
8437 return chainEntry
.size
;
8441 /************************************************************************
8442 * Miscellaneous storage functions
8443 ***********************************************************************/
8445 static HRESULT
create_storagefile(
8449 STGOPTIONS
* pStgOptions
,
8453 StorageBaseImpl
* newStorage
= 0;
8454 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8455 HRESULT hr
= STG_E_INVALIDFLAG
;
8459 DWORD fileAttributes
;
8460 WCHAR tempFileName
[MAX_PATH
];
8463 return STG_E_INVALIDPOINTER
;
8465 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8466 return STG_E_INVALIDPARAMETER
;
8468 /* if no share mode given then DENY_NONE is the default */
8469 if (STGM_SHARE_MODE(grfMode
) == 0)
8470 grfMode
|= STGM_SHARE_DENY_NONE
;
8472 if ( FAILED( validateSTGM(grfMode
) ))
8475 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8476 switch(STGM_ACCESS_MODE(grfMode
))
8479 case STGM_READWRITE
:
8485 /* in direct mode, can only use SHARE_EXCLUSIVE */
8486 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8489 /* but in transacted mode, any share mode is valid */
8492 * Generate a unique name.
8496 WCHAR tempPath
[MAX_PATH
];
8498 memset(tempPath
, 0, sizeof(tempPath
));
8499 memset(tempFileName
, 0, sizeof(tempFileName
));
8501 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8504 if (GetTempFileNameW(tempPath
, L
"STO", 0, tempFileName
) != 0)
8505 pwcsName
= tempFileName
;
8508 hr
= STG_E_INSUFFICIENTMEMORY
;
8512 creationMode
= TRUNCATE_EXISTING
;
8516 creationMode
= GetCreationModeFromSTGM(grfMode
);
8520 * Interpret the STGM value grfMode
8522 shareMode
= GetShareModeFromSTGM(grfMode
);
8523 accessMode
= GetAccessModeFromSTGM(grfMode
);
8525 if (grfMode
& STGM_DELETEONRELEASE
)
8526 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8528 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8532 hFile
= CreateFileW(pwcsName
,
8540 if (hFile
== INVALID_HANDLE_VALUE
)
8542 if(GetLastError() == ERROR_FILE_EXISTS
)
8543 hr
= STG_E_FILEALREADYEXISTS
;
8550 * Allocate and initialize the new IStorage object.
8552 hr
= Storage_Construct(
8559 pStgOptions
->ulSectorSize
,
8567 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8568 IStorage_Release(&newStorage
->IStorage_iface
);
8571 TRACE("<-- %p r = %#lx\n", *ppstgOpen
, hr
);
8576 /******************************************************************************
8577 * StgCreateDocfile [OLE32.@]
8578 * Creates a new compound file storage object
8581 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8582 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8583 * reserved [ ?] unused?, usually 0
8584 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8587 * S_OK if the file was successfully created
8588 * some STG_E_ value if error
8590 * if pwcsName is NULL, create file with new unique name
8591 * the function can returns
8592 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8595 HRESULT WINAPI
StgCreateDocfile(
8599 IStorage
**ppstgOpen
)
8601 STGOPTIONS stgoptions
= {1, 0, 512};
8603 TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName
), grfMode
, reserved
, ppstgOpen
);
8606 return STG_E_INVALIDPOINTER
;
8608 return STG_E_INVALIDPARAMETER
;
8610 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8613 /******************************************************************************
8614 * StgCreateStorageEx [OLE32.@]
8616 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8618 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName
),
8619 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8621 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8623 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8624 return STG_E_INVALIDPARAMETER
;
8627 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8629 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8630 return STG_E_INVALIDPARAMETER
;
8633 if (stgfmt
== STGFMT_FILE
)
8635 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8636 return STG_E_INVALIDPARAMETER
;
8639 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8641 STGOPTIONS defaultOptions
= {1, 0, 512};
8643 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8644 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8648 ERR("Invalid stgfmt argument\n");
8649 return STG_E_INVALIDPARAMETER
;
8652 /******************************************************************************
8653 * StgCreatePropSetStg [OLE32.@]
8655 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8656 IPropertySetStorage
**propset
)
8658 TRACE("%p, %#lx, %p.\n", pstg
, reserved
, propset
);
8660 return STG_E_INVALIDPARAMETER
;
8662 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8665 /******************************************************************************
8666 * StgOpenStorageEx [OLE32.@]
8668 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8670 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName
),
8671 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8673 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8675 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8676 return STG_E_INVALIDPARAMETER
;
8682 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8683 return STG_E_INVALIDPARAMETER
;
8685 case STGFMT_STORAGE
:
8688 case STGFMT_DOCFILE
:
8689 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8691 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8692 return STG_E_INVALIDPARAMETER
;
8694 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8698 WARN("STGFMT_ANY assuming storage\n");
8702 return STG_E_INVALIDPARAMETER
;
8705 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8709 /******************************************************************************
8710 * StgOpenStorage [OLE32.@]
8712 HRESULT WINAPI
StgOpenStorage(
8713 const OLECHAR
*pwcsName
,
8714 IStorage
*pstgPriority
,
8718 IStorage
**ppstgOpen
)
8720 StorageBaseImpl
* newStorage
= 0;
8725 LPWSTR temp_name
= NULL
;
8727 TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8728 snbExclude
, reserved
, ppstgOpen
);
8732 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8733 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8734 if (FAILED(hr
)) goto end
;
8735 pwcsName
= temp_name
;
8736 TRACE("using filename %s\n", debugstr_w(temp_name
));
8741 hr
= STG_E_INVALIDNAME
;
8747 hr
= STG_E_INVALIDPOINTER
;
8753 hr
= STG_E_INVALIDPARAMETER
;
8757 if (grfMode
& STGM_PRIORITY
)
8759 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8760 return STG_E_INVALIDFLAG
;
8761 if (grfMode
& STGM_DELETEONRELEASE
)
8762 return STG_E_INVALIDFUNCTION
;
8763 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8764 return STG_E_INVALIDFLAG
;
8765 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8766 grfMode
|= STGM_SHARE_DENY_NONE
;
8770 * Validate the sharing mode
8772 if (grfMode
& STGM_DIRECT_SWMR
)
8774 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8775 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8777 hr
= STG_E_INVALIDFLAG
;
8781 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8782 switch(STGM_SHARE_MODE(grfMode
))
8784 case STGM_SHARE_EXCLUSIVE
:
8785 case STGM_SHARE_DENY_WRITE
:
8788 hr
= STG_E_INVALIDFLAG
;
8792 if ( FAILED( validateSTGM(grfMode
) ) ||
8793 (grfMode
&STGM_CREATE
))
8795 hr
= STG_E_INVALIDFLAG
;
8799 /* shared reading requires transacted or single writer mode */
8800 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8801 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8802 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8804 hr
= STG_E_INVALIDFLAG
;
8809 * Interpret the STGM value grfMode
8811 shareMode
= GetShareModeFromSTGM(grfMode
);
8812 accessMode
= GetAccessModeFromSTGM(grfMode
);
8816 hFile
= CreateFileW( pwcsName
,
8821 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8824 if (hFile
==INVALID_HANDLE_VALUE
)
8826 DWORD last_error
= GetLastError();
8832 case ERROR_FILE_NOT_FOUND
:
8833 hr
= STG_E_FILENOTFOUND
;
8836 case ERROR_PATH_NOT_FOUND
:
8837 hr
= STG_E_PATHNOTFOUND
;
8840 case ERROR_ACCESS_DENIED
:
8841 case ERROR_WRITE_PROTECT
:
8842 hr
= STG_E_ACCESSDENIED
;
8845 case ERROR_SHARING_VIOLATION
:
8846 hr
= STG_E_SHAREVIOLATION
;
8857 * Refuse to open the file if it's too small to be a structured storage file
8858 * FIXME: verify the file when reading instead of here
8860 if (GetFileSize(hFile
, NULL
) < HEADER_SIZE
)
8863 hr
= STG_E_FILEALREADYEXISTS
;
8868 * Allocate and initialize the new IStorage object.
8870 hr
= Storage_Construct(
8883 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8885 if(hr
== STG_E_INVALIDHEADER
)
8886 hr
= STG_E_FILEALREADYEXISTS
;
8890 *ppstgOpen
= &newStorage
->IStorage_iface
;
8893 CoTaskMemFree(temp_name
);
8894 if (pstgPriority
) IStorage_Release(pstgPriority
);
8895 TRACE("<-- %#lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8899 /******************************************************************************
8900 * StgCreateDocfileOnILockBytes [OLE32.@]
8902 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8906 IStorage
** ppstgOpen
)
8908 StorageBaseImpl
* newStorage
= 0;
8911 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8912 return STG_E_INVALIDPOINTER
;
8915 * Allocate and initialize the new IStorage object.
8917 hr
= Storage_Construct(
8932 *ppstgOpen
= &newStorage
->IStorage_iface
;
8937 /******************************************************************************
8938 * StgOpenStorageOnILockBytes [OLE32.@]
8940 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8942 IStorage
*pstgPriority
,
8946 IStorage
**ppstgOpen
)
8948 StorageBaseImpl
* newStorage
= 0;
8951 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8952 return STG_E_INVALIDPOINTER
;
8954 if ( FAILED( validateSTGM(grfMode
) ))
8955 return STG_E_INVALIDFLAG
;
8960 * Allocate and initialize the new IStorage object.
8962 hr
= Storage_Construct(
8977 *ppstgOpen
= &newStorage
->IStorage_iface
;
8982 /******************************************************************************
8983 * StgSetTimes [ole32.@]
8984 * StgSetTimes [OLE32.@]
8988 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8989 FILETIME
const *patime
, FILETIME
const *pmtime
)
8991 IStorage
*stg
= NULL
;
8994 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8996 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
9000 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
9001 IStorage_Release(stg
);
9007 /******************************************************************************
9008 * StgIsStorageILockBytes [OLE32.@]
9010 * Determines if the ILockBytes contains a storage object.
9012 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
9014 BYTE sig
[sizeof(STORAGE_magic
)];
9015 ULARGE_INTEGER offset
;
9018 offset
.HighPart
= 0;
9021 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
9023 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
9029 /******************************************************************************
9030 * WriteClassStg [OLE32.@]
9032 * This method will store the specified CLSID in the specified storage object
9034 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
9037 return E_INVALIDARG
;
9040 return STG_E_INVALIDPOINTER
;
9042 return IStorage_SetClass(pStg
, rclsid
);
9045 /***********************************************************************
9046 * ReadClassStg (OLE32.@)
9048 * This method reads the CLSID previously written to a storage object with
9049 * the WriteClassStg.
9052 * pstg [I] IStorage pointer
9053 * pclsid [O] Pointer to where the CLSID is written
9057 * Failure: HRESULT code.
9059 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
9064 TRACE("(%p, %p)\n", pstg
, pclsid
);
9066 if(!pstg
|| !pclsid
)
9067 return E_INVALIDARG
;
9070 * read a STATSTG structure (contains the clsid) from the storage
9072 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9075 *pclsid
=pstatstg
.clsid
;
9080 /***********************************************************************
9081 * OleLoadFromStream (OLE32.@)
9083 * This function loads an object from stream
9085 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9089 LPPERSISTSTREAM xstm
;
9091 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9093 res
=ReadClassStm(pStm
,&clsid
);
9096 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9099 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9101 IUnknown_Release((IUnknown
*)*ppvObj
);
9104 res
=IPersistStream_Load(xstm
,pStm
);
9105 IPersistStream_Release(xstm
);
9106 /* FIXME: all refcounts ok at this point? I think they should be:
9109 * xstm : 0 (released)
9114 /***********************************************************************
9115 * OleSaveToStream (OLE32.@)
9117 * This function saves an object with the IPersistStream interface on it
9118 * to the specified stream.
9120 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9126 TRACE("(%p,%p)\n",pPStm
,pStm
);
9128 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9130 if (SUCCEEDED(res
)){
9132 res
=WriteClassStm(pStm
,&clsid
);
9136 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9139 TRACE("Finished Save\n");
9143 /*************************************************************************
9144 * STORAGE_CreateOleStream [Internal]
9146 * Creates the "\001OLE" stream in the IStorage if necessary.
9149 * storage [I] Dest storage to create the stream in
9150 * flags [I] flags to be set for newly created stream
9153 * HRESULT return value
9157 * This stream is still unknown, MS Word seems to have extra data
9158 * but since the data is stored in the OLESTREAM there should be
9159 * no need to recreate the stream. If the stream is manually
9160 * deleted it will create it with this default data.
9163 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9165 static const DWORD version_magic
= 0x02000001;
9169 hr
= IStorage_CreateStream(storage
, L
"\1Ole", STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9172 struct empty_1ole_stream
{
9173 DWORD version_magic
;
9175 DWORD update_options
;
9177 DWORD mon_stream_size
;
9179 struct empty_1ole_stream stream_data
;
9181 stream_data
.version_magic
= version_magic
;
9182 stream_data
.flags
= flags
;
9183 stream_data
.update_options
= 0;
9184 stream_data
.reserved
= 0;
9185 stream_data
.mon_stream_size
= 0;
9187 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9188 IStream_Release(stream
);
9194 /* write a string to a stream, preceded by its length */
9195 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9202 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9203 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9208 str
= CoTaskMemAlloc( len
);
9209 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9210 r
= IStream_Write( stm
, str
, len
, NULL
);
9211 CoTaskMemFree( str
);
9215 /* read a string preceded by its length from a stream */
9216 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9219 DWORD len
, count
= 0;
9223 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9226 if( count
!= sizeof(len
) )
9227 return E_OUTOFMEMORY
;
9229 TRACE("%ld bytes\n",len
);
9231 str
= CoTaskMemAlloc( len
);
9233 return E_OUTOFMEMORY
;
9235 r
= IStream_Read( stm
, str
, len
, &count
);
9238 CoTaskMemFree( str
);
9243 CoTaskMemFree( str
);
9244 return E_OUTOFMEMORY
;
9247 TRACE("Read string %s\n",debugstr_an(str
,len
));
9249 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9250 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9253 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9256 CoTaskMemFree( str
);
9264 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9265 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9270 static const BYTE unknown1
[12] =
9271 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9272 0xFF, 0xFF, 0xFF, 0xFF};
9273 static const BYTE unknown2
[16] =
9274 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9277 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9278 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9279 debugstr_w(szProgIDName
));
9281 /* Create a CompObj stream */
9282 r
= IStorage_CreateStream(pstg
, L
"\1CompObj",
9283 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9287 /* Write CompObj Structure to stream */
9288 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9290 if( SUCCEEDED( r
) )
9291 r
= WriteClassStm( pstm
, clsid
);
9293 if( SUCCEEDED( r
) )
9294 r
= STREAM_WriteString( pstm
, lpszUserType
);
9295 if( SUCCEEDED( r
) )
9296 r
= STREAM_WriteString( pstm
, szClipName
);
9297 if( SUCCEEDED( r
) )
9298 r
= STREAM_WriteString( pstm
, szProgIDName
);
9299 if( SUCCEEDED( r
) )
9300 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9302 IStream_Release( pstm
);
9307 /***********************************************************************
9308 * WriteFmtUserTypeStg (OLE32.@)
9310 HRESULT WINAPI
WriteFmtUserTypeStg(
9311 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9315 WCHAR szwClipName
[0x40];
9317 LPWSTR wstrProgID
= NULL
;
9320 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9322 /* get the clipboard format name */
9325 n
= GetClipboardFormatNameW(cf
, szwClipName
, ARRAY_SIZE(szwClipName
));
9329 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9331 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9337 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9339 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9341 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9342 cf
? szwClipName
: NULL
, wstrProgID
);
9344 CoTaskMemFree(wstrProgID
);
9350 /******************************************************************************
9351 * ReadFmtUserTypeStg [OLE32.@]
9353 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9357 unsigned char unknown1
[12];
9358 unsigned char unknown2
[16];
9360 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9363 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9365 r
= IStorage_OpenStream( pstg
, L
"\1CompObj", NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9368 WARN("Failed to open stream r = %#lx\n", r
);
9372 /* read the various parts of the structure */
9373 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9374 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9376 r
= ReadClassStm( stm
, &clsid
);
9380 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9384 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9388 r
= STREAM_ReadString( stm
, &szProgIDName
);
9392 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9393 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9396 /* ok, success... now we just need to store what we found */
9398 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9400 if( lplpszUserType
)
9402 *lplpszUserType
= szCLSIDName
;
9407 CoTaskMemFree( szCLSIDName
);
9408 CoTaskMemFree( szOleTypeName
);
9409 CoTaskMemFree( szProgIDName
);
9410 IStream_Release( stm
);
9415 /******************************************************************************
9416 * StgIsStorageFile [OLE32.@]
9417 * Verify if the file contains a storage object
9423 * S_OK if file has magic bytes as a storage object
9424 * S_FALSE if file is not storage
9427 StgIsStorageFile(LPCOLESTR fn
)
9433 TRACE("%s\n", debugstr_w(fn
));
9434 hf
= CreateFileW(fn
, GENERIC_READ
,
9435 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9436 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9438 if (hf
== INVALID_HANDLE_VALUE
)
9439 return STG_E_FILENOTFOUND
;
9441 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9443 WARN(" unable to read file\n");
9450 if (bytes_read
!= 8) {
9451 TRACE(" too short\n");
9455 if (!memcmp(magic
,STORAGE_magic
,8)) {
9460 TRACE(" -> Invalid header.\n");
9464 /***********************************************************************
9465 * WriteClassStm (OLE32.@)
9467 * Writes a CLSID to a stream.
9470 * pStm [I] Stream to write to.
9471 * rclsid [I] CLSID to write.
9475 * Failure: HRESULT code.
9477 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9479 TRACE("(%p,%p)\n",pStm
,rclsid
);
9481 if (!pStm
|| !rclsid
)
9482 return E_INVALIDARG
;
9484 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9487 /***********************************************************************
9488 * ReadClassStm (OLE32.@)
9490 * Reads a CLSID from a stream.
9493 * pStm [I] Stream to read from.
9494 * rclsid [O] CLSID to read.
9498 * Failure: HRESULT code.
9500 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9505 TRACE("(%p,%p)\n",pStm
,pclsid
);
9507 if (!pStm
|| !pclsid
)
9508 return E_INVALIDARG
;
9510 /* clear the output args */
9511 *pclsid
= CLSID_NULL
;
9513 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9518 if (nbByte
!= sizeof(CLSID
))
9519 return STG_E_READFAULT
;
9525 /************************************************************************
9526 * OleConvert Functions
9527 ***********************************************************************/
9529 #define OLESTREAM_ID 0x501
9530 #define OLESTREAM_MAX_STR_LEN 255
9532 /* OLESTREAM memory structure to use for Get and Put Routines */
9537 DWORD dwOleTypeNameLength
;
9538 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9539 CHAR
*pstrOleObjFileName
;
9540 DWORD dwOleObjFileNameLength
;
9541 DWORD dwMetaFileWidth
;
9542 DWORD dwMetaFileHeight
;
9543 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9546 } OLECONVERT_OLESTREAM_DATA
;
9548 /* CompObj Stream structure */
9551 BYTE byUnknown1
[12];
9553 DWORD dwCLSIDNameLength
;
9554 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9555 DWORD dwOleTypeNameLength
;
9556 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9557 DWORD dwProgIDNameLength
;
9558 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9559 BYTE byUnknown2
[16];
9560 } OLECONVERT_ISTORAGE_COMPOBJ
;
9562 /* Ole Presentation Stream structure */
9565 BYTE byUnknown1
[28];
9570 } OLECONVERT_ISTORAGE_OLEPRES
;
9573 /*************************************************************************
9574 * OLECONVERT_LoadOLE10 [Internal]
9576 * Loads the OLE10 STREAM to memory
9579 * pOleStream [I] The OLESTREAM
9580 * pData [I] Data Structure for the OLESTREAM Data
9584 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9585 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9588 * This function is used by OleConvertOLESTREAMToIStorage only.
9590 * Memory allocated for pData must be freed by the caller
9592 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStream1
)
9595 HRESULT hRes
= S_OK
;
9599 pData
->pData
= NULL
;
9600 pData
->pstrOleObjFileName
= NULL
;
9602 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9605 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9606 if(dwSize
!= sizeof(pData
->dwOleID
))
9608 hRes
= CONVERT10_E_OLESTREAM_GET
;
9610 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9612 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9623 /* Get the TypeID... more info needed for this field */
9624 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9625 if(dwSize
!= sizeof(pData
->dwTypeID
))
9627 hRes
= CONVERT10_E_OLESTREAM_GET
;
9632 if(pData
->dwTypeID
!= 0)
9634 /* Get the length of the OleTypeName */
9635 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9636 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9638 hRes
= CONVERT10_E_OLESTREAM_GET
;
9643 if(pData
->dwOleTypeNameLength
> 0)
9645 /* Get the OleTypeName */
9646 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9647 if(dwSize
!= pData
->dwOleTypeNameLength
)
9649 hRes
= CONVERT10_E_OLESTREAM_GET
;
9655 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9656 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9658 hRes
= CONVERT10_E_OLESTREAM_GET
;
9662 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9663 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9664 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9665 if(pData
->pstrOleObjFileName
)
9667 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9668 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9670 hRes
= CONVERT10_E_OLESTREAM_GET
;
9674 hRes
= CONVERT10_E_OLESTREAM_GET
;
9679 /* Get the Width of the Metafile */
9680 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9681 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9683 hRes
= CONVERT10_E_OLESTREAM_GET
;
9687 /* Get the Height of the Metafile */
9688 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9689 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9691 hRes
= CONVERT10_E_OLESTREAM_GET
;
9697 /* Get the Length of the Data */
9698 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9699 if(dwSize
!= sizeof(pData
->dwDataLength
))
9701 hRes
= CONVERT10_E_OLESTREAM_GET
;
9705 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9707 if(!bStream1
) /* if it is a second OLE stream data */
9709 pData
->dwDataLength
-= 8;
9710 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9711 if(dwSize
!= sizeof(pData
->strUnknown
))
9713 hRes
= CONVERT10_E_OLESTREAM_GET
;
9719 if(pData
->dwDataLength
> 0)
9721 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9723 /* Get Data (ex. IStorage, Metafile, or BMP) */
9726 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9727 if(dwSize
!= pData
->dwDataLength
)
9729 hRes
= CONVERT10_E_OLESTREAM_GET
;
9734 hRes
= CONVERT10_E_OLESTREAM_GET
;
9743 /*************************************************************************
9744 * OLECONVERT_SaveOLE10 [Internal]
9746 * Saves the OLE10 STREAM From memory
9749 * pData [I] Data Structure for the OLESTREAM Data
9750 * pOleStream [I] The OLESTREAM to save
9754 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9757 * This function is used by OleConvertIStorageToOLESTREAM only.
9760 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9763 HRESULT hRes
= S_OK
;
9767 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9768 if(dwSize
!= sizeof(pData
->dwOleID
))
9770 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9775 /* Set the TypeID */
9776 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9777 if(dwSize
!= sizeof(pData
->dwTypeID
))
9779 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9783 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9785 /* Set the Length of the OleTypeName */
9786 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9787 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9789 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9794 if(pData
->dwOleTypeNameLength
> 0)
9796 /* Set the OleTypeName */
9797 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9798 if(dwSize
!= pData
->dwOleTypeNameLength
)
9800 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9807 /* Set the width of the Metafile */
9808 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9809 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9811 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9817 /* Set the height of the Metafile */
9818 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9819 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9821 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9827 /* Set the length of the Data */
9828 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9829 if(dwSize
!= sizeof(pData
->dwDataLength
))
9831 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9837 if(pData
->dwDataLength
> 0)
9839 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9840 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9841 if(dwSize
!= pData
->dwDataLength
)
9843 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9851 /*************************************************************************
9852 * OLECONVERT_GetOLE20FromOLE10[Internal]
9854 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9855 * opens it, and copies the content to the dest IStorage for
9856 * OleConvertOLESTREAMToIStorage
9860 * pDestStorage [I] The IStorage to copy the data to
9861 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9862 * nBufferLength [I] The size of the buffer
9871 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9875 IStorage
*pTempStorage
;
9876 DWORD dwNumOfBytesWritten
;
9877 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9879 /* Create a temp File */
9880 GetTempPathW(MAX_PATH
, wstrTempDir
);
9881 GetTempFileNameW(wstrTempDir
, L
"sis", 0, wstrTempFile
);
9882 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9884 if(hFile
!= INVALID_HANDLE_VALUE
)
9886 /* Write IStorage Data to File */
9887 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9890 /* Open and copy temp storage to the Dest Storage */
9891 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9894 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9895 IStorage_Release(pTempStorage
);
9897 DeleteFileW(wstrTempFile
);
9902 /*************************************************************************
9903 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9905 * Saves the OLE10 STREAM From memory
9908 * pStorage [I] The Src IStorage to copy
9909 * pData [I] The Dest Memory to write to.
9912 * The size in bytes allocated for pData
9915 * Memory allocated for pData must be freed by the caller
9917 * Used by OleConvertIStorageToOLESTREAM only.
9920 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9924 DWORD nDataLength
= 0;
9925 IStorage
*pTempStorage
;
9926 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9930 /* Create temp Storage */
9931 GetTempPathW(MAX_PATH
, wstrTempDir
);
9932 GetTempFileNameW(wstrTempDir
, L
"sis", 0, wstrTempFile
);
9933 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9937 /* Copy Src Storage to the Temp Storage */
9938 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9939 IStorage_Release(pTempStorage
);
9941 /* Open Temp Storage as a file and copy to memory */
9942 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9943 if(hFile
!= INVALID_HANDLE_VALUE
)
9945 nDataLength
= GetFileSize(hFile
, NULL
);
9946 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9947 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9950 DeleteFileW(wstrTempFile
);
9955 /*************************************************************************
9956 * OLECONVERT_CreateCompObjStream [Internal]
9958 * Creates a "\001CompObj" is the destination IStorage if necessary.
9961 * pStorage [I] The dest IStorage to create the CompObj Stream
9963 * strOleTypeName [I] The ProgID
9967 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9970 * This function is used by OleConvertOLESTREAMToIStorage only.
9972 * The stream data is stored in the OLESTREAM and there should be
9973 * no need to recreate the stream. If the stream is manually
9974 * deleted it will attempt to create it by querying the registry.
9978 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9981 HRESULT hStorageRes
, hRes
= S_OK
;
9982 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9983 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9985 static const BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9986 static const BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9988 /* Initialize the CompObj structure */
9989 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9990 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9991 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9994 /* Create a CompObj stream if it doesn't exist */
9995 hStorageRes
= IStorage_CreateStream(pStorage
, L
"\1CompObj",
9996 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9997 if(hStorageRes
== S_OK
)
9999 /* copy the OleTypeName to the compobj struct */
10000 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
10001 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
10003 /* copy the OleTypeName to the compobj struct */
10004 /* Note: in the test made, these were Identical */
10005 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
10006 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
10008 /* Get the CLSID */
10009 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
10010 bufferW
, OLESTREAM_MAX_STR_LEN
);
10011 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
10017 /* Get the CLSID Default Name from the Registry */
10018 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
10019 if(hErr
== ERROR_SUCCESS
)
10021 char strTemp
[OLESTREAM_MAX_STR_LEN
];
10022 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
10023 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
10024 if(hErr
== ERROR_SUCCESS
)
10026 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
10032 /* Write CompObj Structure to stream */
10033 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
10035 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
10037 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
10038 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
10040 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
10042 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
10043 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
10045 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
10047 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
10048 if(IStorageCompObj
.dwProgIDNameLength
> 0)
10050 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
10052 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
10053 IStream_Release(pStream
);
10059 /*************************************************************************
10060 * OLECONVERT_CreateOlePresStream[Internal]
10062 * Creates the "\002OlePres000" Stream with the Metafile data
10065 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10066 * dwExtentX [I] Width of the Metafile
10067 * dwExtentY [I] Height of the Metafile
10068 * pData [I] Metafile data
10069 * dwDataLength [I] Size of the Metafile data
10073 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10076 * This function is used by OleConvertOLESTREAMToIStorage only.
10079 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10083 static const BYTE pOlePresStreamHeader
[] =
10085 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10086 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10087 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10088 0x00, 0x00, 0x00, 0x00
10091 static const BYTE pOlePresStreamHeaderEmpty
[] =
10093 0x00, 0x00, 0x00, 0x00,
10094 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10095 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10096 0x00, 0x00, 0x00, 0x00
10099 /* Create the OlePres000 Stream */
10100 hRes
= IStorage_CreateStream(pStorage
, L
"\2OlePres000",
10101 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10106 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10108 memset(&OlePres
, 0, sizeof(OlePres
));
10109 /* Do we have any metafile data to save */
10110 if(dwDataLength
> 0)
10112 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10113 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10117 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10118 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10120 /* Set width and height of the metafile */
10121 OlePres
.dwExtentX
= dwExtentX
;
10122 OlePres
.dwExtentY
= -dwExtentY
;
10124 /* Set Data and Length */
10125 if(dwDataLength
> sizeof(METAFILEPICT16
))
10127 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10128 OlePres
.pData
= &(pData
[8]);
10130 /* Save OlePres000 Data to Stream */
10131 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10132 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10133 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10134 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10135 if(OlePres
.dwSize
> 0)
10137 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10139 IStream_Release(pStream
);
10143 /*************************************************************************
10144 * OLECONVERT_CreateOle10NativeStream [Internal]
10146 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10149 * pStorage [I] Dest storage to create the stream in
10150 * pData [I] Ole10 Native Data (ex. bmp)
10151 * dwDataLength [I] Size of the Ole10 Native Data
10157 * This function is used by OleConvertOLESTREAMToIStorage only.
10159 * Might need to verify the data and return appropriate error message
10162 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10167 /* Create the Ole10Native Stream */
10168 hRes
= IStorage_CreateStream(pStorage
, L
"\1Ole10Native",
10169 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10173 /* Write info to stream */
10174 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10175 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10176 IStream_Release(pStream
);
10181 /*************************************************************************
10182 * OLECONVERT_GetOLE10ProgID [Internal]
10184 * Finds the ProgID (or OleTypeID) from the IStorage
10187 * pStorage [I] The Src IStorage to get the ProgID
10188 * strProgID [I] the ProgID string to get
10189 * dwSize [I] the size of the string
10193 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10196 * This function is used by OleConvertIStorageToOLESTREAM only.
10200 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10204 LARGE_INTEGER iSeekPos
;
10205 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10207 /* Open the CompObj Stream */
10208 hRes
= IStorage_OpenStream(pStorage
, L
"\1CompObj", NULL
,
10209 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10213 /*Get the OleType from the CompObj Stream */
10214 iSeekPos
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10215 iSeekPos
.HighPart
= 0;
10217 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10218 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10219 iSeekPos
.LowPart
= CompObj
.dwCLSIDNameLength
;
10220 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10221 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10222 iSeekPos
.LowPart
= CompObj
.dwOleTypeNameLength
;
10223 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10225 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10228 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10230 IStream_Release(pStream
);
10235 LPOLESTR wstrProgID
;
10237 /* Get the OleType from the registry */
10238 REFCLSID clsid
= &(stat
.clsid
);
10239 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10240 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10243 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10244 CoTaskMemFree(wstrProgID
);
10251 /*************************************************************************
10252 * OLECONVERT_GetOle10PresData [Internal]
10254 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10257 * pStorage [I] Src IStroage
10258 * pOleStream [I] Dest OleStream Mem Struct
10264 * This function is used by OleConvertIStorageToOLESTREAM only.
10266 * Memory allocated for pData must be freed by the caller
10270 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10276 /* Initialize Default data for OLESTREAM */
10277 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10278 pOleStreamData
[0].dwTypeID
= 2;
10279 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10280 pOleStreamData
[1].dwTypeID
= 0;
10281 pOleStreamData
[0].dwMetaFileWidth
= 0;
10282 pOleStreamData
[0].dwMetaFileHeight
= 0;
10283 pOleStreamData
[0].pData
= NULL
;
10284 pOleStreamData
[1].pData
= NULL
;
10286 /* Open Ole10Native Stream */
10287 hRes
= IStorage_OpenStream(pStorage
, L
"\1Ole10Native", NULL
,
10288 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10292 /* Read Size and Data */
10293 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10294 if(pOleStreamData
->dwDataLength
> 0)
10296 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10297 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10299 IStream_Release(pStream
);
10305 /*************************************************************************
10306 * OLECONVERT_GetOle20PresData[Internal]
10308 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10311 * pStorage [I] Src IStroage
10312 * pOleStreamData [I] Dest OleStream Mem Struct
10318 * This function is used by OleConvertIStorageToOLESTREAM only.
10320 * Memory allocated for pData must be freed by the caller
10322 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10326 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10328 /* Initialize Default data for OLESTREAM */
10329 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10330 pOleStreamData
[0].dwTypeID
= 2;
10331 pOleStreamData
[0].dwMetaFileWidth
= 0;
10332 pOleStreamData
[0].dwMetaFileHeight
= 0;
10333 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10334 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10335 pOleStreamData
[1].dwTypeID
= 0;
10336 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10337 pOleStreamData
[1].strOleTypeName
[0] = 0;
10338 pOleStreamData
[1].dwMetaFileWidth
= 0;
10339 pOleStreamData
[1].dwMetaFileHeight
= 0;
10340 pOleStreamData
[1].pData
= NULL
;
10341 pOleStreamData
[1].dwDataLength
= 0;
10344 /* Open OlePress000 stream */
10345 hRes
= IStorage_OpenStream(pStorage
, L
"\2OlePres000", NULL
,
10346 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10349 LARGE_INTEGER iSeekPos
;
10350 METAFILEPICT16 MetaFilePict
;
10351 static const char strMetafilePictName
[] = "METAFILEPICT";
10353 /* Set the TypeID for a Metafile */
10354 pOleStreamData
[1].dwTypeID
= 5;
10356 /* Set the OleTypeName to Metafile */
10357 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10358 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10360 iSeekPos
.HighPart
= 0;
10361 iSeekPos
.LowPart
= sizeof(olePress
.byUnknown1
);
10363 /* Get Presentation Data */
10364 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10365 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10366 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10367 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10369 /*Set width and Height */
10370 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10371 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10372 if(olePress
.dwSize
> 0)
10375 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10377 /* Set MetaFilePict struct */
10378 MetaFilePict
.mm
= 8;
10379 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10380 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10381 MetaFilePict
.hMF
= 0;
10383 /* Get Metafile Data */
10384 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10385 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10386 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10388 IStream_Release(pStream
);
10392 /*************************************************************************
10393 * OleConvertOLESTREAMToIStorage [OLE32.@]
10395 * Read info on MSDN
10398 * DVTARGETDEVICE parameter is not handled
10399 * Still unsure of some mem fields for OLE 10 Stream
10400 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10401 * and "\001OLE" streams
10404 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10405 LPOLESTREAM pOleStream
,
10407 const DVTARGETDEVICE
* ptd
)
10411 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10413 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10415 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10419 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10422 if(pstg
== NULL
|| pOleStream
== NULL
)
10424 hRes
= E_INVALIDARG
;
10429 /* Load the OLESTREAM to Memory */
10430 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10435 /* Load the OLESTREAM to Memory (part 2)*/
10436 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10442 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10444 /* Do we have the IStorage Data in the OLESTREAM */
10445 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10447 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10448 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10452 /* It must be an original OLE 1.0 source */
10453 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10458 /* It must be an original OLE 1.0 source */
10459 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10462 /* Create CompObj Stream if necessary */
10463 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10466 /*Create the Ole Stream if necessary */
10467 STORAGE_CreateOleStream(pstg
, 0);
10472 /* Free allocated memory */
10473 for(i
=0; i
< 2; i
++)
10475 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10476 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10477 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10482 /*************************************************************************
10483 * OleConvertIStorageToOLESTREAM [OLE32.@]
10485 * Read info on MSDN
10487 * Read info on MSDN
10490 * Still unsure of some mem fields for OLE 10 Stream
10491 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10492 * and "\001OLE" streams.
10495 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10497 LPOLESTREAM pOleStream
)
10500 HRESULT hRes
= S_OK
;
10502 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10504 TRACE("%p %p\n", pstg
, pOleStream
);
10506 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10508 if(pstg
== NULL
|| pOleStream
== NULL
)
10510 hRes
= E_INVALIDARG
;
10514 /* Get the ProgID */
10515 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10516 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10520 /* Was it originally Ole10 */
10521 hRes
= IStorage_OpenStream(pstg
, L
"\1Ole10Native", 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10524 IStream_Release(pStream
);
10525 /* Get Presentation Data for Ole10Native */
10526 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10530 /* Get Presentation Data (OLE20) */
10531 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10534 /* Save OLESTREAM */
10535 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10538 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10543 /* Free allocated memory */
10544 for(i
=0; i
< 2; i
++)
10546 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10552 enum stream_1ole_flags
{
10553 OleStream_LinkedObject
= 0x00000001,
10554 OleStream_Convert
= 0x00000004
10557 /*************************************************************************
10558 * OleConvertIStorageToOLESTREAMEx [OLE32.@]
10560 HRESULT WINAPI
OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg
, CLIPFORMAT cf
, LONG width
, LONG height
,
10561 DWORD size
, LPSTGMEDIUM medium
, LPOLESTREAM olestream
)
10563 FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg
, cf
, width
, height
, size
, medium
, olestream
);
10568 /***********************************************************************
10569 * GetConvertStg (OLE32.@)
10571 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10573 static const DWORD version_magic
= 0x02000001;
10578 TRACE("%p\n", stg
);
10580 if (!stg
) return E_INVALIDARG
;
10582 hr
= IStorage_OpenStream(stg
, L
"\1Ole", NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10583 if (FAILED(hr
)) return hr
;
10585 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10586 IStream_Release(stream
);
10587 if (FAILED(hr
)) return hr
;
10589 if (header
[0] != version_magic
)
10591 ERR("got wrong version magic for 1Ole stream, %#lx.\n", header
[0]);
10595 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10598 /***********************************************************************
10599 * SetConvertStg (OLE32.@)
10601 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10603 DWORD flags
= convert
? OleStream_Convert
: 0;
10608 TRACE("(%p, %d)\n", storage
, convert
);
10610 hr
= IStorage_OpenStream(storage
, L
"\1Ole", NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10613 if (hr
!= STG_E_FILENOTFOUND
)
10616 return STORAGE_CreateOleStream(storage
, flags
);
10619 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10622 IStream_Release(stream
);
10626 /* update flag if differs */
10627 if ((header
[1] ^ flags
) & OleStream_Convert
)
10629 LARGE_INTEGER pos
= {{0}};
10631 if (header
[1] & OleStream_Convert
)
10632 flags
= header
[1] & ~OleStream_Convert
;
10634 flags
= header
[1] | OleStream_Convert
;
10636 pos
.QuadPart
= sizeof(DWORD
);
10637 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10640 IStream_Release(stream
);
10644 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10647 IStream_Release(stream
);