2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
49 #include "storage32.h"
50 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "wine/wingdi16.h"
54 #include "compobj_private.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
60 * These are signatures to detect the type of Document file.
62 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
63 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
65 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
68 /****************************************************************************
69 * StorageInternalImpl definitions.
71 * Definition of the implementation structure for the IStorage interface.
72 * This one implements the IStorage interface for storage that are
73 * inside another storage.
75 typedef struct StorageInternalImpl
77 struct StorageBaseImpl base
;
80 * Entry in the parent's stream tracking list
82 struct list ParentListEntry
;
84 StorageBaseImpl
*parentStorage
;
85 } StorageInternalImpl
;
87 static const IStorageVtbl StorageInternalImpl_Vtbl
;
88 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
*,DWORD
,DirRef
);
90 typedef struct TransactedDirEntry
92 /* If applicable, a reference to the original DirEntry in the transacted
93 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
94 DirRef transactedParentEntry
;
96 /* True if this entry is being used. */
99 /* True if data is up to date. */
102 /* True if this entry has been modified. */
105 /* True if this entry's stream has been modified. */
108 /* True if this entry has been deleted in the transacted storage, but the
109 * delete has not yet been committed. */
112 /* If this entry's stream has been modified, a reference to where the stream
113 * is stored in the snapshot file. */
116 /* This directory entry's data, including any changes that have been made. */
119 /* A reference to the parent of this node. This is only valid while we are
120 * committing changes. */
123 /* A reference to a newly-created entry in the transacted parent. This is
124 * always equal to transactedParentEntry except when committing changes. */
125 DirRef newTransactedParentEntry
;
126 } TransactedDirEntry
;
129 /****************************************************************************
130 * Transacted storage object.
132 typedef struct TransactedSnapshotImpl
134 struct StorageBaseImpl base
;
137 * Modified streams are temporarily saved to the scratch file.
139 StorageBaseImpl
*scratch
;
141 /* The directory structure is kept here, so that we can track how these
142 * entries relate to those in the parent storage. */
143 TransactedDirEntry
*entries
;
145 ULONG firstFreeEntry
;
148 * Changes are committed to the transacted parent.
150 StorageBaseImpl
*transactedParent
;
152 /* The transaction signature from when we last committed */
153 ULONG lastTransactionSig
;
154 } TransactedSnapshotImpl
;
156 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
157 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*,BOOL
,StorageBaseImpl
**);
159 typedef struct TransactedSharedImpl
161 struct StorageBaseImpl base
;
164 * Snapshot and uncommitted changes go here.
166 TransactedSnapshotImpl
*scratch
;
169 * Changes are committed to the transacted parent.
171 StorageBaseImpl
*transactedParent
;
173 /* The transaction signature from when we last committed */
174 ULONG lastTransactionSig
;
175 } TransactedSharedImpl
;
178 /****************************************************************************
179 * BlockChainStream definitions.
181 * The BlockChainStream class is a utility class that is used to create an
182 * abstraction of the big block chains in the storage file.
187 /* This represents a range of blocks that happen reside in consecutive sectors. */
193 typedef struct BlockChainBlock
199 BYTE data
[MAX_BIG_BLOCK_SIZE
];
202 struct BlockChainStream
204 StorageImpl
* parentStorage
;
205 ULONG
* headOfStreamPlaceHolder
;
206 DirRef ownerDirEntry
;
207 struct BlockChainRun
* indexCache
;
209 ULONG indexCacheSize
;
210 BlockChainBlock cachedBlocks
[2];
216 /* Returns the number of blocks that comprises this chain.
217 * This is not the size of the stream as the last block may not be full!
219 static inline ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
221 return This
->numBlocks
;
224 static BlockChainStream
* BlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
225 static void BlockChainStream_Destroy(BlockChainStream
*);
226 static HRESULT
BlockChainStream_ReadAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
227 static HRESULT
BlockChainStream_WriteAt(BlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
228 static HRESULT
BlockChainStream_Flush(BlockChainStream
*);
229 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
*);
230 static BOOL
BlockChainStream_SetSize(BlockChainStream
*,ULARGE_INTEGER
);
233 /****************************************************************************
234 * SmallBlockChainStream definitions.
236 * The SmallBlockChainStream class is a utility class that is used to create an
237 * abstraction of the small block chains in the storage file.
240 struct SmallBlockChainStream
242 StorageImpl
* parentStorage
;
243 DirRef ownerDirEntry
;
244 ULONG
* headOfStreamPlaceHolder
;
247 static SmallBlockChainStream
* SmallBlockChainStream_Construct(StorageImpl
*,ULONG
*,DirRef
);
248 static void SmallBlockChainStream_Destroy(SmallBlockChainStream
*);
249 static HRESULT
SmallBlockChainStream_ReadAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,void*,ULONG
*);
250 static HRESULT
SmallBlockChainStream_WriteAt(SmallBlockChainStream
*,ULARGE_INTEGER
,ULONG
,const void*,ULONG
*);
251 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
*);
252 static BOOL
SmallBlockChainStream_SetSize(SmallBlockChainStream
*,ULARGE_INTEGER
);
255 /************************************************************************
257 ***********************************************************************/
259 /************************************************************************
260 * This method validates an STGM parameter that can contain the values below
262 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
263 * The stgm values contained in 0xffff0000 are bitmasks.
265 * STGM_DIRECT 0x00000000
266 * STGM_TRANSACTED 0x00010000
267 * STGM_SIMPLE 0x08000000
269 * STGM_READ 0x00000000
270 * STGM_WRITE 0x00000001
271 * STGM_READWRITE 0x00000002
273 * STGM_SHARE_DENY_NONE 0x00000040
274 * STGM_SHARE_DENY_READ 0x00000030
275 * STGM_SHARE_DENY_WRITE 0x00000020
276 * STGM_SHARE_EXCLUSIVE 0x00000010
278 * STGM_PRIORITY 0x00040000
279 * STGM_DELETEONRELEASE 0x04000000
281 * STGM_CREATE 0x00001000
282 * STGM_CONVERT 0x00020000
283 * STGM_FAILIFTHERE 0x00000000
285 * STGM_NOSCRATCH 0x00100000
286 * STGM_NOSNAPSHOT 0x00200000
288 static HRESULT
validateSTGM(DWORD stgm
)
290 DWORD access
= STGM_ACCESS_MODE(stgm
);
291 DWORD share
= STGM_SHARE_MODE(stgm
);
292 DWORD create
= STGM_CREATE_MODE(stgm
);
294 if (stgm
&~STGM_KNOWN_FLAGS
)
296 ERR("unknown flags %08x\n", stgm
);
312 case STGM_SHARE_DENY_NONE
:
313 case STGM_SHARE_DENY_READ
:
314 case STGM_SHARE_DENY_WRITE
:
315 case STGM_SHARE_EXCLUSIVE
:
318 if (!(stgm
& STGM_TRANSACTED
))
328 case STGM_FAILIFTHERE
:
335 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
337 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
341 * STGM_CREATE | STGM_CONVERT
342 * if both are false, STGM_FAILIFTHERE is set to TRUE
344 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
348 * STGM_NOSCRATCH requires STGM_TRANSACTED
350 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
354 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
355 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
357 if ( (stgm
& STGM_NOSNAPSHOT
) &&
358 (!(stgm
& STGM_TRANSACTED
) ||
359 share
== STGM_SHARE_EXCLUSIVE
||
360 share
== STGM_SHARE_DENY_WRITE
) )
366 /************************************************************************
367 * GetShareModeFromSTGM
369 * This method will return a share mode flag from a STGM value.
370 * The STGM value is assumed valid.
372 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
374 switch (STGM_SHARE_MODE(stgm
))
377 assert(stgm
& STGM_TRANSACTED
);
379 case STGM_SHARE_DENY_NONE
:
380 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
381 case STGM_SHARE_DENY_READ
:
382 return FILE_SHARE_WRITE
;
383 case STGM_SHARE_DENY_WRITE
:
384 case STGM_SHARE_EXCLUSIVE
:
385 return FILE_SHARE_READ
;
387 ERR("Invalid share mode!\n");
392 /************************************************************************
393 * GetAccessModeFromSTGM
395 * This method will return an access mode flag from a STGM value.
396 * The STGM value is assumed valid.
398 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
400 switch (STGM_ACCESS_MODE(stgm
))
406 return GENERIC_READ
| GENERIC_WRITE
;
408 ERR("Invalid access mode!\n");
413 /************************************************************************
414 * GetCreationModeFromSTGM
416 * This method will return a creation mode flag from a STGM value.
417 * The STGM value is assumed valid.
419 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
421 switch(STGM_CREATE_MODE(stgm
))
424 return CREATE_ALWAYS
;
426 FIXME("STGM_CONVERT not implemented!\n");
428 case STGM_FAILIFTHERE
:
431 ERR("Invalid create mode!\n");
437 /************************************************************************
438 * IDirectWriterLock implementation
439 ***********************************************************************/
441 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
443 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
446 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
448 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
449 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
452 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
454 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
455 return IStorage_AddRef(&This
->IStorage_iface
);
458 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
460 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
461 return IStorage_Release(&This
->IStorage_iface
);
464 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
466 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
467 FIXME("(%p)->(%d): stub\n", This
, timeout
);
471 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
473 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
474 FIXME("(%p): stub\n", This
);
478 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
480 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
481 FIXME("(%p): stub\n", This
);
485 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
487 directwriterlock_QueryInterface
,
488 directwriterlock_AddRef
,
489 directwriterlock_Release
,
490 directwriterlock_WaitForWriteAccess
,
491 directwriterlock_ReleaseWriteAccess
,
492 directwriterlock_HaveWriteAccess
496 /************************************************************************
497 * StorageBaseImpl implementation : Tree helper functions
498 ***********************************************************************/
500 /****************************************************************************
504 * Case insensitive comparison of DirEntry.name by first considering
507 * Returns <0 when name1 < name2
508 * >0 when name1 > name2
509 * 0 when name1 == name2
511 static LONG
entryNameCmp(
512 const OLECHAR
*name1
,
513 const OLECHAR
*name2
)
515 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
517 while (diff
== 0 && *name1
!= 0)
520 * We compare the string themselves only when they are of the same length
522 diff
= toupperW(*name1
++) - toupperW(*name2
++);
528 /****************************************************************************
532 * Find and read the element of a storage with the given name.
534 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
535 const OLECHAR
*name
, DirEntry
*data
)
539 /* Read the storage entry to find the root of the tree. */
540 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
542 currentEntry
= data
->dirRootEntry
;
544 while (currentEntry
!= DIRENTRY_NULL
)
548 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
550 cmp
= entryNameCmp(name
, data
->name
);
557 currentEntry
= data
->leftChild
;
560 currentEntry
= data
->rightChild
;
566 /****************************************************************************
570 * Find and read the binary tree parent of the element with the given name.
572 * If there is no such element, find a place where it could be inserted and
573 * return STG_E_FILENOTFOUND.
575 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
576 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
582 /* Read the storage entry to find the root of the tree. */
583 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
585 *parentEntry
= storageEntry
;
586 *relation
= DIRENTRY_RELATION_DIR
;
588 childEntry
= parentData
->dirRootEntry
;
590 while (childEntry
!= DIRENTRY_NULL
)
594 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
596 cmp
= entryNameCmp(childName
, childData
.name
);
604 *parentData
= childData
;
605 *parentEntry
= childEntry
;
606 *relation
= DIRENTRY_RELATION_PREVIOUS
;
608 childEntry
= parentData
->leftChild
;
613 *parentData
= childData
;
614 *parentEntry
= childEntry
;
615 *relation
= DIRENTRY_RELATION_NEXT
;
617 childEntry
= parentData
->rightChild
;
621 if (childEntry
== DIRENTRY_NULL
)
622 return STG_E_FILENOTFOUND
;
627 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
631 case DIRENTRY_RELATION_PREVIOUS
:
632 entry
->leftChild
= new_target
;
634 case DIRENTRY_RELATION_NEXT
:
635 entry
->rightChild
= new_target
;
637 case DIRENTRY_RELATION_DIR
:
638 entry
->dirRootEntry
= new_target
;
645 /****************************************************************************
649 * Add a directory entry to a storage
651 static HRESULT
insertIntoTree(
652 StorageBaseImpl
*This
,
653 DirRef parentStorageIndex
,
654 DirRef newEntryIndex
)
656 DirEntry currentEntry
;
660 * Read the inserted entry
662 StorageBaseImpl_ReadDirEntry(This
,
667 * Read the storage entry
669 StorageBaseImpl_ReadDirEntry(This
,
673 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
676 * The root storage contains some element, therefore, start the research
677 * for the appropriate location.
680 DirRef current
, next
, previous
, currentEntryId
;
683 * Keep a reference to the root of the storage's element tree
685 currentEntryId
= currentEntry
.dirRootEntry
;
690 StorageBaseImpl_ReadDirEntry(This
,
691 currentEntry
.dirRootEntry
,
694 previous
= currentEntry
.leftChild
;
695 next
= currentEntry
.rightChild
;
696 current
= currentEntryId
;
700 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
704 if (previous
!= DIRENTRY_NULL
)
706 StorageBaseImpl_ReadDirEntry(This
,
713 currentEntry
.leftChild
= newEntryIndex
;
714 StorageBaseImpl_WriteDirEntry(This
,
722 if (next
!= DIRENTRY_NULL
)
724 StorageBaseImpl_ReadDirEntry(This
,
731 currentEntry
.rightChild
= newEntryIndex
;
732 StorageBaseImpl_WriteDirEntry(This
,
741 * Trying to insert an item with the same name in the
744 return STG_E_FILEALREADYEXISTS
;
747 previous
= currentEntry
.leftChild
;
748 next
= currentEntry
.rightChild
;
754 * The storage is empty, make the new entry the root of its element tree
756 currentEntry
.dirRootEntry
= newEntryIndex
;
757 StorageBaseImpl_WriteDirEntry(This
,
765 /*************************************************************************
769 * This method removes a directory entry from its parent storage tree without
770 * freeing any resources attached to it.
772 static HRESULT
removeFromTree(
773 StorageBaseImpl
*This
,
774 DirRef parentStorageIndex
,
777 DirEntry entryToDelete
;
778 DirEntry parentEntry
;
779 DirRef parentEntryRef
;
780 ULONG typeOfRelation
;
783 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
789 * Find the element that links to the one we want to delete.
791 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
792 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
797 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
800 * Replace the deleted entry with its left child
802 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
804 hr
= StorageBaseImpl_WriteDirEntry(
813 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
816 * We need to reinsert the right child somewhere. We already know it and
817 * its children are greater than everything in the left tree, so we
818 * insert it at the rightmost point in the left tree.
820 DirRef newRightChildParent
= entryToDelete
.leftChild
;
821 DirEntry newRightChildParentEntry
;
825 hr
= StorageBaseImpl_ReadDirEntry(
828 &newRightChildParentEntry
);
834 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
835 newRightChildParent
= newRightChildParentEntry
.rightChild
;
836 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
838 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
840 hr
= StorageBaseImpl_WriteDirEntry(
843 &newRightChildParentEntry
);
853 * Replace the deleted entry with its right child
855 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
857 hr
= StorageBaseImpl_WriteDirEntry(
871 /************************************************************************
872 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
873 ***********************************************************************/
876 * IEnumSTATSTGImpl definitions.
878 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
879 * This class allows iterating through the content of a storage and finding
880 * specific items inside it.
882 struct IEnumSTATSTGImpl
884 IEnumSTATSTG IEnumSTATSTG_iface
;
886 LONG ref
; /* Reference count */
887 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
888 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
890 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
893 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
895 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
898 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
900 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
901 HeapFree(GetProcessHeap(), 0, This
);
904 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
909 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
916 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
917 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
919 *ppvObject
= &This
->IEnumSTATSTG_iface
;
920 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
924 return E_NOINTERFACE
;
927 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
930 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
931 return InterlockedIncrement(&This
->ref
);
934 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
937 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
941 newRef
= InterlockedDecrement(&This
->ref
);
945 IEnumSTATSTGImpl_Destroy(This
);
951 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
952 IEnumSTATSTGImpl
* This
,
955 DirRef result
= DIRENTRY_NULL
;
959 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
961 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
962 This
->parentStorage
->storageDirEntry
, &entry
);
963 searchNode
= entry
.dirRootEntry
;
965 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
967 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
971 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
975 searchNode
= entry
.rightChild
;
980 memcpy(result_name
, entry
.name
, sizeof(result_name
));
981 searchNode
= entry
.leftChild
;
989 if (result
!= DIRENTRY_NULL
)
990 memcpy(This
->name
, result_name
, sizeof(result_name
));
996 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
1000 ULONG
* pceltFetched
)
1002 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1004 DirEntry currentEntry
;
1005 STATSTG
* currentReturnStruct
= rgelt
;
1006 ULONG objectFetched
= 0;
1007 DirRef currentSearchNode
;
1010 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
1011 return E_INVALIDARG
;
1013 if (This
->parentStorage
->reverted
)
1014 return STG_E_REVERTED
;
1017 * To avoid the special case, get another pointer to a ULONG value if
1018 * the caller didn't supply one.
1020 if (pceltFetched
==0)
1021 pceltFetched
= &objectFetched
;
1024 * Start the iteration, we will iterate until we hit the end of the
1025 * linked list or until we hit the number of items to iterate through
1029 while ( *pceltFetched
< celt
)
1031 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1033 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1037 * Read the entry from the storage.
1039 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
1044 * Copy the information to the return buffer.
1046 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
1047 currentReturnStruct
,
1052 * Step to the next item in the iteration
1055 currentReturnStruct
++;
1058 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
1065 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
1066 IEnumSTATSTG
* iface
,
1069 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1071 ULONG objectFetched
= 0;
1072 DirRef currentSearchNode
;
1075 if (This
->parentStorage
->reverted
)
1076 return STG_E_REVERTED
;
1078 while ( (objectFetched
< celt
) )
1080 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
1082 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
1088 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
1094 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
1095 IEnumSTATSTG
* iface
)
1097 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1099 if (This
->parentStorage
->reverted
)
1100 return STG_E_REVERTED
;
1107 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
*,DirRef
);
1109 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
1110 IEnumSTATSTG
* iface
,
1111 IEnumSTATSTG
** ppenum
)
1113 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
1114 IEnumSTATSTGImpl
* newClone
;
1116 if (This
->parentStorage
->reverted
)
1117 return STG_E_REVERTED
;
1120 return E_INVALIDARG
;
1122 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
1123 This
->storageDirEntry
);
1127 return E_OUTOFMEMORY
;
1131 * The new clone enumeration must point to the same current node as
1134 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
1136 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
1142 * Virtual function table for the IEnumSTATSTGImpl class.
1144 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
1146 IEnumSTATSTGImpl_QueryInterface
,
1147 IEnumSTATSTGImpl_AddRef
,
1148 IEnumSTATSTGImpl_Release
,
1149 IEnumSTATSTGImpl_Next
,
1150 IEnumSTATSTGImpl_Skip
,
1151 IEnumSTATSTGImpl_Reset
,
1152 IEnumSTATSTGImpl_Clone
1155 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
1156 StorageBaseImpl
* parentStorage
,
1157 DirRef storageDirEntry
)
1159 IEnumSTATSTGImpl
* newEnumeration
;
1161 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
1165 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
1166 newEnumeration
->ref
= 1;
1167 newEnumeration
->name
[0] = 0;
1170 * We want to nail-down the reference to the storage in case the
1171 * enumeration out-lives the storage in the client application.
1173 newEnumeration
->parentStorage
= parentStorage
;
1174 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
1176 newEnumeration
->storageDirEntry
= storageDirEntry
;
1179 return newEnumeration
;
1183 /************************************************************************
1184 * StorageBaseImpl implementation
1185 ***********************************************************************/
1187 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
1189 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
1192 /************************************************************************
1193 * StorageBaseImpl_QueryInterface (IUnknown)
1195 * This method implements the common QueryInterface for all IStorage
1196 * implementations contained in this file.
1198 * See Windows documentation for more details on IUnknown methods.
1200 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
1205 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1208 return E_INVALIDARG
;
1212 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
1213 IsEqualGUID(&IID_IStorage
, riid
))
1215 *ppvObject
= &This
->IStorage_iface
;
1217 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
1219 *ppvObject
= &This
->IPropertySetStorage_iface
;
1221 /* locking interface is reported for writer only */
1222 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
1224 *ppvObject
= &This
->IDirectWriterLock_iface
;
1227 return E_NOINTERFACE
;
1229 IStorage_AddRef(iface
);
1234 /************************************************************************
1235 * StorageBaseImpl_AddRef (IUnknown)
1237 * This method implements the common AddRef for all IStorage
1238 * implementations contained in this file.
1240 * See Windows documentation for more details on IUnknown methods.
1242 static ULONG WINAPI
StorageBaseImpl_AddRef(
1245 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1246 ULONG ref
= InterlockedIncrement(&This
->ref
);
1248 TRACE("(%p) AddRef to %d\n", This
, ref
);
1253 /************************************************************************
1254 * StorageBaseImpl_Release (IUnknown)
1256 * This method implements the common Release for all IStorage
1257 * implementations contained in this file.
1259 * See Windows documentation for more details on IUnknown methods.
1261 static ULONG WINAPI
StorageBaseImpl_Release(
1264 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1266 ULONG ref
= InterlockedDecrement(&This
->ref
);
1268 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
1273 * Since we are using a system of base-classes, we want to call the
1274 * destructor of the appropriate derived class. To do this, we are
1275 * using virtual functions to implement the destructor.
1277 StorageBaseImpl_Destroy(This
);
1283 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1284 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1285 SNB snbExclude
, IStorage
*pstgDest
);
1287 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1288 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1289 SNB snbExclude
, IStorage
*pstgDest
)
1295 IStream
*pstrChild
, *pstrTmp
;
1298 if (srcEntry
== DIRENTRY_NULL
)
1301 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1308 WCHAR
**snb
= snbExclude
;
1310 while ( *snb
!= NULL
&& !skip
)
1312 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1320 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1323 * create a new storage in destination storage
1325 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1326 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1331 * if it already exist, don't create a new one use this one
1333 if (hr
== STG_E_FILEALREADYEXISTS
)
1335 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1336 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1337 NULL
, 0, &pstgTmp
);
1342 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1343 skip_stream
, NULL
, pstgTmp
);
1345 IStorage_Release(pstgTmp
);
1348 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1351 * create a new stream in destination storage. If the stream already
1352 * exist, it will be deleted and a new one will be created.
1354 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1355 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1359 * open child stream storage. This operation must succeed even if the
1360 * stream is already open, so we use internal functions to do it.
1364 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1368 pstrChild
= &streamimpl
->IStream_iface
;
1370 IStream_AddRef(pstrChild
);
1382 * Get the size of the source stream
1384 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1387 * Set the size of the destination stream.
1389 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1394 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1397 IStream_Release( pstrChild
);
1400 IStream_Release( pstrTmp
);
1406 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1407 skip_stream
, snbExclude
, pstgDest
);
1410 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1411 skip_stream
, snbExclude
, pstgDest
);
1416 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1418 StgStreamImpl
*strm
;
1420 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1422 if (strm
->dirEntry
== streamEntry
)
1431 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1433 StorageInternalImpl
*childstg
;
1435 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1437 if (childstg
->base
.storageDirEntry
== storageEntry
)
1446 /************************************************************************
1447 * StorageBaseImpl_OpenStream (IStorage)
1449 * This method will open the specified stream object from the current storage.
1451 * See Windows documentation for more details on IStorage methods.
1453 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
1455 const OLECHAR
* pwcsName
, /* [string][in] */
1456 void* reserved1
, /* [unique][in] */
1457 DWORD grfMode
, /* [in] */
1458 DWORD reserved2
, /* [in] */
1459 IStream
** ppstm
) /* [out] */
1461 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1462 StgStreamImpl
* newStream
;
1463 DirEntry currentEntry
;
1464 DirRef streamEntryRef
;
1465 HRESULT res
= STG_E_UNKNOWN
;
1467 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1468 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
1470 if ( (pwcsName
==NULL
) || (ppstm
==0) )
1478 if ( FAILED( validateSTGM(grfMode
) ) ||
1479 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1481 res
= STG_E_INVALIDFLAG
;
1488 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
1490 res
= STG_E_INVALIDFUNCTION
;
1496 res
= STG_E_REVERTED
;
1501 * Check that we're compatible with the parent's storage mode, but
1502 * only if we are not in transacted mode
1504 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1505 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1507 res
= STG_E_INVALIDFLAG
;
1513 * Search for the element with the given name
1515 streamEntryRef
= findElement(
1517 This
->storageDirEntry
,
1522 * If it was found, construct the stream object and return a pointer to it.
1524 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
1525 (currentEntry
.stgType
==STGTY_STREAM
) )
1527 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
1529 /* A single stream cannot be opened a second time. */
1530 res
= STG_E_ACCESSDENIED
;
1534 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
1538 newStream
->grfMode
= grfMode
;
1539 *ppstm
= &newStream
->IStream_iface
;
1541 IStream_AddRef(*ppstm
);
1547 res
= E_OUTOFMEMORY
;
1551 res
= STG_E_FILENOTFOUND
;
1555 TRACE("<-- IStream %p\n", *ppstm
);
1556 TRACE("<-- %08x\n", res
);
1560 /************************************************************************
1561 * StorageBaseImpl_OpenStorage (IStorage)
1563 * This method will open a new storage object from the current storage.
1565 * See Windows documentation for more details on IStorage methods.
1567 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
1569 const OLECHAR
* pwcsName
, /* [string][unique][in] */
1570 IStorage
* pstgPriority
, /* [unique][in] */
1571 DWORD grfMode
, /* [in] */
1572 SNB snbExclude
, /* [unique][in] */
1573 DWORD reserved
, /* [in] */
1574 IStorage
** ppstg
) /* [out] */
1576 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1577 StorageInternalImpl
* newStorage
;
1578 StorageBaseImpl
* newTransactedStorage
;
1579 DirEntry currentEntry
;
1580 DirRef storageEntryRef
;
1581 HRESULT res
= STG_E_UNKNOWN
;
1583 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1584 iface
, debugstr_w(pwcsName
), pstgPriority
,
1585 grfMode
, snbExclude
, reserved
, ppstg
);
1587 if ((pwcsName
==NULL
) || (ppstg
==0) )
1593 if (This
->openFlags
& STGM_SIMPLE
)
1595 res
= STG_E_INVALIDFUNCTION
;
1600 if (snbExclude
!= NULL
)
1602 res
= STG_E_INVALIDPARAMETER
;
1606 if ( FAILED( validateSTGM(grfMode
) ))
1608 res
= STG_E_INVALIDFLAG
;
1615 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
1616 (grfMode
& STGM_DELETEONRELEASE
) ||
1617 (grfMode
& STGM_PRIORITY
) )
1619 res
= STG_E_INVALIDFUNCTION
;
1624 return STG_E_REVERTED
;
1627 * Check that we're compatible with the parent's storage mode,
1628 * but only if we are not transacted
1630 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1631 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1633 res
= STG_E_ACCESSDENIED
;
1640 storageEntryRef
= findElement(
1642 This
->storageDirEntry
,
1646 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
1647 (currentEntry
.stgType
==STGTY_STORAGE
) )
1649 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
1651 /* A single storage cannot be opened a second time. */
1652 res
= STG_E_ACCESSDENIED
;
1656 newStorage
= StorageInternalImpl_Construct(
1661 if (newStorage
!= 0)
1663 if (grfMode
& STGM_TRANSACTED
)
1665 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
1669 HeapFree(GetProcessHeap(), 0, newStorage
);
1673 *ppstg
= &newTransactedStorage
->IStorage_iface
;
1677 *ppstg
= &newStorage
->base
.IStorage_iface
;
1680 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
1686 res
= STG_E_INSUFFICIENTMEMORY
;
1690 res
= STG_E_FILENOTFOUND
;
1693 TRACE("<-- %08x\n", res
);
1697 /************************************************************************
1698 * StorageBaseImpl_EnumElements (IStorage)
1700 * This method will create an enumerator object that can be used to
1701 * retrieve information about all the elements in the storage object.
1703 * See Windows documentation for more details on IStorage methods.
1705 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
1707 DWORD reserved1
, /* [in] */
1708 void* reserved2
, /* [size_is][unique][in] */
1709 DWORD reserved3
, /* [in] */
1710 IEnumSTATSTG
** ppenum
) /* [out] */
1712 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1713 IEnumSTATSTGImpl
* newEnum
;
1715 TRACE("(%p, %d, %p, %d, %p)\n",
1716 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
1719 return E_INVALIDARG
;
1722 return STG_E_REVERTED
;
1724 newEnum
= IEnumSTATSTGImpl_Construct(
1726 This
->storageDirEntry
);
1730 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
1734 return E_OUTOFMEMORY
;
1737 /************************************************************************
1738 * StorageBaseImpl_Stat (IStorage)
1740 * This method will retrieve information about this storage object.
1742 * See Windows documentation for more details on IStorage methods.
1744 static HRESULT WINAPI
StorageBaseImpl_Stat(
1746 STATSTG
* pstatstg
, /* [out] */
1747 DWORD grfStatFlag
) /* [in] */
1749 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1750 DirEntry currentEntry
;
1751 HRESULT res
= STG_E_UNKNOWN
;
1753 TRACE("(%p, %p, %x)\n",
1754 iface
, pstatstg
, grfStatFlag
);
1764 res
= STG_E_REVERTED
;
1768 res
= StorageBaseImpl_ReadDirEntry(
1770 This
->storageDirEntry
,
1775 StorageUtl_CopyDirEntryToSTATSTG(
1781 pstatstg
->grfMode
= This
->openFlags
;
1782 pstatstg
->grfStateBits
= This
->stateBits
;
1788 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
1790 TRACE("<-- %08x\n", res
);
1794 /************************************************************************
1795 * StorageBaseImpl_RenameElement (IStorage)
1797 * This method will rename the specified element.
1799 * See Windows documentation for more details on IStorage methods.
1801 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
1803 const OLECHAR
* pwcsOldName
, /* [in] */
1804 const OLECHAR
* pwcsNewName
) /* [in] */
1806 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1807 DirEntry currentEntry
;
1808 DirRef currentEntryRef
;
1810 TRACE("(%p, %s, %s)\n",
1811 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
1814 return STG_E_REVERTED
;
1816 currentEntryRef
= findElement(This
,
1817 This
->storageDirEntry
,
1821 if (currentEntryRef
!= DIRENTRY_NULL
)
1824 * There is already an element with the new name
1826 return STG_E_FILEALREADYEXISTS
;
1830 * Search for the old element name
1832 currentEntryRef
= findElement(This
,
1833 This
->storageDirEntry
,
1837 if (currentEntryRef
!= DIRENTRY_NULL
)
1839 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
1840 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
1842 WARN("Element is already open; cannot rename.\n");
1843 return STG_E_ACCESSDENIED
;
1846 /* Remove the element from its current position in the tree */
1847 removeFromTree(This
, This
->storageDirEntry
,
1850 /* Change the name of the element */
1851 strcpyW(currentEntry
.name
, pwcsNewName
);
1853 /* Delete any sibling links */
1854 currentEntry
.leftChild
= DIRENTRY_NULL
;
1855 currentEntry
.rightChild
= DIRENTRY_NULL
;
1857 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
1860 /* Insert the element in a new position in the tree */
1861 insertIntoTree(This
, This
->storageDirEntry
,
1867 * There is no element with the old name
1869 return STG_E_FILENOTFOUND
;
1872 return StorageBaseImpl_Flush(This
);
1875 /************************************************************************
1876 * StorageBaseImpl_CreateStream (IStorage)
1878 * This method will create a stream object within this storage
1880 * See Windows documentation for more details on IStorage methods.
1882 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
1884 const OLECHAR
* pwcsName
, /* [string][in] */
1885 DWORD grfMode
, /* [in] */
1886 DWORD reserved1
, /* [in] */
1887 DWORD reserved2
, /* [in] */
1888 IStream
** ppstm
) /* [out] */
1890 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1891 StgStreamImpl
* newStream
;
1892 DirEntry currentEntry
, newStreamEntry
;
1893 DirRef currentEntryRef
, newStreamEntryRef
;
1896 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1897 iface
, debugstr_w(pwcsName
), grfMode
,
1898 reserved1
, reserved2
, ppstm
);
1901 return STG_E_INVALIDPOINTER
;
1904 return STG_E_INVALIDNAME
;
1906 if (reserved1
|| reserved2
)
1907 return STG_E_INVALIDPARAMETER
;
1909 if ( FAILED( validateSTGM(grfMode
) ))
1910 return STG_E_INVALIDFLAG
;
1912 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
1913 return STG_E_INVALIDFLAG
;
1916 return STG_E_REVERTED
;
1921 if ((grfMode
& STGM_DELETEONRELEASE
) ||
1922 (grfMode
& STGM_TRANSACTED
))
1923 return STG_E_INVALIDFUNCTION
;
1926 * Don't worry about permissions in transacted mode, as we can always write
1927 * changes; we just can't always commit them.
1929 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
1930 /* Can't create a stream on read-only storage */
1931 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1932 return STG_E_ACCESSDENIED
;
1934 /* Can't create a stream with greater access than the parent. */
1935 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1936 return STG_E_ACCESSDENIED
;
1939 if(This
->openFlags
& STGM_SIMPLE
)
1940 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
1944 currentEntryRef
= findElement(This
,
1945 This
->storageDirEntry
,
1949 if (currentEntryRef
!= DIRENTRY_NULL
)
1952 * An element with this name already exists
1954 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1956 IStorage_DestroyElement(iface
, pwcsName
);
1959 return STG_E_FILEALREADYEXISTS
;
1963 * memset the empty entry
1965 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
1967 newStreamEntry
.sizeOfNameString
=
1968 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1970 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1971 return STG_E_INVALIDNAME
;
1973 strcpyW(newStreamEntry
.name
, pwcsName
);
1975 newStreamEntry
.stgType
= STGTY_STREAM
;
1976 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1977 newStreamEntry
.size
.u
.LowPart
= 0;
1978 newStreamEntry
.size
.u
.HighPart
= 0;
1980 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
1981 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
1982 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
1984 /* call CoFileTime to get the current time
1985 newStreamEntry.ctime
1986 newStreamEntry.mtime
1989 /* newStreamEntry.clsid */
1992 * Create an entry with the new data
1994 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
1999 * Insert the new entry in the parent storage's tree.
2001 hr
= insertIntoTree(
2003 This
->storageDirEntry
,
2007 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
2012 * Open the stream to return it.
2014 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
2018 *ppstm
= &newStream
->IStream_iface
;
2019 IStream_AddRef(*ppstm
);
2023 return STG_E_INSUFFICIENTMEMORY
;
2026 return StorageBaseImpl_Flush(This
);
2029 /************************************************************************
2030 * StorageBaseImpl_SetClass (IStorage)
2032 * This method will write the specified CLSID in the directory entry of this
2035 * See Windows documentation for more details on IStorage methods.
2037 static HRESULT WINAPI
StorageBaseImpl_SetClass(
2039 REFCLSID clsid
) /* [in] */
2041 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2043 DirEntry currentEntry
;
2045 TRACE("(%p, %p)\n", iface
, clsid
);
2048 return STG_E_REVERTED
;
2050 hRes
= StorageBaseImpl_ReadDirEntry(This
,
2051 This
->storageDirEntry
,
2053 if (SUCCEEDED(hRes
))
2055 currentEntry
.clsid
= *clsid
;
2057 hRes
= StorageBaseImpl_WriteDirEntry(This
,
2058 This
->storageDirEntry
,
2062 if (SUCCEEDED(hRes
))
2063 hRes
= StorageBaseImpl_Flush(This
);
2068 /************************************************************************
2069 * StorageBaseImpl_CreateStorage (IStorage)
2071 * This method will create the storage object within the provided storage.
2073 * See Windows documentation for more details on IStorage methods.
2075 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
2077 const OLECHAR
*pwcsName
, /* [string][in] */
2078 DWORD grfMode
, /* [in] */
2079 DWORD reserved1
, /* [in] */
2080 DWORD reserved2
, /* [in] */
2081 IStorage
**ppstg
) /* [out] */
2083 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2085 DirEntry currentEntry
;
2087 DirRef currentEntryRef
;
2091 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2092 iface
, debugstr_w(pwcsName
), grfMode
,
2093 reserved1
, reserved2
, ppstg
);
2096 return STG_E_INVALIDPOINTER
;
2098 if (This
->openFlags
& STGM_SIMPLE
)
2100 return STG_E_INVALIDFUNCTION
;
2104 return STG_E_INVALIDNAME
;
2108 if ( FAILED( validateSTGM(grfMode
) ) ||
2109 (grfMode
& STGM_DELETEONRELEASE
) )
2111 WARN("bad grfMode: 0x%x\n", grfMode
);
2112 return STG_E_INVALIDFLAG
;
2116 return STG_E_REVERTED
;
2119 * Check that we're compatible with the parent's storage mode
2121 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2122 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
2124 WARN("access denied\n");
2125 return STG_E_ACCESSDENIED
;
2128 currentEntryRef
= findElement(This
,
2129 This
->storageDirEntry
,
2133 if (currentEntryRef
!= DIRENTRY_NULL
)
2136 * An element with this name already exists
2138 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
2139 ((This
->openFlags
& STGM_TRANSACTED
) ||
2140 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
2142 hr
= IStorage_DestroyElement(iface
, pwcsName
);
2148 WARN("file already exists\n");
2149 return STG_E_FILEALREADYEXISTS
;
2152 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
2153 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
2155 WARN("read-only storage\n");
2156 return STG_E_ACCESSDENIED
;
2159 memset(&newEntry
, 0, sizeof(DirEntry
));
2161 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
2163 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
2165 FIXME("name too long\n");
2166 return STG_E_INVALIDNAME
;
2169 strcpyW(newEntry
.name
, pwcsName
);
2171 newEntry
.stgType
= STGTY_STORAGE
;
2172 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2173 newEntry
.size
.u
.LowPart
= 0;
2174 newEntry
.size
.u
.HighPart
= 0;
2176 newEntry
.leftChild
= DIRENTRY_NULL
;
2177 newEntry
.rightChild
= DIRENTRY_NULL
;
2178 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
2180 /* call CoFileTime to get the current time
2185 /* newEntry.clsid */
2188 * Create a new directory entry for the storage
2190 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
2195 * Insert the new directory entry into the parent storage's tree
2197 hr
= insertIntoTree(
2199 This
->storageDirEntry
,
2203 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
2208 * Open it to get a pointer to return.
2210 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
2212 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
2218 hr
= StorageBaseImpl_Flush(This
);
2223 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
2224 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
2225 SNB snbExclude
, IStorage
*pstgDest
)
2230 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
2233 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
2236 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
2237 skip_stream
, snbExclude
, pstgDest
);
2242 /*************************************************************************
2245 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
2247 DWORD ciidExclude
, /* [in] */
2248 const IID
* rgiidExclude
, /* [size_is][unique][in] */
2249 SNB snbExclude
, /* [unique][in] */
2250 IStorage
* pstgDest
) /* [unique][in] */
2252 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2254 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
2257 TRACE("(%p, %d, %p, %p, %p)\n",
2258 iface
, ciidExclude
, rgiidExclude
,
2259 snbExclude
, pstgDest
);
2261 if ( pstgDest
== 0 )
2262 return STG_E_INVALIDPOINTER
;
2264 for(i
= 0; i
< ciidExclude
; ++i
)
2266 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
2267 skip_storage
= TRUE
;
2268 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
2271 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
2276 /* Give up early if it looks like this would be infinitely recursive.
2277 * Oddly enough, this includes some cases that aren't really recursive, like
2278 * copying to a transacted child. */
2279 IStorage
*pstgDestAncestor
= pstgDest
;
2280 IStorage
*pstgDestAncestorChild
= NULL
;
2282 /* Go up the chain from the destination until we find the source storage. */
2283 while (pstgDestAncestor
!= iface
) {
2284 pstgDestAncestorChild
= pstgDest
;
2286 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
2288 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
2290 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
2292 else if (pstgDestAncestor
->lpVtbl
== &StorageInternalImpl_Vtbl
)
2294 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
2296 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
2302 if (pstgDestAncestor
== iface
)
2306 if (pstgDestAncestorChild
&& snbExclude
)
2308 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
2310 WCHAR
**snb
= snbExclude
;
2312 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
2314 while ( *snb
!= NULL
&& fail
)
2316 if ( lstrcmpW(data
.name
, *snb
) == 0 )
2323 return STG_E_ACCESSDENIED
;
2327 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
2328 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
2331 /*************************************************************************
2332 * MoveElementTo (IStorage)
2334 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
2336 const OLECHAR
*pwcsName
, /* [string][in] */
2337 IStorage
*pstgDest
, /* [unique][in] */
2338 const OLECHAR
*pwcsNewName
,/* [string][in] */
2339 DWORD grfFlags
) /* [in] */
2341 FIXME("(%p %s %p %s %u): stub\n", iface
,
2342 debugstr_w(pwcsName
), pstgDest
,
2343 debugstr_w(pwcsNewName
), grfFlags
);
2347 /*************************************************************************
2350 * Ensures that any changes made to a storage object open in transacted mode
2351 * are reflected in the parent storage
2353 * In a non-transacted mode, this ensures all cached writes are completed.
2355 static HRESULT WINAPI
StorageBaseImpl_Commit(
2357 DWORD grfCommitFlags
)/* [in] */
2359 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
2360 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
2361 return StorageBaseImpl_Flush(This
);
2364 /*************************************************************************
2367 * Discard all changes that have been made since the last commit operation
2369 static HRESULT WINAPI
StorageBaseImpl_Revert(
2372 TRACE("(%p)\n", iface
);
2376 /*********************************************************************
2378 * Internal helper function for StorageBaseImpl_DestroyElement()
2380 * Delete the contents of a storage entry.
2383 static HRESULT
deleteStorageContents(
2384 StorageBaseImpl
*parentStorage
,
2385 DirRef indexToDelete
,
2386 DirEntry entryDataToDelete
)
2388 IEnumSTATSTG
*elements
= 0;
2389 IStorage
*childStorage
= 0;
2390 STATSTG currentElement
;
2392 HRESULT destroyHr
= S_OK
;
2393 StorageInternalImpl
*stg
, *stg2
;
2395 /* Invalidate any open storage objects. */
2396 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2398 if (stg
->base
.storageDirEntry
== indexToDelete
)
2400 StorageBaseImpl_Invalidate(&stg
->base
);
2405 * Open the storage and enumerate it
2407 hr
= IStorage_OpenStorage(
2408 &parentStorage
->IStorage_iface
,
2409 entryDataToDelete
.name
,
2411 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2422 * Enumerate the elements
2424 hr
= IStorage_EnumElements(childStorage
, 0, 0, 0, &elements
);
2427 IStorage_Release(childStorage
);
2434 * Obtain the next element
2436 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2439 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2441 CoTaskMemFree(currentElement
.pwcsName
);
2445 * We need to Reset the enumeration every time because we delete elements
2446 * and the enumeration could be invalid
2448 IEnumSTATSTG_Reset(elements
);
2450 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2452 IStorage_Release(childStorage
);
2453 IEnumSTATSTG_Release(elements
);
2458 /*********************************************************************
2460 * Internal helper function for StorageBaseImpl_DestroyElement()
2462 * Perform the deletion of a stream's data
2465 static HRESULT
deleteStreamContents(
2466 StorageBaseImpl
*parentStorage
,
2467 DirRef indexToDelete
,
2468 DirEntry entryDataToDelete
)
2472 ULARGE_INTEGER size
;
2473 StgStreamImpl
*strm
, *strm2
;
2475 /* Invalidate any open stream objects. */
2476 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2478 if (strm
->dirEntry
== indexToDelete
)
2480 TRACE("Stream deleted %p\n", strm
);
2481 strm
->parentStorage
= NULL
;
2482 list_remove(&strm
->StrmListEntry
);
2486 size
.u
.HighPart
= 0;
2489 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2490 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2500 hr
= IStream_SetSize(pis
, size
);
2508 * Release the stream object.
2510 IStream_Release(pis
);
2515 /*************************************************************************
2516 * DestroyElement (IStorage)
2518 * Strategy: This implementation is built this way for simplicity not for speed.
2519 * I always delete the topmost element of the enumeration and adjust
2520 * the deleted element pointer all the time. This takes longer to
2521 * do but allows reinvoking DestroyElement whenever we encounter a
2522 * storage object. The optimisation resides in the usage of another
2523 * enumeration strategy that would give all the leaves of a storage
2524 * first. (postfix order)
2526 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
2528 const OLECHAR
*pwcsName
)/* [string][in] */
2530 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2533 DirEntry entryToDelete
;
2534 DirRef entryToDeleteRef
;
2537 iface
, debugstr_w(pwcsName
));
2540 return STG_E_INVALIDPOINTER
;
2543 return STG_E_REVERTED
;
2545 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
2546 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
2547 return STG_E_ACCESSDENIED
;
2549 entryToDeleteRef
= findElement(
2551 This
->storageDirEntry
,
2555 if ( entryToDeleteRef
== DIRENTRY_NULL
)
2557 return STG_E_FILENOTFOUND
;
2560 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
2562 hr
= deleteStorageContents(
2567 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
2569 hr
= deleteStreamContents(
2579 * Remove the entry from its parent storage
2581 hr
= removeFromTree(
2583 This
->storageDirEntry
,
2587 * Invalidate the entry
2590 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
2593 hr
= StorageBaseImpl_Flush(This
);
2598 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2600 struct list
*cur
, *cur2
;
2601 StgStreamImpl
*strm
=NULL
;
2602 StorageInternalImpl
*childstg
=NULL
;
2604 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2605 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2606 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2607 strm
->parentStorage
= NULL
;
2611 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2612 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2613 StorageBaseImpl_Invalidate( &childstg
->base
);
2616 if (stg
->transactedChild
)
2618 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2620 stg
->transactedChild
= NULL
;
2624 /******************************************************************************
2625 * SetElementTimes (IStorage)
2627 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2629 const OLECHAR
*pwcsName
,/* [string][in] */
2630 const FILETIME
*pctime
, /* [in] */
2631 const FILETIME
*patime
, /* [in] */
2632 const FILETIME
*pmtime
) /* [in] */
2634 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2638 /******************************************************************************
2639 * SetStateBits (IStorage)
2641 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2643 DWORD grfStateBits
,/* [in] */
2644 DWORD grfMask
) /* [in] */
2646 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2649 return STG_E_REVERTED
;
2651 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2655 /******************************************************************************
2656 * Internal stream list handlers
2659 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2661 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2662 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2665 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2667 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2668 list_remove(&(strm
->StrmListEntry
));
2671 static HRESULT
StorageBaseImpl_CopyStream(
2672 StorageBaseImpl
*dst
, DirRef dst_entry
,
2673 StorageBaseImpl
*src
, DirRef src_entry
)
2678 ULARGE_INTEGER bytes_copied
;
2679 ULONG bytestocopy
, bytesread
, byteswritten
;
2681 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
2685 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
2687 bytes_copied
.QuadPart
= 0;
2688 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
2690 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
2692 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
2694 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
2697 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
2698 data
, &byteswritten
);
2701 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
2702 bytes_copied
.QuadPart
+= byteswritten
;
2710 static HRESULT
StorageBaseImpl_DupStorageTree(
2711 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
2712 StorageBaseImpl
*src
, DirRef src_entry
)
2716 BOOL has_stream
=FALSE
;
2718 if (src_entry
== DIRENTRY_NULL
)
2720 *dst_entry
= DIRENTRY_NULL
;
2724 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
2727 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
2728 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
2729 data
.size
.QuadPart
= 0;
2731 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
2735 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
2738 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
2741 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
2743 if (SUCCEEDED(hr
) && has_stream
)
2744 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
2749 static HRESULT
StorageBaseImpl_CopyStorageTree(
2750 StorageBaseImpl
*dst
, DirRef dst_entry
,
2751 StorageBaseImpl
*src
, DirRef src_entry
)
2754 DirEntry src_data
, dst_data
;
2755 DirRef new_root_entry
;
2757 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
2761 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
2766 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
2767 dst_data
.clsid
= src_data
.clsid
;
2768 dst_data
.ctime
= src_data
.ctime
;
2769 dst_data
.mtime
= src_data
.mtime
;
2770 dst_data
.dirRootEntry
= new_root_entry
;
2774 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
2779 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
2783 ULARGE_INTEGER zero
;
2785 if (entry
== DIRENTRY_NULL
)
2790 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
2792 if (SUCCEEDED(hr
) && include_siblings
)
2793 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
2795 if (SUCCEEDED(hr
) && include_siblings
)
2796 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
2799 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
2801 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
2802 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
2805 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
2811 /************************************************************************
2812 * StorageImpl implementation
2813 ***********************************************************************/
2815 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
2816 ULARGE_INTEGER offset
,
2821 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
2824 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
2825 ULARGE_INTEGER offset
,
2828 ULONG
* bytesWritten
)
2830 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
2833 /******************************************************************************
2834 * StorageImpl_LoadFileHeader
2836 * This method will read in the file header
2838 static HRESULT
StorageImpl_LoadFileHeader(
2842 BYTE headerBigBlock
[HEADER_SIZE
];
2844 ULARGE_INTEGER offset
;
2849 * Get a pointer to the big block of data containing the header.
2851 offset
.u
.HighPart
= 0;
2852 offset
.u
.LowPart
= 0;
2853 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2854 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2855 hr
= STG_E_FILENOTFOUND
;
2858 * Extract the information from the header.
2863 * Check for the "magic number" signature and return an error if it is not
2866 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2868 return STG_E_OLDFORMAT
;
2871 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2873 return STG_E_INVALIDHEADER
;
2876 StorageUtl_ReadWord(
2878 OFFSET_BIGBLOCKSIZEBITS
,
2879 &This
->bigBlockSizeBits
);
2881 StorageUtl_ReadWord(
2883 OFFSET_SMALLBLOCKSIZEBITS
,
2884 &This
->smallBlockSizeBits
);
2886 StorageUtl_ReadDWord(
2888 OFFSET_BBDEPOTCOUNT
,
2889 &This
->bigBlockDepotCount
);
2891 StorageUtl_ReadDWord(
2893 OFFSET_ROOTSTARTBLOCK
,
2894 &This
->rootStartBlock
);
2896 StorageUtl_ReadDWord(
2898 OFFSET_TRANSACTIONSIG
,
2899 &This
->transactionSig
);
2901 StorageUtl_ReadDWord(
2903 OFFSET_SMALLBLOCKLIMIT
,
2904 &This
->smallBlockLimit
);
2906 StorageUtl_ReadDWord(
2908 OFFSET_SBDEPOTSTART
,
2909 &This
->smallBlockDepotStart
);
2911 StorageUtl_ReadDWord(
2913 OFFSET_EXTBBDEPOTSTART
,
2914 &This
->extBigBlockDepotStart
);
2916 StorageUtl_ReadDWord(
2918 OFFSET_EXTBBDEPOTCOUNT
,
2919 &This
->extBigBlockDepotCount
);
2921 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2923 StorageUtl_ReadDWord(
2925 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2926 &(This
->bigBlockDepotStart
[index
]));
2930 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2932 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2933 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2936 * Right now, the code is making some assumptions about the size of the
2937 * blocks, just make sure they are what we're expecting.
2939 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
2940 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
2941 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
2943 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2944 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
2945 hr
= STG_E_INVALIDHEADER
;
2954 /******************************************************************************
2955 * StorageImpl_SaveFileHeader
2957 * This method will save to the file the header
2959 static void StorageImpl_SaveFileHeader(
2962 BYTE headerBigBlock
[HEADER_SIZE
];
2965 ULARGE_INTEGER offset
;
2966 DWORD bytes_read
, bytes_written
;
2967 DWORD major_version
, dirsectorcount
;
2970 * Get a pointer to the big block of data containing the header.
2972 offset
.u
.HighPart
= 0;
2973 offset
.u
.LowPart
= 0;
2974 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
2975 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
2976 hr
= STG_E_FILENOTFOUND
;
2978 if (This
->bigBlockSizeBits
== 0x9)
2980 else if (This
->bigBlockSizeBits
== 0xc)
2984 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
2989 * If the block read failed, the file is probably new.
2994 * Initialize for all unknown fields.
2996 memset(headerBigBlock
, 0, HEADER_SIZE
);
2999 * Initialize the magic number.
3001 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3005 * Write the information to the header.
3007 StorageUtl_WriteWord(
3009 OFFSET_MINORVERSION
,
3012 StorageUtl_WriteWord(
3014 OFFSET_MAJORVERSION
,
3017 StorageUtl_WriteWord(
3019 OFFSET_BYTEORDERMARKER
,
3022 StorageUtl_WriteWord(
3024 OFFSET_BIGBLOCKSIZEBITS
,
3025 This
->bigBlockSizeBits
);
3027 StorageUtl_WriteWord(
3029 OFFSET_SMALLBLOCKSIZEBITS
,
3030 This
->smallBlockSizeBits
);
3032 if (major_version
>= 4)
3034 if (This
->rootBlockChain
)
3035 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3037 /* This file is being created, and it will start out with one block. */
3041 /* This field must be 0 in versions older than 4 */
3044 StorageUtl_WriteDWord(
3046 OFFSET_DIRSECTORCOUNT
,
3049 StorageUtl_WriteDWord(
3051 OFFSET_BBDEPOTCOUNT
,
3052 This
->bigBlockDepotCount
);
3054 StorageUtl_WriteDWord(
3056 OFFSET_ROOTSTARTBLOCK
,
3057 This
->rootStartBlock
);
3059 StorageUtl_WriteDWord(
3061 OFFSET_TRANSACTIONSIG
,
3062 This
->transactionSig
);
3064 StorageUtl_WriteDWord(
3066 OFFSET_SMALLBLOCKLIMIT
,
3067 This
->smallBlockLimit
);
3069 StorageUtl_WriteDWord(
3071 OFFSET_SBDEPOTSTART
,
3072 This
->smallBlockDepotStart
);
3074 StorageUtl_WriteDWord(
3076 OFFSET_SBDEPOTCOUNT
,
3077 This
->smallBlockDepotChain
?
3078 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3080 StorageUtl_WriteDWord(
3082 OFFSET_EXTBBDEPOTSTART
,
3083 This
->extBigBlockDepotStart
);
3085 StorageUtl_WriteDWord(
3087 OFFSET_EXTBBDEPOTCOUNT
,
3088 This
->extBigBlockDepotCount
);
3090 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3092 StorageUtl_WriteDWord(
3094 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3095 (This
->bigBlockDepotStart
[index
]));
3099 * Write the big block back to the file.
3101 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3105 /************************************************************************
3106 * StorageImpl implementation : DirEntry methods
3107 ***********************************************************************/
3109 /******************************************************************************
3110 * StorageImpl_ReadRawDirEntry
3112 * This method will read the raw data from a directory entry in the file.
3114 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3116 static HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3118 ULARGE_INTEGER offset
;
3122 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3124 hr
= BlockChainStream_ReadAt(
3125 This
->rootBlockChain
,
3131 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3132 return STG_E_READFAULT
;
3137 /******************************************************************************
3138 * StorageImpl_WriteRawDirEntry
3140 * This method will write the raw data from a directory entry in the file.
3142 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3144 static HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3146 ULARGE_INTEGER offset
;
3149 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
3151 return BlockChainStream_WriteAt(
3152 This
->rootBlockChain
,
3159 /***************************************************************************
3163 * Mark a directory entry in the file as free.
3165 static HRESULT
StorageImpl_DestroyDirEntry(
3166 StorageBaseImpl
*base
,
3169 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3170 StorageImpl
*storage
= (StorageImpl
*)base
;
3172 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3174 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
3177 /******************************************************************************
3180 * Update raw directory entry data from the fields in newData.
3182 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3184 static void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3186 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3189 buffer
+ OFFSET_PS_NAME
,
3191 DIRENTRY_NAME_BUFFER_LEN
);
3193 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3195 StorageUtl_WriteWord(
3197 OFFSET_PS_NAMELENGTH
,
3198 newData
->sizeOfNameString
);
3200 StorageUtl_WriteDWord(
3202 OFFSET_PS_LEFTCHILD
,
3203 newData
->leftChild
);
3205 StorageUtl_WriteDWord(
3207 OFFSET_PS_RIGHTCHILD
,
3208 newData
->rightChild
);
3210 StorageUtl_WriteDWord(
3213 newData
->dirRootEntry
);
3215 StorageUtl_WriteGUID(
3220 StorageUtl_WriteDWord(
3223 newData
->ctime
.dwLowDateTime
);
3225 StorageUtl_WriteDWord(
3227 OFFSET_PS_CTIMEHIGH
,
3228 newData
->ctime
.dwHighDateTime
);
3230 StorageUtl_WriteDWord(
3233 newData
->mtime
.dwLowDateTime
);
3235 StorageUtl_WriteDWord(
3237 OFFSET_PS_MTIMEHIGH
,
3238 newData
->ctime
.dwHighDateTime
);
3240 StorageUtl_WriteDWord(
3242 OFFSET_PS_STARTBLOCK
,
3243 newData
->startingBlock
);
3245 StorageUtl_WriteDWord(
3248 newData
->size
.u
.LowPart
);
3250 StorageUtl_WriteDWord(
3252 OFFSET_PS_SIZE_HIGH
,
3253 newData
->size
.u
.HighPart
);
3256 /***************************************************************************
3260 * Reserve a directory entry in the file and initialize it.
3262 static HRESULT
StorageImpl_CreateDirEntry(
3263 StorageBaseImpl
*base
,
3264 const DirEntry
*newData
,
3267 StorageImpl
*storage
= (StorageImpl
*)base
;
3268 ULONG currentEntryIndex
= 0;
3269 ULONG newEntryIndex
= DIRENTRY_NULL
;
3271 BYTE currentData
[RAW_DIRENTRY_SIZE
];
3272 WORD sizeOfNameString
;
3276 hr
= StorageImpl_ReadRawDirEntry(storage
,
3282 StorageUtl_ReadWord(
3284 OFFSET_PS_NAMELENGTH
,
3287 if (sizeOfNameString
== 0)
3290 * The entry exists and is available, we found it.
3292 newEntryIndex
= currentEntryIndex
;
3298 * We exhausted the directory entries, we will create more space below
3300 newEntryIndex
= currentEntryIndex
;
3302 currentEntryIndex
++;
3304 } while (newEntryIndex
== DIRENTRY_NULL
);
3307 * grow the directory stream
3311 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
3312 ULARGE_INTEGER newSize
;
3314 ULONG lastEntry
= 0;
3315 ULONG blockCount
= 0;
3318 * obtain the new count of blocks in the directory stream
3320 blockCount
= BlockChainStream_GetCount(
3321 storage
->rootBlockChain
)+1;
3324 * initialize the size used by the directory stream
3326 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
3329 * add a block to the directory stream
3331 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
3334 * memset the empty entry in order to initialize the unused newly
3337 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
3342 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
3345 entryIndex
= newEntryIndex
+ 1;
3346 entryIndex
< lastEntry
;
3349 StorageImpl_WriteRawDirEntry(
3355 StorageImpl_SaveFileHeader(storage
);
3358 UpdateRawDirEntry(currentData
, newData
);
3360 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
3363 *index
= newEntryIndex
;
3368 /******************************************************************************
3369 * StorageImpl_ReadDirEntry
3371 * This method will read the specified directory entry.
3373 static HRESULT
StorageImpl_ReadDirEntry(
3378 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3381 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3383 if (SUCCEEDED(readRes
))
3385 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3388 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3389 DIRENTRY_NAME_BUFFER_LEN
);
3390 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3392 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3394 StorageUtl_ReadWord(
3396 OFFSET_PS_NAMELENGTH
,
3397 &buffer
->sizeOfNameString
);
3399 StorageUtl_ReadDWord(
3401 OFFSET_PS_LEFTCHILD
,
3402 &buffer
->leftChild
);
3404 StorageUtl_ReadDWord(
3406 OFFSET_PS_RIGHTCHILD
,
3407 &buffer
->rightChild
);
3409 StorageUtl_ReadDWord(
3412 &buffer
->dirRootEntry
);
3414 StorageUtl_ReadGUID(
3419 StorageUtl_ReadDWord(
3422 &buffer
->ctime
.dwLowDateTime
);
3424 StorageUtl_ReadDWord(
3426 OFFSET_PS_CTIMEHIGH
,
3427 &buffer
->ctime
.dwHighDateTime
);
3429 StorageUtl_ReadDWord(
3432 &buffer
->mtime
.dwLowDateTime
);
3434 StorageUtl_ReadDWord(
3436 OFFSET_PS_MTIMEHIGH
,
3437 &buffer
->mtime
.dwHighDateTime
);
3439 StorageUtl_ReadDWord(
3441 OFFSET_PS_STARTBLOCK
,
3442 &buffer
->startingBlock
);
3444 StorageUtl_ReadDWord(
3447 &buffer
->size
.u
.LowPart
);
3449 if (This
->bigBlockSize
< 4096)
3451 /* Version 3 files may have junk in the high part of size. */
3452 buffer
->size
.u
.HighPart
= 0;
3456 StorageUtl_ReadDWord(
3458 OFFSET_PS_SIZE_HIGH
,
3459 &buffer
->size
.u
.HighPart
);
3466 /*********************************************************************
3467 * Write the specified directory entry to the file
3469 static HRESULT
StorageImpl_WriteDirEntry(
3472 const DirEntry
* buffer
)
3474 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3476 UpdateRawDirEntry(currentEntry
, buffer
);
3478 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3482 /************************************************************************
3483 * StorageImpl implementation : Block methods
3484 ***********************************************************************/
3486 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3488 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3491 static HRESULT
StorageImpl_ReadBigBlock(
3497 ULARGE_INTEGER ulOffset
;
3501 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3503 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3505 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3507 /* File ends during this block; fill the rest with 0's. */
3508 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3511 if (out_read
) *out_read
= read
;
3516 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3522 ULARGE_INTEGER ulOffset
;
3526 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3527 ulOffset
.QuadPart
+= offset
;
3529 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3530 *value
= lendian32toh(tmp
);
3531 return (read
== sizeof(DWORD
));
3534 static BOOL
StorageImpl_WriteBigBlock(
3539 ULARGE_INTEGER ulOffset
;
3542 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3544 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3545 return (wrote
== This
->bigBlockSize
);
3548 static BOOL
StorageImpl_WriteDWordToBigBlock(
3554 ULARGE_INTEGER ulOffset
;
3557 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3558 ulOffset
.QuadPart
+= offset
;
3560 value
= htole32(value
);
3561 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3562 return (wrote
== sizeof(DWORD
));
3565 /******************************************************************************
3566 * Storage32Impl_SmallBlocksToBigBlocks
3568 * This method will convert a small block chain to a big block chain.
3569 * The small block chain will be destroyed.
3571 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3573 SmallBlockChainStream
** ppsbChain
)
3575 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3576 ULARGE_INTEGER size
, offset
;
3577 ULONG cbRead
, cbWritten
;
3578 ULARGE_INTEGER cbTotalRead
;
3579 DirRef streamEntryRef
;
3580 HRESULT resWrite
= S_OK
;
3582 DirEntry streamEntry
;
3584 BlockChainStream
*bbTempChain
= NULL
;
3585 BlockChainStream
*bigBlockChain
= NULL
;
3588 * Create a temporary big block chain that doesn't have
3589 * an associated directory entry. This temporary chain will be
3590 * used to copy data from small blocks to big blocks.
3592 bbTempChain
= BlockChainStream_Construct(This
,
3595 if(!bbTempChain
) return NULL
;
3597 * Grow the big block chain.
3599 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3600 BlockChainStream_SetSize(bbTempChain
, size
);
3603 * Copy the contents of the small block chain to the big block chain
3604 * by small block size increments.
3606 offset
.u
.LowPart
= 0;
3607 offset
.u
.HighPart
= 0;
3608 cbTotalRead
.QuadPart
= 0;
3610 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3613 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3615 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3618 if (FAILED(resRead
))
3623 cbTotalRead
.QuadPart
+= cbRead
;
3625 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3631 if (FAILED(resWrite
))
3634 offset
.u
.LowPart
+= cbRead
;
3638 resRead
= STG_E_READFAULT
;
3641 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3642 HeapFree(GetProcessHeap(),0,buffer
);
3644 size
.u
.HighPart
= 0;
3647 if (FAILED(resRead
) || FAILED(resWrite
))
3649 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3650 BlockChainStream_SetSize(bbTempChain
, size
);
3651 BlockChainStream_Destroy(bbTempChain
);
3656 * Destroy the small block chain.
3658 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3659 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3660 SmallBlockChainStream_Destroy(*ppsbChain
);
3664 * Change the directory entry. This chain is now a big block chain
3665 * and it doesn't reside in the small blocks chain anymore.
3667 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3669 streamEntry
.startingBlock
= bbHeadOfChain
;
3671 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3674 * Destroy the temporary entryless big block chain.
3675 * Create a new big block chain associated with this entry.
3677 BlockChainStream_Destroy(bbTempChain
);
3678 bigBlockChain
= BlockChainStream_Construct(This
,
3682 return bigBlockChain
;
3685 /******************************************************************************
3686 * Storage32Impl_BigBlocksToSmallBlocks
3688 * This method will convert a big block chain to a small block chain.
3689 * The big block chain will be destroyed on success.
3691 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3693 BlockChainStream
** ppbbChain
,
3694 ULARGE_INTEGER newSize
)
3696 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3697 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3698 DirRef streamEntryRef
;
3699 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3700 DirEntry streamEntry
;
3702 SmallBlockChainStream
* sbTempChain
;
3704 TRACE("%p %p\n", This
, ppbbChain
);
3706 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3712 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3713 size
= BlockChainStream_GetSize(*ppbbChain
);
3714 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3716 offset
.u
.HighPart
= 0;
3717 offset
.u
.LowPart
= 0;
3718 cbTotalRead
.QuadPart
= 0;
3719 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3720 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3722 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3723 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3731 cbTotalRead
.QuadPart
+= cbRead
;
3733 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3734 cbRead
, buffer
, &cbWritten
);
3736 if(FAILED(resWrite
))
3739 offset
.u
.LowPart
+= cbRead
;
3743 resRead
= STG_E_READFAULT
;
3747 HeapFree(GetProcessHeap(), 0, buffer
);
3749 size
.u
.HighPart
= 0;
3752 if(FAILED(resRead
) || FAILED(resWrite
))
3754 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3755 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3756 SmallBlockChainStream_Destroy(sbTempChain
);
3760 /* destroy the original big block chain */
3761 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3762 BlockChainStream_SetSize(*ppbbChain
, size
);
3763 BlockChainStream_Destroy(*ppbbChain
);
3766 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3767 streamEntry
.startingBlock
= sbHeadOfChain
;
3768 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3770 SmallBlockChainStream_Destroy(sbTempChain
);
3771 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3774 /******************************************************************************
3775 * Storage32Impl_AddBlockDepot
3777 * This will create a depot block, essentially it is a block initialized
3780 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3782 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3783 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3784 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3785 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3788 * Initialize blocks as free
3790 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3792 /* Reserve the range lock sector */
3793 if (depotIndex
== rangeLockDepot
)
3795 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3798 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3801 /******************************************************************************
3802 * Storage32Impl_GetExtDepotBlock
3804 * Returns the index of the block that corresponds to the specified depot
3805 * index. This method is only for depot indexes equal or greater than
3806 * COUNT_BBDEPOTINHEADER.
3808 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3810 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3811 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3812 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3813 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3814 ULONG blockIndex
= BLOCK_UNUSED
;
3815 ULONG extBlockIndex
;
3816 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3817 int index
, num_blocks
;
3819 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3821 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3822 return BLOCK_UNUSED
;
3824 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3826 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3828 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3830 num_blocks
= This
->bigBlockSize
/ 4;
3832 for (index
= 0; index
< num_blocks
; index
++)
3834 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3835 This
->extBlockDepotCached
[index
] = blockIndex
;
3838 This
->indexExtBlockDepotCached
= extBlockCount
;
3841 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3846 /******************************************************************************
3847 * Storage32Impl_SetExtDepotBlock
3849 * Associates the specified block index to the specified depot index.
3850 * This method is only for depot indexes equal or greater than
3851 * COUNT_BBDEPOTINHEADER.
3853 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3855 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3856 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3857 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3858 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3859 ULONG extBlockIndex
;
3861 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3863 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3865 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3867 if (extBlockIndex
!= BLOCK_UNUSED
)
3869 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3870 extBlockOffset
* sizeof(ULONG
),
3874 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3876 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3880 /******************************************************************************
3881 * Storage32Impl_AddExtBlockDepot
3883 * Creates an extended depot block.
3885 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3887 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3888 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3889 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3890 ULONG index
= BLOCK_UNUSED
;
3891 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3892 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3893 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3895 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3896 blocksPerDepotBlock
;
3898 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3901 * The first extended block.
3903 This
->extBigBlockDepotStart
= index
;
3908 * Find the last existing extended block.
3910 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3913 * Add the new extended block to the chain.
3915 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3920 * Initialize this block.
3922 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3923 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3925 /* Add the block to our cache. */
3926 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3928 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3929 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3931 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3932 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3934 This
->extBigBlockDepotLocations
= new_cache
;
3935 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3937 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3942 /************************************************************************
3943 * StorageImpl_GetNextBlockInChain
3945 * This method will retrieve the block index of the next big block in
3948 * Params: This - Pointer to the Storage object.
3949 * blockIndex - Index of the block to retrieve the chain
3951 * nextBlockIndex - receives the return value.
3953 * Returns: This method returns the index of the next block in the chain.
3954 * It will return the constants:
3955 * BLOCK_SPECIAL - If the block given was not part of a
3957 * BLOCK_END_OF_CHAIN - If the block given was the last in
3959 * BLOCK_UNUSED - If the block given was not past of a chain
3961 * BLOCK_EXTBBDEPOT - This block is part of the extended
3964 * See Windows documentation for more details on IStorage methods.
3966 static HRESULT
StorageImpl_GetNextBlockInChain(
3969 ULONG
* nextBlockIndex
)
3971 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3972 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3973 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3974 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3976 ULONG depotBlockIndexPos
;
3977 int index
, num_blocks
;
3979 *nextBlockIndex
= BLOCK_SPECIAL
;
3981 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3983 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3984 This
->bigBlockDepotCount
);
3985 return STG_E_READFAULT
;
3989 * Cache the currently accessed depot block.
3991 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3993 This
->indexBlockDepotCached
= depotBlockCount
;
3995 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3997 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4002 * We have to look in the extended depot.
4004 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4007 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4010 return STG_E_READFAULT
;
4012 num_blocks
= This
->bigBlockSize
/ 4;
4014 for (index
= 0; index
< num_blocks
; index
++)
4016 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4017 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4021 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4026 /******************************************************************************
4027 * Storage32Impl_GetNextExtendedBlock
4029 * Given an extended block this method will return the next extended block.
4032 * The last ULONG of an extended block is the block index of the next
4033 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4037 * - The index of the next extended block
4038 * - BLOCK_UNUSED: there is no next extended block.
4039 * - Any other return values denotes failure.
4041 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4043 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4044 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4046 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4049 return nextBlockIndex
;
4052 /******************************************************************************
4053 * StorageImpl_SetNextBlockInChain
4055 * This method will write the index of the specified block's next block
4056 * in the big block depot.
4058 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4061 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4062 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4063 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4066 static void StorageImpl_SetNextBlockInChain(
4071 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4072 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4073 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4074 ULONG depotBlockIndexPos
;
4076 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4077 assert(blockIndex
!= nextBlock
);
4079 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4080 /* This should never happen (storage file format spec forbids it), but
4081 * older versions of Wine may have generated broken files. We don't want to
4082 * assert and potentially lose data, but we do want to know if this ever
4083 * happens in a newly-created file. */
4084 ERR("Using range lock page\n");
4086 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4088 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4093 * We have to look in the extended depot.
4095 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4098 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4101 * Update the cached block depot, if necessary.
4103 if (depotBlockCount
== This
->indexBlockDepotCached
)
4105 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4109 /******************************************************************************
4110 * StorageImpl_GetNextFreeBigBlock
4112 * Returns the index of the next free big block.
4113 * If the big block depot is filled, this method will enlarge it.
4116 static ULONG
StorageImpl_GetNextFreeBigBlock(
4119 ULONG depotBlockIndexPos
;
4120 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4121 ULONG depotBlockOffset
;
4122 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4123 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4125 ULONG freeBlock
= BLOCK_UNUSED
;
4127 ULARGE_INTEGER neededSize
;
4130 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4131 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4134 * Scan the entire big block depot until we find a block marked free
4136 while (nextBlockIndex
!= BLOCK_UNUSED
)
4138 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4140 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4143 * Grow the primary depot.
4145 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4147 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4150 * Add a block depot.
4152 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4153 This
->bigBlockDepotCount
++;
4154 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4157 * Flag it as a block depot.
4159 StorageImpl_SetNextBlockInChain(This
,
4163 /* Save new header information.
4165 StorageImpl_SaveFileHeader(This
);
4170 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4172 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4175 * Grow the extended depot.
4177 ULONG extIndex
= BLOCK_UNUSED
;
4178 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4179 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4181 if (extBlockOffset
== 0)
4183 /* We need an extended block.
4185 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4186 This
->extBigBlockDepotCount
++;
4187 depotBlockIndexPos
= extIndex
+ 1;
4190 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4193 * Add a block depot and mark it in the extended block.
4195 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4196 This
->bigBlockDepotCount
++;
4197 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4199 /* Flag the block depot.
4201 StorageImpl_SetNextBlockInChain(This
,
4205 /* If necessary, flag the extended depot block.
4207 if (extIndex
!= BLOCK_UNUSED
)
4208 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4210 /* Save header information.
4212 StorageImpl_SaveFileHeader(This
);
4216 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4220 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4221 ( nextBlockIndex
!= BLOCK_UNUSED
))
4223 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4225 if (nextBlockIndex
== BLOCK_UNUSED
)
4227 freeBlock
= (depotIndex
* blocksPerDepot
) +
4228 (depotBlockOffset
/sizeof(ULONG
));
4231 depotBlockOffset
+= sizeof(ULONG
);
4236 depotBlockOffset
= 0;
4240 * make sure that the block physically exists before using it
4242 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
4244 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4246 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4247 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4249 This
->prevFreeBlock
= freeBlock
;
4254 /******************************************************************************
4255 * StorageImpl_FreeBigBlock
4257 * This method will flag the specified block as free in the big block depot.
4259 static void StorageImpl_FreeBigBlock(
4263 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4265 if (blockIndex
< This
->prevFreeBlock
)
4266 This
->prevFreeBlock
= blockIndex
;
4270 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4271 DirRef index
, const DirEntry
*data
)
4273 StorageImpl
*This
= (StorageImpl
*)base
;
4274 return StorageImpl_WriteDirEntry(This
, index
, data
);
4277 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4278 DirRef index
, DirEntry
*data
)
4280 StorageImpl
*This
= (StorageImpl
*)base
;
4281 return StorageImpl_ReadDirEntry(This
, index
, data
);
4284 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4288 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4290 if (!This
->blockChainCache
[i
])
4292 return &This
->blockChainCache
[i
];
4296 i
= This
->blockChainToEvict
;
4298 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4299 This
->blockChainCache
[i
] = NULL
;
4301 This
->blockChainToEvict
++;
4302 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4303 This
->blockChainToEvict
= 0;
4305 return &This
->blockChainCache
[i
];
4308 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4311 int i
, free_index
=-1;
4313 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4315 if (!This
->blockChainCache
[i
])
4317 if (free_index
== -1) free_index
= i
;
4319 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4321 return &This
->blockChainCache
[i
];
4325 if (free_index
== -1)
4327 free_index
= This
->blockChainToEvict
;
4329 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4330 This
->blockChainCache
[free_index
] = NULL
;
4332 This
->blockChainToEvict
++;
4333 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4334 This
->blockChainToEvict
= 0;
4337 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4338 return &This
->blockChainCache
[free_index
];
4341 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4345 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4347 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4349 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4350 This
->blockChainCache
[i
] = NULL
;
4356 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4357 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4359 StorageImpl
*This
= (StorageImpl
*)base
;
4364 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4365 if (FAILED(hr
)) return hr
;
4367 if (data
.size
.QuadPart
== 0)
4373 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4375 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4382 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4384 SmallBlockChainStream
*stream
;
4386 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4387 if (!stream
) return E_OUTOFMEMORY
;
4389 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4391 SmallBlockChainStream_Destroy(stream
);
4397 BlockChainStream
*stream
= NULL
;
4399 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4400 if (!stream
) return E_OUTOFMEMORY
;
4402 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4408 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4409 ULARGE_INTEGER newsize
)
4411 StorageImpl
*This
= (StorageImpl
*)base
;
4414 SmallBlockChainStream
*smallblock
=NULL
;
4415 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4417 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4418 if (FAILED(hr
)) return hr
;
4420 /* In simple mode keep the stream size above the small block limit */
4421 if (This
->base
.openFlags
& STGM_SIMPLE
)
4422 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4424 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4427 /* Create a block chain object of the appropriate type */
4428 if (data
.size
.QuadPart
== 0)
4430 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4432 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4433 if (!smallblock
) return E_OUTOFMEMORY
;
4437 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4438 bigblock
= *pbigblock
;
4439 if (!bigblock
) return E_OUTOFMEMORY
;
4442 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4444 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4445 if (!smallblock
) return E_OUTOFMEMORY
;
4449 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4450 bigblock
= *pbigblock
;
4451 if (!bigblock
) return E_OUTOFMEMORY
;
4454 /* Change the block chain type if necessary. */
4455 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4457 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4460 SmallBlockChainStream_Destroy(smallblock
);
4464 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4465 *pbigblock
= bigblock
;
4467 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4469 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4474 /* Set the size of the block chain. */
4477 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4478 SmallBlockChainStream_Destroy(smallblock
);
4482 BlockChainStream_SetSize(bigblock
, newsize
);
4485 /* Set the size in the directory entry. */
4486 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4489 data
.size
= newsize
;
4491 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4496 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4497 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4499 StorageImpl
*This
= (StorageImpl
*)base
;
4502 ULARGE_INTEGER newSize
;
4504 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4505 if (FAILED(hr
)) return hr
;
4507 /* Grow the stream if necessary */
4508 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4510 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4512 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4516 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4517 if (FAILED(hr
)) return hr
;
4520 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4522 SmallBlockChainStream
*stream
;
4524 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4525 if (!stream
) return E_OUTOFMEMORY
;
4527 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4529 SmallBlockChainStream_Destroy(stream
);
4535 BlockChainStream
*stream
;
4537 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4538 if (!stream
) return E_OUTOFMEMORY
;
4540 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4544 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4547 StorageImpl
*This
= (StorageImpl
*)base
;
4548 DirEntry dst_data
, src_data
;
4551 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4554 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4558 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4559 dst_data
.startingBlock
= src_data
.startingBlock
;
4560 dst_data
.size
= src_data
.size
;
4562 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4568 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4571 DirEntry currentEntry
;
4572 DirRef currentEntryRef
;
4573 BlockChainStream
*blockChainStream
;
4577 ULARGE_INTEGER size
;
4578 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4580 /* Discard any existing data. */
4582 ILockBytes_SetSize(This
->lockBytes
, size
);
4585 * Initialize all header variables:
4586 * - The big block depot consists of one block and it is at block 0
4587 * - The directory table starts at block 1
4588 * - There is no small block depot
4590 memset( This
->bigBlockDepotStart
,
4592 sizeof(This
->bigBlockDepotStart
));
4594 This
->bigBlockDepotCount
= 1;
4595 This
->bigBlockDepotStart
[0] = 0;
4596 This
->rootStartBlock
= 1;
4597 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4598 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4599 if (This
->bigBlockSize
== 4096)
4600 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4602 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4603 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4604 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4605 This
->extBigBlockDepotCount
= 0;
4607 StorageImpl_SaveFileHeader(This
);
4610 * Add one block for the big block depot and one block for the directory table
4612 size
.u
.HighPart
= 0;
4613 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4614 ILockBytes_SetSize(This
->lockBytes
, size
);
4617 * Initialize the big block depot
4619 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4620 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4621 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4622 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4627 * Load the header for the file.
4629 hr
= StorageImpl_LoadFileHeader(This
);
4638 * There is no block depot cached yet.
4640 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4641 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4644 * Start searching for free blocks with block 0.
4646 This
->prevFreeBlock
= 0;
4648 This
->firstFreeSmallBlock
= 0;
4650 /* Read the extended big block depot locations. */
4651 if (This
->extBigBlockDepotCount
!= 0)
4653 ULONG current_block
= This
->extBigBlockDepotStart
;
4654 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4657 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4658 if (!This
->extBigBlockDepotLocations
)
4660 return E_OUTOFMEMORY
;
4663 This
->extBigBlockDepotLocationsSize
= cache_size
;
4665 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4667 if (current_block
== BLOCK_END_OF_CHAIN
)
4669 WARN("File has too few extended big block depot blocks.\n");
4670 return STG_E_DOCFILECORRUPT
;
4672 This
->extBigBlockDepotLocations
[i
] = current_block
;
4673 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4678 This
->extBigBlockDepotLocations
= NULL
;
4679 This
->extBigBlockDepotLocationsSize
= 0;
4683 * Create the block chain abstractions.
4685 if(!(blockChainStream
=
4686 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4688 return STG_E_READFAULT
;
4691 BlockChainStream_Destroy(This
->rootBlockChain
);
4692 This
->rootBlockChain
= blockChainStream
;
4694 if(!(blockChainStream
=
4695 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4698 return STG_E_READFAULT
;
4701 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4702 This
->smallBlockDepotChain
= blockChainStream
;
4705 * Write the root storage entry (memory only)
4709 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4712 * Initialize the directory table
4714 memset(&rootEntry
, 0, sizeof(rootEntry
));
4715 strcpyW(rootEntry
.name
, rootentryW
);
4716 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
4717 rootEntry
.stgType
= STGTY_ROOT
;
4718 rootEntry
.leftChild
= DIRENTRY_NULL
;
4719 rootEntry
.rightChild
= DIRENTRY_NULL
;
4720 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4721 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4722 rootEntry
.size
.u
.HighPart
= 0;
4723 rootEntry
.size
.u
.LowPart
= 0;
4725 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4729 * Find the ID of the root storage.
4731 currentEntryRef
= 0;
4735 hr
= StorageImpl_ReadDirEntry(
4742 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4743 (currentEntry
.stgType
== STGTY_ROOT
) )
4745 This
->base
.storageDirEntry
= currentEntryRef
;
4751 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4755 return STG_E_READFAULT
;
4759 * Create the block chain abstraction for the small block root chain.
4761 if(!(blockChainStream
=
4762 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4764 return STG_E_READFAULT
;
4767 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4768 This
->smallBlockRootChain
= blockChainStream
;
4773 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4775 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4776 This
->blockChainCache
[i
] = NULL
;
4783 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4784 ULONG
* result
, BOOL refresh
)
4786 StorageImpl
*This
= (StorageImpl
*)base
;
4788 DWORD oldTransactionSig
= This
->transactionSig
;
4792 ULARGE_INTEGER offset
;
4796 offset
.u
.HighPart
= 0;
4797 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4798 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4802 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4804 if (oldTransactionSig
!= This
->transactionSig
)
4806 /* Someone else wrote to this, so toss all cached information. */
4807 TRACE("signature changed\n");
4809 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4813 This
->transactionSig
= oldTransactionSig
;
4817 *result
= This
->transactionSig
;
4822 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4825 StorageImpl
*This
= (StorageImpl
*)base
;
4827 This
->transactionSig
= value
;
4828 StorageImpl_SaveFileHeader(This
);
4833 static HRESULT
StorageImpl_LockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4834 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4836 if ((dwLockType
& This
->locks_supported
) == 0)
4838 if (supported
) *supported
= FALSE
;
4842 if (supported
) *supported
= TRUE
;
4843 return ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4846 static HRESULT
StorageImpl_UnlockRegion(StorageImpl
*This
, ULARGE_INTEGER offset
,
4847 ULARGE_INTEGER cb
, DWORD dwLockType
)
4849 if ((dwLockType
& This
->locks_supported
) == 0)
4852 return ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4855 /* Internal function */
4856 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4857 ULARGE_INTEGER cb
, DWORD dwLockType
, BOOL
*supported
)
4861 DWORD start_time
= GetTickCount();
4862 DWORD last_sanity_check
= start_time
;
4863 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4865 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4866 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4870 hr
= StorageImpl_LockRegion(This
, offset
, cb
, dwLockType
, supported
);
4872 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4874 DWORD current_time
= GetTickCount();
4875 if (current_time
- start_time
>= 20000)
4880 if (current_time
- last_sanity_check
>= 500)
4882 /* Any storage implementation with the file open in a
4883 * shared mode should not lock these bytes for writing. However,
4884 * some programs (LibreOffice Writer) will keep ALL bytes locked
4885 * when opening in exclusive mode. We can use a read lock to
4886 * detect this case early, and not hang a full 20 seconds.
4888 * This can collide with another attempt to open the file in
4889 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4890 hr
= StorageImpl_LockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
, NULL
);
4891 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4895 StorageImpl_UnlockRegion(This
, sanity_offset
, sanity_cb
, WINE_LOCK_READ
);
4896 hr
= STG_E_ACCESSDENIED
;
4899 last_sanity_check
= current_time
;
4902 if (delay
< 150) delay
++;
4904 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4909 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4911 StorageImpl
*This
= (StorageImpl
*)base
;
4913 ULARGE_INTEGER offset
, cb
;
4917 /* Synchronous grab of second priority range, the commit lock, and the
4918 * lock-checking lock. */
4919 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4920 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4924 offset
.QuadPart
= RANGELOCK_COMMIT
;
4928 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4933 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4935 StorageImpl
*This
= (StorageImpl
*)base
;
4937 ULARGE_INTEGER offset
, cb
;
4941 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4942 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4946 offset
.QuadPart
= RANGELOCK_COMMIT
;
4950 hr
= StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4955 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4957 StorageImpl
*This
= (StorageImpl
*) iface
;
4961 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
4963 *result
= statstg
.pwcsName
;
4968 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
4969 ULONG end
, HRESULT fail_hr
)
4972 ULARGE_INTEGER offset
, cb
;
4974 offset
.QuadPart
= start
;
4975 cb
.QuadPart
= 1 + end
- start
;
4977 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4978 if (SUCCEEDED(hr
)) StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
4986 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
4990 ULARGE_INTEGER offset
, cb
;
4994 for (i
=start
; i
<=end
; i
++)
4996 offset
.QuadPart
= i
;
4997 hr
= StorageImpl_LockRegion(This
, offset
, cb
, LOCK_ONLYONCE
, NULL
);
4998 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
5004 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
5006 if (This
->locked_bytes
[j
] == 0)
5008 This
->locked_bytes
[j
] = i
;
5017 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5020 ULARGE_INTEGER offset
;
5022 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5025 if (openFlags
& STGM_NOSNAPSHOT
)
5027 /* STGM_NOSNAPSHOT implies deny write */
5028 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5029 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5032 /* Wrap all other locking inside a single lock so we can check ranges safely */
5033 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5035 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
, &supported
);
5037 /* If the ILockBytes doesn't support locking that's ok. */
5038 if (!supported
) return S_OK
;
5039 else if (FAILED(hr
)) return hr
;
5043 /* First check for any conflicting locks. */
5044 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5045 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5047 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5048 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5050 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5051 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5053 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5054 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5056 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5057 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5059 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5061 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5064 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5067 /* Then grab our locks. */
5068 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5070 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5072 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5075 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5076 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5078 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5079 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5081 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5082 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5084 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5085 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5087 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5088 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5090 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5092 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5097 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5099 StorageImpl
*This
= (StorageImpl
*)storage
;
5102 TRACE("(%p)\n", This
);
5104 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5107 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5110 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5112 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5113 if (This
->blockChainCache
[i
])
5114 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5117 hr
= ILockBytes_Flush(This
->lockBytes
);
5122 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5124 StorageImpl
*This
= (StorageImpl
*) iface
;
5126 StorageBaseImpl_DeleteAll(&This
->base
);
5128 This
->base
.reverted
= TRUE
;
5131 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5133 StorageImpl
*This
= (StorageImpl
*) iface
;
5135 TRACE("(%p)\n", This
);
5137 StorageImpl_Flush(iface
);
5139 StorageImpl_Invalidate(iface
);
5141 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5143 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5144 BlockChainStream_Destroy(This
->rootBlockChain
);
5145 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5147 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5148 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5150 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
5152 ULARGE_INTEGER offset
, cb
;
5154 if (This
->locked_bytes
[i
] != 0)
5156 offset
.QuadPart
= This
->locked_bytes
[i
];
5157 StorageImpl_UnlockRegion(This
, offset
, cb
, LOCK_ONLYONCE
);
5161 if (This
->lockBytes
)
5162 ILockBytes_Release(This
->lockBytes
);
5163 HeapFree(GetProcessHeap(), 0, This
);
5167 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5169 StorageImpl_Destroy
,
5170 StorageImpl_Invalidate
,
5172 StorageImpl_GetFilename
,
5173 StorageImpl_CreateDirEntry
,
5174 StorageImpl_BaseWriteDirEntry
,
5175 StorageImpl_BaseReadDirEntry
,
5176 StorageImpl_DestroyDirEntry
,
5177 StorageImpl_StreamReadAt
,
5178 StorageImpl_StreamWriteAt
,
5179 StorageImpl_StreamSetSize
,
5180 StorageImpl_StreamLink
,
5181 StorageImpl_GetTransactionSig
,
5182 StorageImpl_SetTransactionSig
,
5183 StorageImpl_LockTransaction
,
5184 StorageImpl_UnlockTransaction
5189 * Virtual function table for the IStorageBaseImpl class.
5191 static const IStorageVtbl StorageImpl_Vtbl
=
5193 StorageBaseImpl_QueryInterface
,
5194 StorageBaseImpl_AddRef
,
5195 StorageBaseImpl_Release
,
5196 StorageBaseImpl_CreateStream
,
5197 StorageBaseImpl_OpenStream
,
5198 StorageBaseImpl_CreateStorage
,
5199 StorageBaseImpl_OpenStorage
,
5200 StorageBaseImpl_CopyTo
,
5201 StorageBaseImpl_MoveElementTo
,
5202 StorageBaseImpl_Commit
,
5203 StorageBaseImpl_Revert
,
5204 StorageBaseImpl_EnumElements
,
5205 StorageBaseImpl_DestroyElement
,
5206 StorageBaseImpl_RenameElement
,
5207 StorageBaseImpl_SetElementTimes
,
5208 StorageBaseImpl_SetClass
,
5209 StorageBaseImpl_SetStateBits
,
5210 StorageBaseImpl_Stat
5213 static HRESULT
StorageImpl_Construct(
5221 StorageImpl
** result
)
5227 if ( FAILED( validateSTGM(openFlags
) ))
5228 return STG_E_INVALIDFLAG
;
5230 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5232 return E_OUTOFMEMORY
;
5234 memset(This
, 0, sizeof(StorageImpl
));
5236 list_init(&This
->base
.strmHead
);
5238 list_init(&This
->base
.storageHead
);
5240 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5241 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5242 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5243 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5244 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5246 This
->base
.create
= create
;
5248 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5249 This
->base
.lockingrole
= SWMR_Writer
;
5250 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5251 This
->base
.lockingrole
= SWMR_Reader
;
5253 This
->base
.lockingrole
= SWMR_None
;
5255 This
->base
.reverted
= FALSE
;
5258 * Initialize the big block cache.
5260 This
->bigBlockSize
= sector_size
;
5261 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5263 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5266 This
->lockBytes
= pLkbyt
;
5267 ILockBytes_AddRef(pLkbyt
);
5271 hr
= ILockBytes_Stat(This
->lockBytes
, &stat
, STATFLAG_NONAME
);
5275 This
->locks_supported
= stat
.grfLocksSupported
;
5277 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5278 This
->locks_supported
&= ~WINE_LOCK_READ
;
5280 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5284 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5288 IStorage_Release(&This
->base
.IStorage_iface
);
5293 StorageImpl_Flush(&This
->base
);
5301 /************************************************************************
5302 * StorageInternalImpl implementation
5303 ***********************************************************************/
5305 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5307 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5309 if (!This
->base
.reverted
)
5311 TRACE("Storage invalidated (stg=%p)\n", This
);
5313 This
->base
.reverted
= TRUE
;
5315 This
->parentStorage
= NULL
;
5317 StorageBaseImpl_DeleteAll(&This
->base
);
5319 list_remove(&This
->ParentListEntry
);
5323 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5325 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5327 StorageInternalImpl_Invalidate(&This
->base
);
5329 HeapFree(GetProcessHeap(), 0, This
);
5332 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5334 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5336 return StorageBaseImpl_Flush(This
->parentStorage
);
5339 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5341 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5343 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5346 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5347 const DirEntry
*newData
, DirRef
*index
)
5349 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5351 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5355 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5356 DirRef index
, const DirEntry
*data
)
5358 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5360 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5364 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5365 DirRef index
, DirEntry
*data
)
5367 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5369 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5373 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5376 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5378 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5382 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5383 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5385 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5387 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5388 index
, offset
, size
, buffer
, bytesRead
);
5391 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5392 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5394 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5396 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5397 index
, offset
, size
, buffer
, bytesWritten
);
5400 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5401 DirRef index
, ULARGE_INTEGER newsize
)
5403 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5405 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5409 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5410 DirRef dst
, DirRef src
)
5412 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5414 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5418 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5419 ULONG
* result
, BOOL refresh
)
5424 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5430 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5435 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5440 /******************************************************************************
5442 ** StorageInternalImpl_Commit
5445 static HRESULT WINAPI
StorageInternalImpl_Commit(
5447 DWORD grfCommitFlags
) /* [in] */
5449 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5450 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5451 return StorageBaseImpl_Flush(This
);
5454 /******************************************************************************
5456 ** StorageInternalImpl_Revert
5459 static HRESULT WINAPI
StorageInternalImpl_Revert(
5462 FIXME("(%p): stub\n", iface
);
5467 * Virtual function table for the StorageInternalImpl class.
5469 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5471 StorageBaseImpl_QueryInterface
,
5472 StorageBaseImpl_AddRef
,
5473 StorageBaseImpl_Release
,
5474 StorageBaseImpl_CreateStream
,
5475 StorageBaseImpl_OpenStream
,
5476 StorageBaseImpl_CreateStorage
,
5477 StorageBaseImpl_OpenStorage
,
5478 StorageBaseImpl_CopyTo
,
5479 StorageBaseImpl_MoveElementTo
,
5480 StorageInternalImpl_Commit
,
5481 StorageInternalImpl_Revert
,
5482 StorageBaseImpl_EnumElements
,
5483 StorageBaseImpl_DestroyElement
,
5484 StorageBaseImpl_RenameElement
,
5485 StorageBaseImpl_SetElementTimes
,
5486 StorageBaseImpl_SetClass
,
5487 StorageBaseImpl_SetStateBits
,
5488 StorageBaseImpl_Stat
5491 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5493 StorageInternalImpl_Destroy
,
5494 StorageInternalImpl_Invalidate
,
5495 StorageInternalImpl_Flush
,
5496 StorageInternalImpl_GetFilename
,
5497 StorageInternalImpl_CreateDirEntry
,
5498 StorageInternalImpl_WriteDirEntry
,
5499 StorageInternalImpl_ReadDirEntry
,
5500 StorageInternalImpl_DestroyDirEntry
,
5501 StorageInternalImpl_StreamReadAt
,
5502 StorageInternalImpl_StreamWriteAt
,
5503 StorageInternalImpl_StreamSetSize
,
5504 StorageInternalImpl_StreamLink
,
5505 StorageInternalImpl_GetTransactionSig
,
5506 StorageInternalImpl_SetTransactionSig
,
5507 StorageInternalImpl_LockTransaction
,
5508 StorageInternalImpl_UnlockTransaction
5511 static StorageInternalImpl
* StorageInternalImpl_Construct(
5512 StorageBaseImpl
* parentStorage
,
5514 DirRef storageDirEntry
)
5516 StorageInternalImpl
* newStorage
;
5518 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5522 list_init(&newStorage
->base
.strmHead
);
5524 list_init(&newStorage
->base
.storageHead
);
5527 * Initialize the virtual function table.
5529 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5530 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5531 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5532 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5534 newStorage
->base
.reverted
= FALSE
;
5536 newStorage
->base
.ref
= 1;
5538 newStorage
->parentStorage
= parentStorage
;
5541 * Keep a reference to the directory entry of this storage
5543 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5545 newStorage
->base
.create
= FALSE
;
5554 /************************************************************************
5555 * TransactedSnapshotImpl implementation
5556 ***********************************************************************/
5558 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5560 DirRef result
=This
->firstFreeEntry
;
5562 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5565 if (result
== This
->entries_size
)
5567 ULONG new_size
= This
->entries_size
* 2;
5568 TransactedDirEntry
*new_entries
;
5570 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5571 if (!new_entries
) return DIRENTRY_NULL
;
5573 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5574 HeapFree(GetProcessHeap(), 0, This
->entries
);
5576 This
->entries
= new_entries
;
5577 This
->entries_size
= new_size
;
5580 This
->entries
[result
].inuse
= TRUE
;
5582 This
->firstFreeEntry
= result
+1;
5587 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5588 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5590 DirRef stubEntryRef
;
5591 TransactedDirEntry
*entry
;
5593 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5595 if (stubEntryRef
!= DIRENTRY_NULL
)
5597 entry
= &This
->entries
[stubEntryRef
];
5599 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5601 entry
->read
= FALSE
;
5604 return stubEntryRef
;
5607 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5608 TransactedSnapshotImpl
*This
, DirRef entry
)
5613 if (!This
->entries
[entry
].read
)
5615 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5616 This
->entries
[entry
].transactedParentEntry
,
5619 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5621 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5623 if (data
.leftChild
== DIRENTRY_NULL
)
5627 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5629 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5631 if (data
.rightChild
== DIRENTRY_NULL
)
5635 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5637 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5639 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5645 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5646 This
->entries
[entry
].read
= TRUE
;
5653 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5654 TransactedSnapshotImpl
*This
, DirRef entry
)
5658 if (!This
->entries
[entry
].stream_dirty
)
5660 DirEntry new_entrydata
;
5662 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5663 new_entrydata
.name
[0] = 'S';
5664 new_entrydata
.sizeOfNameString
= 1;
5665 new_entrydata
.stgType
= STGTY_STREAM
;
5666 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5667 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5668 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5669 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5671 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5672 &This
->entries
[entry
].stream_entry
);
5674 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5676 hr
= StorageBaseImpl_CopyStream(
5677 This
->scratch
, This
->entries
[entry
].stream_entry
,
5678 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5681 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5685 This
->entries
[entry
].stream_dirty
= TRUE
;
5687 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5689 /* Since this entry is modified, and we aren't using its stream data, we
5690 * no longer care about the original entry. */
5692 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5694 if (delete_ref
!= DIRENTRY_NULL
)
5695 This
->entries
[delete_ref
].deleted
= TRUE
;
5697 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5704 /* Find the first entry in a depth-first traversal. */
5705 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5706 TransactedSnapshotImpl
* This
, DirRef parent
)
5708 DirRef cursor
, prev
;
5709 TransactedDirEntry
*entry
;
5712 entry
= &This
->entries
[cursor
];
5715 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5718 cursor
= entry
->data
.leftChild
;
5719 entry
= &This
->entries
[cursor
];
5720 entry
->parent
= prev
;
5722 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5725 cursor
= entry
->data
.rightChild
;
5726 entry
= &This
->entries
[cursor
];
5727 entry
->parent
= prev
;
5729 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5732 cursor
= entry
->data
.dirRootEntry
;
5733 entry
= &This
->entries
[cursor
];
5734 entry
->parent
= prev
;
5743 /* Find the next entry in a depth-first traversal. */
5744 static DirRef
TransactedSnapshotImpl_FindNextChild(
5745 TransactedSnapshotImpl
* This
, DirRef current
)
5748 TransactedDirEntry
*parent_entry
;
5750 parent
= This
->entries
[current
].parent
;
5751 parent_entry
= &This
->entries
[parent
];
5753 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5755 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5757 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5758 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5761 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5763 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5764 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5771 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5772 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5773 TransactedSnapshotImpl
* This
, DirRef entry
)
5775 return entry
!= DIRENTRY_NULL
&&
5776 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5779 /* Destroy the entries created by CopyTree. */
5780 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5781 TransactedSnapshotImpl
* This
, DirRef stop
)
5784 TransactedDirEntry
*entry
;
5785 ULARGE_INTEGER zero
;
5789 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5792 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5794 if (cursor
== DIRENTRY_NULL
)
5797 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5799 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5801 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5803 entry
= &This
->entries
[cursor
];
5805 if (entry
->stream_dirty
)
5806 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5807 entry
->newTransactedParentEntry
, zero
);
5809 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5810 entry
->newTransactedParentEntry
);
5812 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5815 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5819 /* Make a copy of our edited tree that we can use in the parent. */
5820 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5823 TransactedDirEntry
*entry
;
5826 cursor
= This
->base
.storageDirEntry
;
5827 entry
= &This
->entries
[cursor
];
5828 entry
->parent
= DIRENTRY_NULL
;
5829 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5831 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5834 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5836 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5837 entry
= &This
->entries
[cursor
];
5839 while (cursor
!= DIRENTRY_NULL
)
5841 /* Make a copy of this entry in the transacted parent. */
5843 (!entry
->dirty
&& !entry
->stream_dirty
&&
5844 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5845 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5846 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5847 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5852 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5854 newData
.size
.QuadPart
= 0;
5855 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5857 if (newData
.leftChild
!= DIRENTRY_NULL
)
5858 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5860 if (newData
.rightChild
!= DIRENTRY_NULL
)
5861 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5863 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5864 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5866 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5867 &entry
->newTransactedParentEntry
);
5870 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5874 if (entry
->stream_dirty
)
5876 hr
= StorageBaseImpl_CopyStream(
5877 This
->transactedParent
, entry
->newTransactedParentEntry
,
5878 This
->scratch
, entry
->stream_entry
);
5880 else if (entry
->data
.size
.QuadPart
)
5882 hr
= StorageBaseImpl_StreamLink(
5883 This
->transactedParent
, entry
->newTransactedParentEntry
,
5884 entry
->transactedParentEntry
);
5889 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5890 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5895 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5896 entry
= &This
->entries
[cursor
];
5902 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5904 DWORD grfCommitFlags
) /* [in] */
5906 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5907 TransactedDirEntry
*root_entry
;
5908 DirRef i
, dir_root_ref
;
5910 ULARGE_INTEGER zero
;
5912 ULONG transactionSig
;
5916 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5918 /* Cannot commit a read-only transacted storage */
5919 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5920 return STG_E_ACCESSDENIED
;
5922 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5923 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5926 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5929 if (transactionSig
!= This
->lastTransactionSig
)
5931 ERR("file was externally modified\n");
5932 hr
= STG_E_NOTCURRENT
;
5937 This
->lastTransactionSig
= transactionSig
+1;
5938 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5941 else if (hr
== E_NOTIMPL
)
5944 if (FAILED(hr
)) goto end
;
5946 /* To prevent data loss, we create the new structure in the file before we
5947 * delete the old one, so that in case of errors the old data is intact. We
5948 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5949 * needed in the rare situation where we have just enough free disk space to
5950 * overwrite the existing data. */
5952 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5954 if (!root_entry
->read
)
5957 hr
= TransactedSnapshotImpl_CopyTree(This
);
5958 if (FAILED(hr
)) goto end
;
5960 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5961 dir_root_ref
= DIRENTRY_NULL
;
5963 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5965 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5967 /* Update the storage to use the new data in one step. */
5969 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5970 root_entry
->transactedParentEntry
, &data
);
5974 data
.dirRootEntry
= dir_root_ref
;
5975 data
.clsid
= root_entry
->data
.clsid
;
5976 data
.ctime
= root_entry
->data
.ctime
;
5977 data
.mtime
= root_entry
->data
.mtime
;
5979 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5980 root_entry
->transactedParentEntry
, &data
);
5983 /* Try to flush after updating the root storage, but if the flush fails, keep
5984 * going, on the theory that it'll either succeed later or the subsequent
5985 * writes will fail. */
5986 StorageBaseImpl_Flush(This
->transactedParent
);
5990 /* Destroy the old now-orphaned data. */
5991 for (i
=0; i
<This
->entries_size
; i
++)
5993 TransactedDirEntry
*entry
= &This
->entries
[i
];
5998 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5999 entry
->transactedParentEntry
, zero
);
6000 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6001 entry
->transactedParentEntry
);
6002 memset(entry
, 0, sizeof(TransactedDirEntry
));
6003 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
6005 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
6007 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
6008 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
6009 entry
->transactedParentEntry
);
6010 if (entry
->stream_dirty
)
6012 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
6013 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
6014 entry
->stream_dirty
= FALSE
;
6016 entry
->dirty
= FALSE
;
6017 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
6024 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
6028 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6030 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6036 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6039 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6040 ULARGE_INTEGER zero
;
6043 TRACE("(%p)\n", iface
);
6045 /* Destroy the open objects. */
6046 StorageBaseImpl_DeleteAll(&This
->base
);
6048 /* Clear out the scratch file. */
6050 for (i
=0; i
<This
->entries_size
; i
++)
6052 if (This
->entries
[i
].stream_dirty
)
6054 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6057 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6061 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6063 This
->firstFreeEntry
= 0;
6064 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6069 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6071 if (!This
->reverted
)
6073 TRACE("Storage invalidated (stg=%p)\n", This
);
6075 This
->reverted
= TRUE
;
6077 StorageBaseImpl_DeleteAll(This
);
6081 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6083 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6085 IStorage_Revert(&This
->base
.IStorage_iface
);
6086 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6087 IStorage_Release(&This
->scratch
->IStorage_iface
);
6088 HeapFree(GetProcessHeap(), 0, This
->entries
);
6089 HeapFree(GetProcessHeap(), 0, This
);
6092 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6094 /* We only need to flush when committing. */
6098 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6100 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6102 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6105 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6106 const DirEntry
*newData
, DirRef
*index
)
6108 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6110 TransactedDirEntry
*new_entry
;
6112 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6113 if (new_ref
== DIRENTRY_NULL
)
6114 return E_OUTOFMEMORY
;
6116 new_entry
= &This
->entries
[new_ref
];
6118 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6119 new_entry
->read
= TRUE
;
6120 new_entry
->dirty
= TRUE
;
6121 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6125 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6130 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6131 DirRef index
, const DirEntry
*data
)
6133 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6136 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6138 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6139 if (FAILED(hr
)) return hr
;
6141 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6143 if (index
!= This
->base
.storageDirEntry
)
6145 This
->entries
[index
].dirty
= TRUE
;
6147 if (data
->size
.QuadPart
== 0 &&
6148 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6150 /* Since this entry is modified, and we aren't using its stream data, we
6151 * no longer care about the original entry. */
6153 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6155 if (delete_ref
!= DIRENTRY_NULL
)
6156 This
->entries
[delete_ref
].deleted
= TRUE
;
6158 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6165 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6166 DirRef index
, DirEntry
*data
)
6168 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6171 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6172 if (FAILED(hr
)) return hr
;
6174 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6176 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6181 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6184 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6186 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6187 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6189 /* If we deleted this entry while it has stream data. We must have left the
6190 * data because some other entry is using it, and we need to leave the
6191 * original entry alone. */
6192 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6193 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6197 This
->entries
[index
].deleted
= TRUE
;
6203 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6204 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6206 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6208 if (This
->entries
[index
].stream_dirty
)
6210 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6211 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6213 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6215 /* This stream doesn't live in the parent, and we haven't allocated storage
6222 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6223 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6227 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6228 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6230 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6233 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6234 if (FAILED(hr
)) return hr
;
6236 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6237 if (FAILED(hr
)) return hr
;
6239 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6240 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6242 if (SUCCEEDED(hr
) && size
!= 0)
6243 This
->entries
[index
].data
.size
.QuadPart
= max(
6244 This
->entries
[index
].data
.size
.QuadPart
,
6245 offset
.QuadPart
+ size
);
6250 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6251 DirRef index
, ULARGE_INTEGER newsize
)
6253 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6256 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6257 if (FAILED(hr
)) return hr
;
6259 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6262 if (newsize
.QuadPart
== 0)
6264 /* Destroy any parent references or entries in the scratch file. */
6265 if (This
->entries
[index
].stream_dirty
)
6267 ULARGE_INTEGER zero
;
6269 StorageBaseImpl_StreamSetSize(This
->scratch
,
6270 This
->entries
[index
].stream_entry
, zero
);
6271 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6272 This
->entries
[index
].stream_entry
);
6273 This
->entries
[index
].stream_dirty
= FALSE
;
6275 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6278 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6280 if (delete_ref
!= DIRENTRY_NULL
)
6281 This
->entries
[delete_ref
].deleted
= TRUE
;
6283 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6288 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6289 if (FAILED(hr
)) return hr
;
6291 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6292 This
->entries
[index
].stream_entry
, newsize
);
6296 This
->entries
[index
].data
.size
= newsize
;
6301 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6302 DirRef dst
, DirRef src
)
6304 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6306 TransactedDirEntry
*dst_entry
, *src_entry
;
6308 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6309 if (FAILED(hr
)) return hr
;
6311 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6312 if (FAILED(hr
)) return hr
;
6314 dst_entry
= &This
->entries
[dst
];
6315 src_entry
= &This
->entries
[src
];
6317 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6318 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6319 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6320 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6321 dst_entry
->data
.size
= src_entry
->data
.size
;
6326 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6327 ULONG
* result
, BOOL refresh
)
6332 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6338 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6343 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6348 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6350 StorageBaseImpl_QueryInterface
,
6351 StorageBaseImpl_AddRef
,
6352 StorageBaseImpl_Release
,
6353 StorageBaseImpl_CreateStream
,
6354 StorageBaseImpl_OpenStream
,
6355 StorageBaseImpl_CreateStorage
,
6356 StorageBaseImpl_OpenStorage
,
6357 StorageBaseImpl_CopyTo
,
6358 StorageBaseImpl_MoveElementTo
,
6359 TransactedSnapshotImpl_Commit
,
6360 TransactedSnapshotImpl_Revert
,
6361 StorageBaseImpl_EnumElements
,
6362 StorageBaseImpl_DestroyElement
,
6363 StorageBaseImpl_RenameElement
,
6364 StorageBaseImpl_SetElementTimes
,
6365 StorageBaseImpl_SetClass
,
6366 StorageBaseImpl_SetStateBits
,
6367 StorageBaseImpl_Stat
6370 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6372 TransactedSnapshotImpl_Destroy
,
6373 TransactedSnapshotImpl_Invalidate
,
6374 TransactedSnapshotImpl_Flush
,
6375 TransactedSnapshotImpl_GetFilename
,
6376 TransactedSnapshotImpl_CreateDirEntry
,
6377 TransactedSnapshotImpl_WriteDirEntry
,
6378 TransactedSnapshotImpl_ReadDirEntry
,
6379 TransactedSnapshotImpl_DestroyDirEntry
,
6380 TransactedSnapshotImpl_StreamReadAt
,
6381 TransactedSnapshotImpl_StreamWriteAt
,
6382 TransactedSnapshotImpl_StreamSetSize
,
6383 TransactedSnapshotImpl_StreamLink
,
6384 TransactedSnapshotImpl_GetTransactionSig
,
6385 TransactedSnapshotImpl_SetTransactionSig
,
6386 TransactedSnapshotImpl_LockTransaction
,
6387 TransactedSnapshotImpl_UnlockTransaction
6390 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6391 TransactedSnapshotImpl
** result
)
6395 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6400 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6402 /* This is OK because the property set storage functions use the IStorage functions. */
6403 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6404 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6406 list_init(&(*result
)->base
.strmHead
);
6408 list_init(&(*result
)->base
.storageHead
);
6410 (*result
)->base
.ref
= 1;
6412 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6414 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6415 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6417 /* Create a new temporary storage to act as the scratch file. */
6418 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6420 (*result
)->scratch
= impl_from_IStorage(scratch
);
6424 ULONG num_entries
= 20;
6426 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6427 (*result
)->entries_size
= num_entries
;
6428 (*result
)->firstFreeEntry
= 0;
6430 if ((*result
)->entries
)
6432 /* parentStorage already has 1 reference, which we take over here. */
6433 (*result
)->transactedParent
= parentStorage
;
6435 parentStorage
->transactedChild
= &(*result
)->base
;
6437 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6441 IStorage_Release(scratch
);
6447 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6452 return E_OUTOFMEMORY
;
6456 /************************************************************************
6457 * TransactedSharedImpl implementation
6458 ***********************************************************************/
6460 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6462 if (!This
->reverted
)
6464 TRACE("Storage invalidated (stg=%p)\n", This
);
6466 This
->reverted
= TRUE
;
6468 StorageBaseImpl_DeleteAll(This
);
6472 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6474 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6476 TransactedSharedImpl_Invalidate(&This
->base
);
6477 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6478 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6479 HeapFree(GetProcessHeap(), 0, This
);
6482 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6484 /* We only need to flush when committing. */
6488 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6490 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6492 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6495 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6496 const DirEntry
*newData
, DirRef
*index
)
6498 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6500 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6504 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6505 DirRef index
, const DirEntry
*data
)
6507 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6509 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6513 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6514 DirRef index
, DirEntry
*data
)
6516 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6518 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6522 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6525 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6527 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6531 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6532 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6534 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6536 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6537 index
, offset
, size
, buffer
, bytesRead
);
6540 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6541 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6543 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6545 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6546 index
, offset
, size
, buffer
, bytesWritten
);
6549 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6550 DirRef index
, ULARGE_INTEGER newsize
)
6552 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6554 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6558 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6559 DirRef dst
, DirRef src
)
6561 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6563 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6567 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6568 ULONG
* result
, BOOL refresh
)
6573 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6579 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6584 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6589 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6591 DWORD grfCommitFlags
) /* [in] */
6593 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6594 DirRef new_storage_ref
, prev_storage_ref
;
6595 DirEntry src_data
, dst_data
;
6597 ULONG transactionSig
;
6599 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6601 /* Cannot commit a read-only transacted storage */
6602 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6603 return STG_E_ACCESSDENIED
;
6605 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6606 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6609 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6612 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6613 hr
= STG_E_NOTCURRENT
;
6616 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6618 else if (hr
== E_NOTIMPL
)
6622 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6624 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6626 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6629 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6632 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6636 prev_storage_ref
= dst_data
.dirRootEntry
;
6637 dst_data
.dirRootEntry
= new_storage_ref
;
6638 dst_data
.clsid
= src_data
.clsid
;
6639 dst_data
.ctime
= src_data
.ctime
;
6640 dst_data
.mtime
= src_data
.mtime
;
6641 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6646 /* Try to flush after updating the root storage, but if the flush fails, keep
6647 * going, on the theory that it'll either succeed later or the subsequent
6648 * writes will fail. */
6649 StorageBaseImpl_Flush(This
->transactedParent
);
6651 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6655 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6657 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6660 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6664 This
->lastTransactionSig
= transactionSig
+1;
6671 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6674 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6676 TRACE("(%p)\n", iface
);
6678 /* Destroy the open objects. */
6679 StorageBaseImpl_DeleteAll(&This
->base
);
6681 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6684 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6686 StorageBaseImpl_QueryInterface
,
6687 StorageBaseImpl_AddRef
,
6688 StorageBaseImpl_Release
,
6689 StorageBaseImpl_CreateStream
,
6690 StorageBaseImpl_OpenStream
,
6691 StorageBaseImpl_CreateStorage
,
6692 StorageBaseImpl_OpenStorage
,
6693 StorageBaseImpl_CopyTo
,
6694 StorageBaseImpl_MoveElementTo
,
6695 TransactedSharedImpl_Commit
,
6696 TransactedSharedImpl_Revert
,
6697 StorageBaseImpl_EnumElements
,
6698 StorageBaseImpl_DestroyElement
,
6699 StorageBaseImpl_RenameElement
,
6700 StorageBaseImpl_SetElementTimes
,
6701 StorageBaseImpl_SetClass
,
6702 StorageBaseImpl_SetStateBits
,
6703 StorageBaseImpl_Stat
6706 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6708 TransactedSharedImpl_Destroy
,
6709 TransactedSharedImpl_Invalidate
,
6710 TransactedSharedImpl_Flush
,
6711 TransactedSharedImpl_GetFilename
,
6712 TransactedSharedImpl_CreateDirEntry
,
6713 TransactedSharedImpl_WriteDirEntry
,
6714 TransactedSharedImpl_ReadDirEntry
,
6715 TransactedSharedImpl_DestroyDirEntry
,
6716 TransactedSharedImpl_StreamReadAt
,
6717 TransactedSharedImpl_StreamWriteAt
,
6718 TransactedSharedImpl_StreamSetSize
,
6719 TransactedSharedImpl_StreamLink
,
6720 TransactedSharedImpl_GetTransactionSig
,
6721 TransactedSharedImpl_SetTransactionSig
,
6722 TransactedSharedImpl_LockTransaction
,
6723 TransactedSharedImpl_UnlockTransaction
6726 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6727 TransactedSharedImpl
** result
)
6731 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6736 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6738 /* This is OK because the property set storage functions use the IStorage functions. */
6739 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6740 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6742 list_init(&(*result
)->base
.strmHead
);
6744 list_init(&(*result
)->base
.storageHead
);
6746 (*result
)->base
.ref
= 1;
6748 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6750 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6756 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6757 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6761 stgo
.ulSectorSize
= 4096;
6762 stgo
.pwcsTemplateFile
= NULL
;
6764 /* Create a new temporary storage to act as the scratch file. */
6765 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6766 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6767 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6771 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6772 parentStorage
, parentStorage
->storageDirEntry
);
6776 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6778 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6779 (*result
)->transactedParent
= parentStorage
;
6783 IStorage_Release(scratch
);
6786 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6789 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6794 return E_OUTOFMEMORY
;
6797 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6798 BOOL toplevel
, StorageBaseImpl
** result
)
6800 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6802 if (parentStorage
->openFlags
& fixme_flags
)
6804 fixme_flags
&= ~parentStorage
->openFlags
;
6805 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6808 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6809 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6810 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6812 /* Need to create a temp file for the snapshot */
6813 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6816 return TransactedSnapshotImpl_Construct(parentStorage
,
6817 (TransactedSnapshotImpl
**)result
);
6820 static HRESULT
Storage_Construct(
6828 StorageBaseImpl
** result
)
6830 StorageImpl
*newStorage
;
6831 StorageBaseImpl
*newTransactedStorage
;
6834 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6835 if (FAILED(hr
)) goto end
;
6837 if (openFlags
& STGM_TRANSACTED
)
6839 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6841 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6843 *result
= newTransactedStorage
;
6846 *result
= &newStorage
->base
;
6853 /************************************************************************
6854 * StorageUtl helper functions
6855 ***********************************************************************/
6857 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6861 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6862 *value
= lendian16toh(tmp
);
6865 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6867 value
= htole16(value
);
6868 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6871 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6875 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6876 *value
= lendian32toh(tmp
);
6879 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6881 value
= htole32(value
);
6882 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6885 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6886 ULARGE_INTEGER
* value
)
6888 #ifdef WORDS_BIGENDIAN
6891 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6892 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6893 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6895 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6899 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6900 const ULARGE_INTEGER
*value
)
6902 #ifdef WORDS_BIGENDIAN
6905 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6906 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6907 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6909 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6913 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6915 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6916 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6917 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6919 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6922 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6924 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6925 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6926 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6928 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6931 void StorageUtl_CopyDirEntryToSTATSTG(
6932 StorageBaseImpl
* storage
,
6933 STATSTG
* destination
,
6934 const DirEntry
* source
,
6938 * The copy of the string occurs only when the flag is not set
6940 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6942 /* Use the filename for the root storage. */
6943 destination
->pwcsName
= 0;
6944 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6946 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6947 (source
->name
[0] == 0) )
6949 destination
->pwcsName
= 0;
6953 destination
->pwcsName
=
6954 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6956 strcpyW(destination
->pwcsName
, source
->name
);
6959 switch (source
->stgType
)
6963 destination
->type
= STGTY_STORAGE
;
6966 destination
->type
= STGTY_STREAM
;
6969 destination
->type
= STGTY_STREAM
;
6973 destination
->cbSize
= source
->size
;
6975 currentReturnStruct->mtime = {0}; TODO
6976 currentReturnStruct->ctime = {0};
6977 currentReturnStruct->atime = {0};
6979 destination
->grfMode
= 0;
6980 destination
->grfLocksSupported
= 0;
6981 destination
->clsid
= source
->clsid
;
6982 destination
->grfStateBits
= 0;
6983 destination
->reserved
= 0;
6987 /************************************************************************
6988 * BlockChainStream implementation
6989 ***********************************************************************/
6991 /******************************************************************************
6992 * BlockChainStream_GetHeadOfChain
6994 * Returns the head of this stream chain.
6995 * Some special chains don't have directory entries, their heads are kept in
6996 * This->headOfStreamPlaceHolder.
6999 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7001 DirEntry chainEntry
;
7004 if (This
->headOfStreamPlaceHolder
!= 0)
7005 return *(This
->headOfStreamPlaceHolder
);
7007 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7009 hr
= StorageImpl_ReadDirEntry(
7010 This
->parentStorage
,
7011 This
->ownerDirEntry
,
7014 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7015 return chainEntry
.startingBlock
;
7018 return BLOCK_END_OF_CHAIN
;
7021 /* Read and save the index of all blocks in this stream. */
7022 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
7024 ULONG next_sector
, next_offset
;
7026 struct BlockChainRun
*last_run
;
7028 if (This
->indexCacheLen
== 0)
7032 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7036 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7037 next_offset
= last_run
->lastOffset
+1;
7038 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7039 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7041 if (FAILED(hr
)) return hr
;
7044 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7046 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7048 /* Add the current block to the cache. */
7049 if (This
->indexCacheSize
== 0)
7051 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7052 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7053 This
->indexCacheSize
= 16;
7055 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7057 struct BlockChainRun
*new_cache
;
7060 new_size
= This
->indexCacheSize
* 2;
7061 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7062 if (!new_cache
) return E_OUTOFMEMORY
;
7063 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7065 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7066 This
->indexCache
= new_cache
;
7067 This
->indexCacheSize
= new_size
;
7070 This
->indexCacheLen
++;
7071 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7072 last_run
->firstSector
= next_sector
;
7073 last_run
->firstOffset
= next_offset
;
7076 last_run
->lastOffset
= next_offset
;
7078 /* Find the next block. */
7080 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7081 if (FAILED(hr
)) return hr
;
7084 if (This
->indexCacheLen
)
7086 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7087 This
->numBlocks
= last_run
->lastOffset
+1;
7091 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7092 This
->numBlocks
= 0;
7098 /* Locate the nth block in this stream. */
7099 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7101 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7102 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7104 if (offset
>= This
->numBlocks
)
7105 return BLOCK_END_OF_CHAIN
;
7107 while (min_run
< max_run
)
7109 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7110 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7112 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7113 max_run
= run_to_check
-1;
7115 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7117 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7118 min_run
= run_to_check
+1;
7121 /* Block is in this run. */
7122 min_run
= max_run
= run_to_check
;
7125 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7128 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7129 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7131 BlockChainBlock
*result
=NULL
;
7135 if (This
->cachedBlocks
[i
].index
== index
)
7137 *sector
= This
->cachedBlocks
[i
].sector
;
7138 *block
= &This
->cachedBlocks
[i
];
7142 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7143 if (*sector
== BLOCK_END_OF_CHAIN
)
7144 return STG_E_DOCFILECORRUPT
;
7148 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7149 result
= &This
->cachedBlocks
[0];
7150 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7151 result
= &This
->cachedBlocks
[1];
7154 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7155 if (This
->blockToEvict
== 2)
7156 This
->blockToEvict
= 0;
7161 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7162 return STG_E_WRITEFAULT
;
7163 result
->dirty
= FALSE
;
7166 result
->read
= FALSE
;
7167 result
->index
= index
;
7168 result
->sector
= *sector
;
7175 BlockChainStream
* BlockChainStream_Construct(
7176 StorageImpl
* parentStorage
,
7177 ULONG
* headOfStreamPlaceHolder
,
7180 BlockChainStream
* newStream
;
7182 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7184 newStream
->parentStorage
= parentStorage
;
7185 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7186 newStream
->ownerDirEntry
= dirEntry
;
7187 newStream
->indexCache
= NULL
;
7188 newStream
->indexCacheLen
= 0;
7189 newStream
->indexCacheSize
= 0;
7190 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7191 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7192 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7193 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7194 newStream
->blockToEvict
= 0;
7196 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7198 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7199 HeapFree(GetProcessHeap(), 0, newStream
);
7206 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7209 if (!This
) return S_OK
;
7212 if (This
->cachedBlocks
[i
].dirty
)
7214 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7215 This
->cachedBlocks
[i
].dirty
= FALSE
;
7217 return STG_E_WRITEFAULT
;
7223 void BlockChainStream_Destroy(BlockChainStream
* This
)
7227 BlockChainStream_Flush(This
);
7228 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7230 HeapFree(GetProcessHeap(), 0, This
);
7233 /******************************************************************************
7234 * BlockChainStream_Shrink
7236 * Shrinks this chain in the big block depot.
7238 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7239 ULARGE_INTEGER newSize
)
7246 * Figure out how many blocks are needed to contain the new size
7248 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7250 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7256 * Go to the new end of chain
7258 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7260 /* Mark the new end of chain */
7261 StorageImpl_SetNextBlockInChain(
7262 This
->parentStorage
,
7264 BLOCK_END_OF_CHAIN
);
7266 This
->tailIndex
= blockIndex
;
7270 if (This
->headOfStreamPlaceHolder
!= 0)
7272 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7276 DirEntry chainEntry
;
7277 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7279 StorageImpl_ReadDirEntry(
7280 This
->parentStorage
,
7281 This
->ownerDirEntry
,
7284 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7286 StorageImpl_WriteDirEntry(
7287 This
->parentStorage
,
7288 This
->ownerDirEntry
,
7292 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7295 This
->numBlocks
= numBlocks
;
7298 * Mark the extra blocks as free
7300 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7302 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7303 StorageImpl_FreeBigBlock(This
->parentStorage
,
7304 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7305 if (last_run
->lastOffset
== last_run
->firstOffset
)
7306 This
->indexCacheLen
--;
7308 last_run
->lastOffset
--;
7312 * Reset the last accessed block cache.
7316 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7318 This
->cachedBlocks
[i
].index
= 0xffffffff;
7319 This
->cachedBlocks
[i
].dirty
= FALSE
;
7326 /******************************************************************************
7327 * BlockChainStream_Enlarge
7329 * Grows this chain in the big block depot.
7331 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7332 ULARGE_INTEGER newSize
)
7334 ULONG blockIndex
, currentBlock
;
7336 ULONG oldNumBlocks
= 0;
7338 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7341 * Empty chain. Create the head.
7343 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7345 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7346 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7348 BLOCK_END_OF_CHAIN
);
7350 if (This
->headOfStreamPlaceHolder
!= 0)
7352 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7356 DirEntry chainEntry
;
7357 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7359 StorageImpl_ReadDirEntry(
7360 This
->parentStorage
,
7361 This
->ownerDirEntry
,
7364 chainEntry
.startingBlock
= blockIndex
;
7366 StorageImpl_WriteDirEntry(
7367 This
->parentStorage
,
7368 This
->ownerDirEntry
,
7372 This
->tailIndex
= blockIndex
;
7373 This
->numBlocks
= 1;
7377 * Figure out how many blocks are needed to contain this stream
7379 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7381 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7385 * Go to the current end of chain
7387 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7389 currentBlock
= blockIndex
;
7391 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7394 currentBlock
= blockIndex
;
7396 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7401 This
->tailIndex
= currentBlock
;
7404 currentBlock
= This
->tailIndex
;
7405 oldNumBlocks
= This
->numBlocks
;
7408 * Add new blocks to the chain
7410 if (oldNumBlocks
< newNumBlocks
)
7412 while (oldNumBlocks
< newNumBlocks
)
7414 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7416 StorageImpl_SetNextBlockInChain(
7417 This
->parentStorage
,
7421 StorageImpl_SetNextBlockInChain(
7422 This
->parentStorage
,
7424 BLOCK_END_OF_CHAIN
);
7426 currentBlock
= blockIndex
;
7430 This
->tailIndex
= blockIndex
;
7431 This
->numBlocks
= newNumBlocks
;
7434 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7441 /******************************************************************************
7442 * BlockChainStream_GetSize
7444 * Returns the size of this chain.
7445 * Will return the block count if this chain doesn't have a directory entry.
7447 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7449 DirEntry chainEntry
;
7451 if(This
->headOfStreamPlaceHolder
== NULL
)
7454 * This chain has a directory entry so use the size value from there.
7456 StorageImpl_ReadDirEntry(
7457 This
->parentStorage
,
7458 This
->ownerDirEntry
,
7461 return chainEntry
.size
;
7466 * this chain is a chain that does not have a directory entry, figure out the
7467 * size by making the product number of used blocks times the
7470 ULARGE_INTEGER result
;
7472 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7473 This
->parentStorage
->bigBlockSize
;
7479 /******************************************************************************
7480 * BlockChainStream_SetSize
7482 * Sets the size of this stream. The big block depot will be updated.
7483 * The file will grow if we grow the chain.
7485 * TODO: Free the actual blocks in the file when we shrink the chain.
7486 * Currently, the blocks are still in the file. So the file size
7487 * doesn't shrink even if we shrink streams.
7489 BOOL
BlockChainStream_SetSize(
7490 BlockChainStream
* This
,
7491 ULARGE_INTEGER newSize
)
7493 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7495 if (newSize
.QuadPart
== size
.QuadPart
)
7498 if (newSize
.QuadPart
< size
.QuadPart
)
7500 BlockChainStream_Shrink(This
, newSize
);
7504 BlockChainStream_Enlarge(This
, newSize
);
7510 /******************************************************************************
7511 * BlockChainStream_ReadAt
7513 * Reads a specified number of bytes from this chain at the specified offset.
7514 * bytesRead may be NULL.
7515 * Failure will be returned if the specified number of bytes has not been read.
7517 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7518 ULARGE_INTEGER offset
,
7523 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7524 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7525 ULONG bytesToReadInBuffer
;
7528 ULARGE_INTEGER stream_size
;
7530 BlockChainBlock
*cachedBlock
;
7532 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7535 * Find the first block in the stream that contains part of the buffer.
7537 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7541 stream_size
= BlockChainStream_GetSize(This
);
7542 if (stream_size
.QuadPart
> offset
.QuadPart
)
7543 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7548 * Start reading the buffer.
7550 bufferWalker
= buffer
;
7554 ULARGE_INTEGER ulOffset
;
7558 * Calculate how many bytes we can copy from this big block.
7560 bytesToReadInBuffer
=
7561 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7563 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7570 /* Not in cache, and we're going to read past the end of the block. */
7571 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7574 StorageImpl_ReadAt(This
->parentStorage
,
7577 bytesToReadInBuffer
,
7582 if (!cachedBlock
->read
)
7585 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7586 return STG_E_READFAULT
;
7588 cachedBlock
->read
= TRUE
;
7591 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7592 bytesReadAt
= bytesToReadInBuffer
;
7595 blockNoInSequence
++;
7596 bufferWalker
+= bytesReadAt
;
7597 size
-= bytesReadAt
;
7598 *bytesRead
+= bytesReadAt
;
7599 offsetInBlock
= 0; /* There is no offset on the next block */
7601 if (bytesToReadInBuffer
!= bytesReadAt
)
7608 /******************************************************************************
7609 * BlockChainStream_WriteAt
7611 * Writes the specified number of bytes to this chain at the specified offset.
7612 * Will fail if not all specified number of bytes have been written.
7614 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7615 ULARGE_INTEGER offset
,
7618 ULONG
* bytesWritten
)
7620 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7621 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7624 const BYTE
* bufferWalker
;
7626 BlockChainBlock
*cachedBlock
;
7629 bufferWalker
= buffer
;
7633 ULARGE_INTEGER ulOffset
;
7634 DWORD bytesWrittenAt
;
7637 * Calculate how many bytes we can copy to this big block.
7640 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7642 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7644 /* BlockChainStream_SetSize should have already been called to ensure we have
7645 * enough blocks in the chain to write into */
7648 ERR("not enough blocks in chain to write data\n");
7654 /* Not in cache, and we're going to write past the end of the block. */
7655 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7658 StorageImpl_WriteAt(This
->parentStorage
,
7666 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7669 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7670 return STG_E_READFAULT
;
7673 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7674 bytesWrittenAt
= bytesToWrite
;
7675 cachedBlock
->read
= TRUE
;
7676 cachedBlock
->dirty
= TRUE
;
7679 blockNoInSequence
++;
7680 bufferWalker
+= bytesWrittenAt
;
7681 size
-= bytesWrittenAt
;
7682 *bytesWritten
+= bytesWrittenAt
;
7683 offsetInBlock
= 0; /* There is no offset on the next block */
7685 if (bytesWrittenAt
!= bytesToWrite
)
7689 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7693 /************************************************************************
7694 * SmallBlockChainStream implementation
7695 ***********************************************************************/
7697 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7698 StorageImpl
* parentStorage
,
7699 ULONG
* headOfStreamPlaceHolder
,
7702 SmallBlockChainStream
* newStream
;
7704 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7706 newStream
->parentStorage
= parentStorage
;
7707 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7708 newStream
->ownerDirEntry
= dirEntry
;
7713 void SmallBlockChainStream_Destroy(
7714 SmallBlockChainStream
* This
)
7716 HeapFree(GetProcessHeap(), 0, This
);
7719 /******************************************************************************
7720 * SmallBlockChainStream_GetHeadOfChain
7722 * Returns the head of this chain of small blocks.
7724 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7725 SmallBlockChainStream
* This
)
7727 DirEntry chainEntry
;
7730 if (This
->headOfStreamPlaceHolder
!= NULL
)
7731 return *(This
->headOfStreamPlaceHolder
);
7733 if (This
->ownerDirEntry
)
7735 hr
= StorageImpl_ReadDirEntry(
7736 This
->parentStorage
,
7737 This
->ownerDirEntry
,
7740 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7741 return chainEntry
.startingBlock
;
7744 return BLOCK_END_OF_CHAIN
;
7747 /******************************************************************************
7748 * SmallBlockChainStream_GetNextBlockInChain
7750 * Returns the index of the next small block in this chain.
7753 * - BLOCK_END_OF_CHAIN: end of this chain
7754 * - BLOCK_UNUSED: small block 'blockIndex' is free
7756 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7757 SmallBlockChainStream
* This
,
7759 ULONG
* nextBlockInChain
)
7761 ULARGE_INTEGER offsetOfBlockInDepot
;
7766 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7768 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7771 * Read those bytes in the buffer from the small block file.
7773 res
= BlockChainStream_ReadAt(
7774 This
->parentStorage
->smallBlockDepotChain
,
7775 offsetOfBlockInDepot
,
7780 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7781 res
= STG_E_READFAULT
;
7785 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7792 /******************************************************************************
7793 * SmallBlockChainStream_SetNextBlockInChain
7795 * Writes the index of the next block of the specified block in the small
7797 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7798 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7800 static void SmallBlockChainStream_SetNextBlockInChain(
7801 SmallBlockChainStream
* This
,
7805 ULARGE_INTEGER offsetOfBlockInDepot
;
7809 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7811 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7814 * Read those bytes in the buffer from the small block file.
7816 BlockChainStream_WriteAt(
7817 This
->parentStorage
->smallBlockDepotChain
,
7818 offsetOfBlockInDepot
,
7824 /******************************************************************************
7825 * SmallBlockChainStream_FreeBlock
7827 * Flag small block 'blockIndex' as free in the small block depot.
7829 static void SmallBlockChainStream_FreeBlock(
7830 SmallBlockChainStream
* This
,
7833 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7836 /******************************************************************************
7837 * SmallBlockChainStream_GetNextFreeBlock
7839 * Returns the index of a free small block. The small block depot will be
7840 * enlarged if necessary. The small block chain will also be enlarged if
7843 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7844 SmallBlockChainStream
* This
)
7846 ULARGE_INTEGER offsetOfBlockInDepot
;
7849 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7850 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7852 ULONG smallBlocksPerBigBlock
;
7854 ULONG blocksRequired
;
7855 ULARGE_INTEGER old_size
, size_required
;
7857 offsetOfBlockInDepot
.u
.HighPart
= 0;
7860 * Scan the small block depot for a free block
7862 while (nextBlockIndex
!= BLOCK_UNUSED
)
7864 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7866 res
= BlockChainStream_ReadAt(
7867 This
->parentStorage
->smallBlockDepotChain
,
7868 offsetOfBlockInDepot
,
7874 * If we run out of space for the small block depot, enlarge it
7876 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7878 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7880 if (nextBlockIndex
!= BLOCK_UNUSED
)
7886 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7888 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7889 ULARGE_INTEGER newSize
, offset
;
7892 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7893 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7896 * Initialize all the small blocks to free
7898 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7899 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7900 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7901 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7903 StorageImpl_SaveFileHeader(This
->parentStorage
);
7907 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7909 smallBlocksPerBigBlock
=
7910 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7913 * Verify if we have to allocate big blocks to contain small blocks
7915 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7917 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7919 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7921 if (size_required
.QuadPart
> old_size
.QuadPart
)
7923 BlockChainStream_SetSize(
7924 This
->parentStorage
->smallBlockRootChain
,
7927 StorageImpl_ReadDirEntry(
7928 This
->parentStorage
,
7929 This
->parentStorage
->base
.storageDirEntry
,
7932 rootEntry
.size
= size_required
;
7934 StorageImpl_WriteDirEntry(
7935 This
->parentStorage
,
7936 This
->parentStorage
->base
.storageDirEntry
,
7943 /******************************************************************************
7944 * SmallBlockChainStream_ReadAt
7946 * Reads a specified number of bytes from this chain at the specified offset.
7947 * bytesRead may be NULL.
7948 * Failure will be returned if the specified number of bytes has not been read.
7950 HRESULT
SmallBlockChainStream_ReadAt(
7951 SmallBlockChainStream
* This
,
7952 ULARGE_INTEGER offset
,
7958 ULARGE_INTEGER offsetInBigBlockFile
;
7959 ULONG blockNoInSequence
=
7960 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7962 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7963 ULONG bytesToReadInBuffer
;
7965 ULONG bytesReadFromBigBlockFile
;
7967 ULARGE_INTEGER stream_size
;
7970 * This should never happen on a small block file.
7972 assert(offset
.u
.HighPart
==0);
7976 stream_size
= SmallBlockChainStream_GetSize(This
);
7977 if (stream_size
.QuadPart
> offset
.QuadPart
)
7978 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7983 * Find the first block in the stream that contains part of the buffer.
7985 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7987 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7989 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7992 blockNoInSequence
--;
7996 * Start reading the buffer.
7998 bufferWalker
= buffer
;
8000 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8003 * Calculate how many bytes we can copy from this small block.
8005 bytesToReadInBuffer
=
8006 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8009 * Calculate the offset of the small block in the small block file.
8011 offsetInBigBlockFile
.QuadPart
=
8012 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8014 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8017 * Read those bytes in the buffer from the small block file.
8018 * The small block has already been identified so it shouldn't fail
8019 * unless the file is corrupt.
8021 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
8022 offsetInBigBlockFile
,
8023 bytesToReadInBuffer
,
8025 &bytesReadFromBigBlockFile
);
8030 if (!bytesReadFromBigBlockFile
)
8031 return STG_E_DOCFILECORRUPT
;
8034 * Step to the next big block.
8036 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8038 return STG_E_DOCFILECORRUPT
;
8040 bufferWalker
+= bytesReadFromBigBlockFile
;
8041 size
-= bytesReadFromBigBlockFile
;
8042 *bytesRead
+= bytesReadFromBigBlockFile
;
8043 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8049 /******************************************************************************
8050 * SmallBlockChainStream_WriteAt
8052 * Writes the specified number of bytes to this chain at the specified offset.
8053 * Will fail if not all specified number of bytes have been written.
8055 HRESULT
SmallBlockChainStream_WriteAt(
8056 SmallBlockChainStream
* This
,
8057 ULARGE_INTEGER offset
,
8060 ULONG
* bytesWritten
)
8062 ULARGE_INTEGER offsetInBigBlockFile
;
8063 ULONG blockNoInSequence
=
8064 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8066 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8067 ULONG bytesToWriteInBuffer
;
8069 ULONG bytesWrittenToBigBlockFile
;
8070 const BYTE
* bufferWalker
;
8074 * This should never happen on a small block file.
8076 assert(offset
.u
.HighPart
==0);
8079 * Find the first block in the stream that contains part of the buffer.
8081 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8083 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8085 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8086 return STG_E_DOCFILECORRUPT
;
8087 blockNoInSequence
--;
8091 * Start writing the buffer.
8094 bufferWalker
= buffer
;
8095 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8098 * Calculate how many bytes we can copy to this small block.
8100 bytesToWriteInBuffer
=
8101 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8104 * Calculate the offset of the small block in the small block file.
8106 offsetInBigBlockFile
.QuadPart
=
8107 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8109 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8112 * Write those bytes in the buffer to the small block file.
8114 res
= BlockChainStream_WriteAt(
8115 This
->parentStorage
->smallBlockRootChain
,
8116 offsetInBigBlockFile
,
8117 bytesToWriteInBuffer
,
8119 &bytesWrittenToBigBlockFile
);
8124 * Step to the next big block.
8126 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8129 bufferWalker
+= bytesWrittenToBigBlockFile
;
8130 size
-= bytesWrittenToBigBlockFile
;
8131 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8132 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8135 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8138 /******************************************************************************
8139 * SmallBlockChainStream_Shrink
8141 * Shrinks this chain in the small block depot.
8143 static BOOL
SmallBlockChainStream_Shrink(
8144 SmallBlockChainStream
* This
,
8145 ULARGE_INTEGER newSize
)
8147 ULONG blockIndex
, extraBlock
;
8151 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8153 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8156 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8159 * Go to the new end of chain
8161 while (count
< numBlocks
)
8163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8170 * If the count is 0, we have a special case, the head of the chain was
8175 DirEntry chainEntry
;
8177 StorageImpl_ReadDirEntry(This
->parentStorage
,
8178 This
->ownerDirEntry
,
8181 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8183 StorageImpl_WriteDirEntry(This
->parentStorage
,
8184 This
->ownerDirEntry
,
8188 * We start freeing the chain at the head block.
8190 extraBlock
= blockIndex
;
8194 /* Get the next block before marking the new end */
8195 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8199 /* Mark the new end of chain */
8200 SmallBlockChainStream_SetNextBlockInChain(
8203 BLOCK_END_OF_CHAIN
);
8207 * Mark the extra blocks as free
8209 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8214 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8215 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8216 extraBlock
= blockIndex
;
8222 /******************************************************************************
8223 * SmallBlockChainStream_Enlarge
8225 * Grows this chain in the small block depot.
8227 static BOOL
SmallBlockChainStream_Enlarge(
8228 SmallBlockChainStream
* This
,
8229 ULARGE_INTEGER newSize
)
8231 ULONG blockIndex
, currentBlock
;
8233 ULONG oldNumBlocks
= 0;
8235 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8238 * Empty chain. Create the head.
8240 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8242 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8243 SmallBlockChainStream_SetNextBlockInChain(
8246 BLOCK_END_OF_CHAIN
);
8248 if (This
->headOfStreamPlaceHolder
!= NULL
)
8250 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8254 DirEntry chainEntry
;
8256 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8259 chainEntry
.startingBlock
= blockIndex
;
8261 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8266 currentBlock
= blockIndex
;
8269 * Figure out how many blocks are needed to contain this stream
8271 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8273 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8277 * Go to the current end of chain
8279 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8282 currentBlock
= blockIndex
;
8283 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8288 * Add new blocks to the chain
8290 while (oldNumBlocks
< newNumBlocks
)
8292 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8293 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8295 SmallBlockChainStream_SetNextBlockInChain(
8298 BLOCK_END_OF_CHAIN
);
8300 currentBlock
= blockIndex
;
8307 /******************************************************************************
8308 * SmallBlockChainStream_SetSize
8310 * Sets the size of this stream.
8311 * The file will grow if we grow the chain.
8313 * TODO: Free the actual blocks in the file when we shrink the chain.
8314 * Currently, the blocks are still in the file. So the file size
8315 * doesn't shrink even if we shrink streams.
8317 BOOL
SmallBlockChainStream_SetSize(
8318 SmallBlockChainStream
* This
,
8319 ULARGE_INTEGER newSize
)
8321 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8323 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8326 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8328 SmallBlockChainStream_Shrink(This
, newSize
);
8332 SmallBlockChainStream_Enlarge(This
, newSize
);
8338 /******************************************************************************
8339 * SmallBlockChainStream_GetCount
8341 * Returns the number of small blocks that comprises this chain.
8342 * This is not the size of the stream as the last block may not be full!
8345 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8350 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8352 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8356 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8357 blockIndex
, &blockIndex
)))
8364 /******************************************************************************
8365 * SmallBlockChainStream_GetSize
8367 * Returns the size of this chain.
8369 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8371 DirEntry chainEntry
;
8373 if(This
->headOfStreamPlaceHolder
!= NULL
)
8375 ULARGE_INTEGER result
;
8376 result
.u
.HighPart
= 0;
8378 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8379 This
->parentStorage
->smallBlockSize
;
8384 StorageImpl_ReadDirEntry(
8385 This
->parentStorage
,
8386 This
->ownerDirEntry
,
8389 return chainEntry
.size
;
8393 /************************************************************************
8394 * Miscellaneous storage functions
8395 ***********************************************************************/
8397 static HRESULT
create_storagefile(
8401 STGOPTIONS
* pStgOptions
,
8405 StorageBaseImpl
* newStorage
= 0;
8406 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8407 HRESULT hr
= STG_E_INVALIDFLAG
;
8411 DWORD fileAttributes
;
8412 WCHAR tempFileName
[MAX_PATH
];
8415 return STG_E_INVALIDPOINTER
;
8417 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8418 return STG_E_INVALIDPARAMETER
;
8420 /* if no share mode given then DENY_NONE is the default */
8421 if (STGM_SHARE_MODE(grfMode
) == 0)
8422 grfMode
|= STGM_SHARE_DENY_NONE
;
8424 if ( FAILED( validateSTGM(grfMode
) ))
8427 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8428 switch(STGM_ACCESS_MODE(grfMode
))
8431 case STGM_READWRITE
:
8437 /* in direct mode, can only use SHARE_EXCLUSIVE */
8438 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8441 /* but in transacted mode, any share mode is valid */
8444 * Generate a unique name.
8448 WCHAR tempPath
[MAX_PATH
];
8449 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8451 memset(tempPath
, 0, sizeof(tempPath
));
8452 memset(tempFileName
, 0, sizeof(tempFileName
));
8454 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8457 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8458 pwcsName
= tempFileName
;
8461 hr
= STG_E_INSUFFICIENTMEMORY
;
8465 creationMode
= TRUNCATE_EXISTING
;
8469 creationMode
= GetCreationModeFromSTGM(grfMode
);
8473 * Interpret the STGM value grfMode
8475 shareMode
= GetShareModeFromSTGM(grfMode
);
8476 accessMode
= GetAccessModeFromSTGM(grfMode
);
8478 if (grfMode
& STGM_DELETEONRELEASE
)
8479 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8481 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8485 hFile
= CreateFileW(pwcsName
,
8493 if (hFile
== INVALID_HANDLE_VALUE
)
8495 if(GetLastError() == ERROR_FILE_EXISTS
)
8496 hr
= STG_E_FILEALREADYEXISTS
;
8503 * Allocate and initialize the new IStorage object.
8505 hr
= Storage_Construct(
8512 pStgOptions
->ulSectorSize
,
8520 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8521 IStorage_Release(&newStorage
->IStorage_iface
);
8524 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8529 /******************************************************************************
8530 * StgCreateDocfile [OLE32.@]
8531 * Creates a new compound file storage object
8534 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8535 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8536 * reserved [ ?] unused?, usually 0
8537 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8540 * S_OK if the file was successfully created
8541 * some STG_E_ value if error
8543 * if pwcsName is NULL, create file with new unique name
8544 * the function can returns
8545 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8548 HRESULT WINAPI
StgCreateDocfile(
8552 IStorage
**ppstgOpen
)
8554 STGOPTIONS stgoptions
= {1, 0, 512};
8556 TRACE("(%s, %x, %d, %p)\n",
8557 debugstr_w(pwcsName
), grfMode
,
8558 reserved
, ppstgOpen
);
8561 return STG_E_INVALIDPOINTER
;
8563 return STG_E_INVALIDPARAMETER
;
8565 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8568 /******************************************************************************
8569 * StgCreateStorageEx [OLE32.@]
8571 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8573 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8574 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8576 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8578 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8579 return STG_E_INVALIDPARAMETER
;
8582 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8584 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8585 return STG_E_INVALIDPARAMETER
;
8588 if (stgfmt
== STGFMT_FILE
)
8590 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8591 return STG_E_INVALIDPARAMETER
;
8594 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8596 STGOPTIONS defaultOptions
= {1, 0, 512};
8598 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8599 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8603 ERR("Invalid stgfmt argument\n");
8604 return STG_E_INVALIDPARAMETER
;
8607 /******************************************************************************
8608 * StgCreatePropSetStg [OLE32.@]
8610 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8611 IPropertySetStorage
**propset
)
8613 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8615 return STG_E_INVALIDPARAMETER
;
8617 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8620 /******************************************************************************
8621 * StgOpenStorageEx [OLE32.@]
8623 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8625 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8626 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8628 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8630 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8631 return STG_E_INVALIDPARAMETER
;
8637 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8638 return STG_E_INVALIDPARAMETER
;
8640 case STGFMT_STORAGE
:
8643 case STGFMT_DOCFILE
:
8644 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8646 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8647 return STG_E_INVALIDPARAMETER
;
8649 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8653 WARN("STGFMT_ANY assuming storage\n");
8657 return STG_E_INVALIDPARAMETER
;
8660 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8664 /******************************************************************************
8665 * StgOpenStorage [OLE32.@]
8667 HRESULT WINAPI
StgOpenStorage(
8668 const OLECHAR
*pwcsName
,
8669 IStorage
*pstgPriority
,
8673 IStorage
**ppstgOpen
)
8675 StorageBaseImpl
* newStorage
= 0;
8680 LPWSTR temp_name
= NULL
;
8682 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8683 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8684 snbExclude
, reserved
, ppstgOpen
);
8688 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8689 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8690 if (FAILED(hr
)) goto end
;
8691 pwcsName
= temp_name
;
8692 TRACE("using filename %s\n", debugstr_w(temp_name
));
8697 hr
= STG_E_INVALIDNAME
;
8703 hr
= STG_E_INVALIDPOINTER
;
8709 hr
= STG_E_INVALIDPARAMETER
;
8713 if (grfMode
& STGM_PRIORITY
)
8715 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8716 return STG_E_INVALIDFLAG
;
8717 if (grfMode
& STGM_DELETEONRELEASE
)
8718 return STG_E_INVALIDFUNCTION
;
8719 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8720 return STG_E_INVALIDFLAG
;
8721 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8722 grfMode
|= STGM_SHARE_DENY_NONE
;
8726 * Validate the sharing mode
8728 if (grfMode
& STGM_DIRECT_SWMR
)
8730 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8731 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8733 hr
= STG_E_INVALIDFLAG
;
8737 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8738 switch(STGM_SHARE_MODE(grfMode
))
8740 case STGM_SHARE_EXCLUSIVE
:
8741 case STGM_SHARE_DENY_WRITE
:
8744 hr
= STG_E_INVALIDFLAG
;
8748 if ( FAILED( validateSTGM(grfMode
) ) ||
8749 (grfMode
&STGM_CREATE
))
8751 hr
= STG_E_INVALIDFLAG
;
8755 /* shared reading requires transacted or single writer mode */
8756 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8757 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8758 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8760 hr
= STG_E_INVALIDFLAG
;
8765 * Interpret the STGM value grfMode
8767 shareMode
= GetShareModeFromSTGM(grfMode
);
8768 accessMode
= GetAccessModeFromSTGM(grfMode
);
8772 hFile
= CreateFileW( pwcsName
,
8777 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8780 if (hFile
==INVALID_HANDLE_VALUE
)
8782 DWORD last_error
= GetLastError();
8788 case ERROR_FILE_NOT_FOUND
:
8789 hr
= STG_E_FILENOTFOUND
;
8792 case ERROR_PATH_NOT_FOUND
:
8793 hr
= STG_E_PATHNOTFOUND
;
8796 case ERROR_ACCESS_DENIED
:
8797 case ERROR_WRITE_PROTECT
:
8798 hr
= STG_E_ACCESSDENIED
;
8801 case ERROR_SHARING_VIOLATION
:
8802 hr
= STG_E_SHAREVIOLATION
;
8813 * Refuse to open the file if it's too small to be a structured storage file
8814 * FIXME: verify the file when reading instead of here
8816 if (GetFileSize(hFile
, NULL
) < 0x100)
8819 hr
= STG_E_FILEALREADYEXISTS
;
8824 * Allocate and initialize the new IStorage object.
8826 hr
= Storage_Construct(
8839 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8841 if(hr
== STG_E_INVALIDHEADER
)
8842 hr
= STG_E_FILEALREADYEXISTS
;
8846 *ppstgOpen
= &newStorage
->IStorage_iface
;
8849 CoTaskMemFree(temp_name
);
8850 if (pstgPriority
) IStorage_Release(pstgPriority
);
8851 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8855 /******************************************************************************
8856 * StgCreateDocfileOnILockBytes [OLE32.@]
8858 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8862 IStorage
** ppstgOpen
)
8864 StorageBaseImpl
* newStorage
= 0;
8867 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8868 return STG_E_INVALIDPOINTER
;
8871 * Allocate and initialize the new IStorage object.
8873 hr
= Storage_Construct(
8888 *ppstgOpen
= &newStorage
->IStorage_iface
;
8893 /******************************************************************************
8894 * StgOpenStorageOnILockBytes [OLE32.@]
8896 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8898 IStorage
*pstgPriority
,
8902 IStorage
**ppstgOpen
)
8904 StorageBaseImpl
* newStorage
= 0;
8907 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8908 return STG_E_INVALIDPOINTER
;
8910 if ( FAILED( validateSTGM(grfMode
) ))
8911 return STG_E_INVALIDFLAG
;
8916 * Allocate and initialize the new IStorage object.
8918 hr
= Storage_Construct(
8933 *ppstgOpen
= &newStorage
->IStorage_iface
;
8938 /******************************************************************************
8939 * StgSetTimes [ole32.@]
8940 * StgSetTimes [OLE32.@]
8944 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8945 FILETIME
const *patime
, FILETIME
const *pmtime
)
8947 IStorage
*stg
= NULL
;
8950 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8952 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8956 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8957 IStorage_Release(stg
);
8963 /******************************************************************************
8964 * StgIsStorageILockBytes [OLE32.@]
8966 * Determines if the ILockBytes contains a storage object.
8968 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8970 BYTE sig
[sizeof(STORAGE_magic
)];
8971 ULARGE_INTEGER offset
;
8974 offset
.u
.HighPart
= 0;
8975 offset
.u
.LowPart
= 0;
8977 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8979 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8985 /******************************************************************************
8986 * WriteClassStg [OLE32.@]
8988 * This method will store the specified CLSID in the specified storage object
8990 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8993 return E_INVALIDARG
;
8996 return STG_E_INVALIDPOINTER
;
8998 return IStorage_SetClass(pStg
, rclsid
);
9001 /***********************************************************************
9002 * ReadClassStg (OLE32.@)
9004 * This method reads the CLSID previously written to a storage object with
9005 * the WriteClassStg.
9008 * pstg [I] IStorage pointer
9009 * pclsid [O] Pointer to where the CLSID is written
9013 * Failure: HRESULT code.
9015 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
9020 TRACE("(%p, %p)\n", pstg
, pclsid
);
9022 if(!pstg
|| !pclsid
)
9023 return E_INVALIDARG
;
9026 * read a STATSTG structure (contains the clsid) from the storage
9028 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9031 *pclsid
=pstatstg
.clsid
;
9036 /***********************************************************************
9037 * OleLoadFromStream (OLE32.@)
9039 * This function loads an object from stream
9041 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9045 LPPERSISTSTREAM xstm
;
9047 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9049 res
=ReadClassStm(pStm
,&clsid
);
9052 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9055 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9057 IUnknown_Release((IUnknown
*)*ppvObj
);
9060 res
=IPersistStream_Load(xstm
,pStm
);
9061 IPersistStream_Release(xstm
);
9062 /* FIXME: all refcounts ok at this point? I think they should be:
9065 * xstm : 0 (released)
9070 /***********************************************************************
9071 * OleSaveToStream (OLE32.@)
9073 * This function saves an object with the IPersistStream interface on it
9074 * to the specified stream.
9076 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9082 TRACE("(%p,%p)\n",pPStm
,pStm
);
9084 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9086 if (SUCCEEDED(res
)){
9088 res
=WriteClassStm(pStm
,&clsid
);
9092 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9095 TRACE("Finished Save\n");
9099 /*************************************************************************
9100 * STORAGE_CreateOleStream [Internal]
9102 * Creates the "\001OLE" stream in the IStorage if necessary.
9105 * storage [I] Dest storage to create the stream in
9106 * flags [I] flags to be set for newly created stream
9109 * HRESULT return value
9113 * This stream is still unknown, MS Word seems to have extra data
9114 * but since the data is stored in the OLESTREAM there should be
9115 * no need to recreate the stream. If the stream is manually
9116 * deleted it will create it with this default data.
9119 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9121 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9122 static const DWORD version_magic
= 0x02000001;
9126 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9129 struct empty_1ole_stream
{
9130 DWORD version_magic
;
9132 DWORD update_options
;
9134 DWORD mon_stream_size
;
9136 struct empty_1ole_stream stream_data
;
9138 stream_data
.version_magic
= version_magic
;
9139 stream_data
.flags
= flags
;
9140 stream_data
.update_options
= 0;
9141 stream_data
.reserved
= 0;
9142 stream_data
.mon_stream_size
= 0;
9144 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9145 IStream_Release(stream
);
9151 /* write a string to a stream, preceded by its length */
9152 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9159 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9160 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9165 str
= CoTaskMemAlloc( len
);
9166 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9167 r
= IStream_Write( stm
, str
, len
, NULL
);
9168 CoTaskMemFree( str
);
9172 /* read a string preceded by its length from a stream */
9173 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9176 DWORD len
, count
= 0;
9180 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9183 if( count
!= sizeof(len
) )
9184 return E_OUTOFMEMORY
;
9186 TRACE("%d bytes\n",len
);
9188 str
= CoTaskMemAlloc( len
);
9190 return E_OUTOFMEMORY
;
9192 r
= IStream_Read( stm
, str
, len
, &count
);
9195 CoTaskMemFree( str
);
9200 CoTaskMemFree( str
);
9201 return E_OUTOFMEMORY
;
9204 TRACE("Read string %s\n",debugstr_an(str
,len
));
9206 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9207 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9210 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9213 CoTaskMemFree( str
);
9221 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9222 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9226 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9228 static const BYTE unknown1
[12] =
9229 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9230 0xFF, 0xFF, 0xFF, 0xFF};
9231 static const BYTE unknown2
[16] =
9232 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9235 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9236 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9237 debugstr_w(szProgIDName
));
9239 /* Create a CompObj stream */
9240 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9241 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9245 /* Write CompObj Structure to stream */
9246 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9248 if( SUCCEEDED( r
) )
9249 r
= WriteClassStm( pstm
, clsid
);
9251 if( SUCCEEDED( r
) )
9252 r
= STREAM_WriteString( pstm
, lpszUserType
);
9253 if( SUCCEEDED( r
) )
9254 r
= STREAM_WriteString( pstm
, szClipName
);
9255 if( SUCCEEDED( r
) )
9256 r
= STREAM_WriteString( pstm
, szProgIDName
);
9257 if( SUCCEEDED( r
) )
9258 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9260 IStream_Release( pstm
);
9265 /***********************************************************************
9266 * WriteFmtUserTypeStg (OLE32.@)
9268 HRESULT WINAPI
WriteFmtUserTypeStg(
9269 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9273 WCHAR szwClipName
[0x40];
9275 LPWSTR wstrProgID
= NULL
;
9278 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9280 /* get the clipboard format name */
9283 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9284 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9288 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9290 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9296 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9298 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9300 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9301 cf
? szwClipName
: NULL
, wstrProgID
);
9303 CoTaskMemFree(wstrProgID
);
9309 /******************************************************************************
9310 * ReadFmtUserTypeStg [OLE32.@]
9312 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9316 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9317 unsigned char unknown1
[12];
9318 unsigned char unknown2
[16];
9320 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9323 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9325 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9326 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9329 WARN("Failed to open stream r = %08x\n", r
);
9333 /* read the various parts of the structure */
9334 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9335 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9337 r
= ReadClassStm( stm
, &clsid
);
9341 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9345 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9349 r
= STREAM_ReadString( stm
, &szProgIDName
);
9353 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9354 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9357 /* ok, success... now we just need to store what we found */
9359 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9361 if( lplpszUserType
)
9363 *lplpszUserType
= szCLSIDName
;
9368 CoTaskMemFree( szCLSIDName
);
9369 CoTaskMemFree( szOleTypeName
);
9370 CoTaskMemFree( szProgIDName
);
9371 IStream_Release( stm
);
9376 /******************************************************************************
9377 * StgIsStorageFile [OLE32.@]
9378 * Verify if the file contains a storage object
9384 * S_OK if file has magic bytes as a storage object
9385 * S_FALSE if file is not storage
9388 StgIsStorageFile(LPCOLESTR fn
)
9394 TRACE("%s\n", debugstr_w(fn
));
9395 hf
= CreateFileW(fn
, GENERIC_READ
,
9396 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9397 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9399 if (hf
== INVALID_HANDLE_VALUE
)
9400 return STG_E_FILENOTFOUND
;
9402 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9404 WARN(" unable to read file\n");
9411 if (bytes_read
!= 8) {
9412 TRACE(" too short\n");
9416 if (!memcmp(magic
,STORAGE_magic
,8)) {
9421 TRACE(" -> Invalid header.\n");
9425 /***********************************************************************
9426 * WriteClassStm (OLE32.@)
9428 * Writes a CLSID to a stream.
9431 * pStm [I] Stream to write to.
9432 * rclsid [I] CLSID to write.
9436 * Failure: HRESULT code.
9438 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9440 TRACE("(%p,%p)\n",pStm
,rclsid
);
9442 if (!pStm
|| !rclsid
)
9443 return E_INVALIDARG
;
9445 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9448 /***********************************************************************
9449 * ReadClassStm (OLE32.@)
9451 * Reads a CLSID from a stream.
9454 * pStm [I] Stream to read from.
9455 * rclsid [O] CLSID to read.
9459 * Failure: HRESULT code.
9461 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9466 TRACE("(%p,%p)\n",pStm
,pclsid
);
9468 if (!pStm
|| !pclsid
)
9469 return E_INVALIDARG
;
9471 /* clear the output args */
9472 *pclsid
= CLSID_NULL
;
9474 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9479 if (nbByte
!= sizeof(CLSID
))
9480 return STG_E_READFAULT
;
9486 /************************************************************************
9487 * OleConvert Functions
9488 ***********************************************************************/
9490 #define OLESTREAM_ID 0x501
9491 #define OLESTREAM_MAX_STR_LEN 255
9493 /* OLESTREAM memory structure to use for Get and Put Routines */
9498 DWORD dwOleTypeNameLength
;
9499 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9500 CHAR
*pstrOleObjFileName
;
9501 DWORD dwOleObjFileNameLength
;
9502 DWORD dwMetaFileWidth
;
9503 DWORD dwMetaFileHeight
;
9504 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9507 } OLECONVERT_OLESTREAM_DATA
;
9509 /* CompObj Stream structure */
9512 BYTE byUnknown1
[12];
9514 DWORD dwCLSIDNameLength
;
9515 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9516 DWORD dwOleTypeNameLength
;
9517 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9518 DWORD dwProgIDNameLength
;
9519 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9520 BYTE byUnknown2
[16];
9521 } OLECONVERT_ISTORAGE_COMPOBJ
;
9523 /* Ole Presentation Stream structure */
9526 BYTE byUnknown1
[28];
9531 } OLECONVERT_ISTORAGE_OLEPRES
;
9534 /*************************************************************************
9535 * OLECONVERT_LoadOLE10 [Internal]
9537 * Loads the OLE10 STREAM to memory
9540 * pOleStream [I] The OLESTREAM
9541 * pData [I] Data Structure for the OLESTREAM Data
9545 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9546 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9549 * This function is used by OleConvertOLESTREAMToIStorage only.
9551 * Memory allocated for pData must be freed by the caller
9553 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9556 HRESULT hRes
= S_OK
;
9560 pData
->pData
= NULL
;
9561 pData
->pstrOleObjFileName
= NULL
;
9563 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9566 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9567 if(dwSize
!= sizeof(pData
->dwOleID
))
9569 hRes
= CONVERT10_E_OLESTREAM_GET
;
9571 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9573 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9584 /* Get the TypeID... more info needed for this field */
9585 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9586 if(dwSize
!= sizeof(pData
->dwTypeID
))
9588 hRes
= CONVERT10_E_OLESTREAM_GET
;
9593 if(pData
->dwTypeID
!= 0)
9595 /* Get the length of the OleTypeName */
9596 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9597 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9599 hRes
= CONVERT10_E_OLESTREAM_GET
;
9604 if(pData
->dwOleTypeNameLength
> 0)
9606 /* Get the OleTypeName */
9607 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9608 if(dwSize
!= pData
->dwOleTypeNameLength
)
9610 hRes
= CONVERT10_E_OLESTREAM_GET
;
9616 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9617 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9619 hRes
= CONVERT10_E_OLESTREAM_GET
;
9623 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9624 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9625 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9626 if(pData
->pstrOleObjFileName
)
9628 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9629 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9631 hRes
= CONVERT10_E_OLESTREAM_GET
;
9635 hRes
= CONVERT10_E_OLESTREAM_GET
;
9640 /* Get the Width of the Metafile */
9641 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9642 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9644 hRes
= CONVERT10_E_OLESTREAM_GET
;
9648 /* Get the Height of the Metafile */
9649 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9650 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9652 hRes
= CONVERT10_E_OLESTREAM_GET
;
9658 /* Get the Length of the Data */
9659 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9660 if(dwSize
!= sizeof(pData
->dwDataLength
))
9662 hRes
= CONVERT10_E_OLESTREAM_GET
;
9666 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9668 if(!bStrem1
) /* if it is a second OLE stream data */
9670 pData
->dwDataLength
-= 8;
9671 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9672 if(dwSize
!= sizeof(pData
->strUnknown
))
9674 hRes
= CONVERT10_E_OLESTREAM_GET
;
9680 if(pData
->dwDataLength
> 0)
9682 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9684 /* Get Data (ex. IStorage, Metafile, or BMP) */
9687 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9688 if(dwSize
!= pData
->dwDataLength
)
9690 hRes
= CONVERT10_E_OLESTREAM_GET
;
9695 hRes
= CONVERT10_E_OLESTREAM_GET
;
9704 /*************************************************************************
9705 * OLECONVERT_SaveOLE10 [Internal]
9707 * Saves the OLE10 STREAM From memory
9710 * pData [I] Data Structure for the OLESTREAM Data
9711 * pOleStream [I] The OLESTREAM to save
9715 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9718 * This function is used by OleConvertIStorageToOLESTREAM only.
9721 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9724 HRESULT hRes
= S_OK
;
9728 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9729 if(dwSize
!= sizeof(pData
->dwOleID
))
9731 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9736 /* Set the TypeID */
9737 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9738 if(dwSize
!= sizeof(pData
->dwTypeID
))
9740 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9744 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9746 /* Set the Length of the OleTypeName */
9747 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9748 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9750 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9755 if(pData
->dwOleTypeNameLength
> 0)
9757 /* Set the OleTypeName */
9758 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9759 if(dwSize
!= pData
->dwOleTypeNameLength
)
9761 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9768 /* Set the width of the Metafile */
9769 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9770 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9772 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9778 /* Set the height of the Metafile */
9779 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9780 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9782 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9788 /* Set the length of the Data */
9789 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9790 if(dwSize
!= sizeof(pData
->dwDataLength
))
9792 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9798 if(pData
->dwDataLength
> 0)
9800 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9801 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9802 if(dwSize
!= pData
->dwDataLength
)
9804 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9812 /*************************************************************************
9813 * OLECONVERT_GetOLE20FromOLE10[Internal]
9815 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9816 * opens it, and copies the content to the dest IStorage for
9817 * OleConvertOLESTREAMToIStorage
9821 * pDestStorage [I] The IStorage to copy the data to
9822 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9823 * nBufferLength [I] The size of the buffer
9832 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9836 IStorage
*pTempStorage
;
9837 DWORD dwNumOfBytesWritten
;
9838 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9839 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9841 /* Create a temp File */
9842 GetTempPathW(MAX_PATH
, wstrTempDir
);
9843 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9844 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9846 if(hFile
!= INVALID_HANDLE_VALUE
)
9848 /* Write IStorage Data to File */
9849 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9852 /* Open and copy temp storage to the Dest Storage */
9853 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9856 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9857 IStorage_Release(pTempStorage
);
9859 DeleteFileW(wstrTempFile
);
9864 /*************************************************************************
9865 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9867 * Saves the OLE10 STREAM From memory
9870 * pStorage [I] The Src IStorage to copy
9871 * pData [I] The Dest Memory to write to.
9874 * The size in bytes allocated for pData
9877 * Memory allocated for pData must be freed by the caller
9879 * Used by OleConvertIStorageToOLESTREAM only.
9882 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9886 DWORD nDataLength
= 0;
9887 IStorage
*pTempStorage
;
9888 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9889 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9893 /* Create temp Storage */
9894 GetTempPathW(MAX_PATH
, wstrTempDir
);
9895 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9896 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9900 /* Copy Src Storage to the Temp Storage */
9901 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9902 IStorage_Release(pTempStorage
);
9904 /* Open Temp Storage as a file and copy to memory */
9905 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9906 if(hFile
!= INVALID_HANDLE_VALUE
)
9908 nDataLength
= GetFileSize(hFile
, NULL
);
9909 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9910 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9913 DeleteFileW(wstrTempFile
);
9918 /*************************************************************************
9919 * OLECONVERT_CreateCompObjStream [Internal]
9921 * Creates a "\001CompObj" is the destination IStorage if necessary.
9924 * pStorage [I] The dest IStorage to create the CompObj Stream
9926 * strOleTypeName [I] The ProgID
9930 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9933 * This function is used by OleConvertOLESTREAMToIStorage only.
9935 * The stream data is stored in the OLESTREAM and there should be
9936 * no need to recreate the stream. If the stream is manually
9937 * deleted it will attempt to create it by querying the registry.
9941 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9944 HRESULT hStorageRes
, hRes
= S_OK
;
9945 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9946 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9947 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9949 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9950 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9952 /* Initialize the CompObj structure */
9953 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9954 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9955 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9958 /* Create a CompObj stream if it doesn't exist */
9959 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9960 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9961 if(hStorageRes
== S_OK
)
9963 /* copy the OleTypeName to the compobj struct */
9964 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9965 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9967 /* copy the OleTypeName to the compobj struct */
9968 /* Note: in the test made, these were Identical */
9969 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9970 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9973 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9974 bufferW
, OLESTREAM_MAX_STR_LEN
);
9975 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9981 /* Get the CLSID Default Name from the Registry */
9982 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9983 if(hErr
== ERROR_SUCCESS
)
9985 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9986 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9987 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9988 if(hErr
== ERROR_SUCCESS
)
9990 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9996 /* Write CompObj Structure to stream */
9997 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9999 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
10001 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
10002 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
10004 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
10006 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
10007 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
10009 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
10011 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
10012 if(IStorageCompObj
.dwProgIDNameLength
> 0)
10014 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
10016 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
10017 IStream_Release(pStream
);
10023 /*************************************************************************
10024 * OLECONVERT_CreateOlePresStream[Internal]
10026 * Creates the "\002OlePres000" Stream with the Metafile data
10029 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10030 * dwExtentX [I] Width of the Metafile
10031 * dwExtentY [I] Height of the Metafile
10032 * pData [I] Metafile data
10033 * dwDataLength [I] Size of the Metafile data
10037 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10040 * This function is used by OleConvertOLESTREAMToIStorage only.
10043 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10047 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10048 BYTE pOlePresStreamHeader
[] =
10050 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10051 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10052 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10053 0x00, 0x00, 0x00, 0x00
10056 BYTE pOlePresStreamHeaderEmpty
[] =
10058 0x00, 0x00, 0x00, 0x00,
10059 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10060 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10061 0x00, 0x00, 0x00, 0x00
10064 /* Create the OlePres000 Stream */
10065 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10066 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10071 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10073 memset(&OlePres
, 0, sizeof(OlePres
));
10074 /* Do we have any metafile data to save */
10075 if(dwDataLength
> 0)
10077 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10078 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10082 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10083 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10085 /* Set width and height of the metafile */
10086 OlePres
.dwExtentX
= dwExtentX
;
10087 OlePres
.dwExtentY
= -dwExtentY
;
10089 /* Set Data and Length */
10090 if(dwDataLength
> sizeof(METAFILEPICT16
))
10092 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10093 OlePres
.pData
= &(pData
[8]);
10095 /* Save OlePres000 Data to Stream */
10096 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10097 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10098 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10099 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10100 if(OlePres
.dwSize
> 0)
10102 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10104 IStream_Release(pStream
);
10108 /*************************************************************************
10109 * OLECONVERT_CreateOle10NativeStream [Internal]
10111 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10114 * pStorage [I] Dest storage to create the stream in
10115 * pData [I] Ole10 Native Data (ex. bmp)
10116 * dwDataLength [I] Size of the Ole10 Native Data
10122 * This function is used by OleConvertOLESTREAMToIStorage only.
10124 * Might need to verify the data and return appropriate error message
10127 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10131 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10133 /* Create the Ole10Native Stream */
10134 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10135 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10139 /* Write info to stream */
10140 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10141 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10142 IStream_Release(pStream
);
10147 /*************************************************************************
10148 * OLECONVERT_GetOLE10ProgID [Internal]
10150 * Finds the ProgID (or OleTypeID) from the IStorage
10153 * pStorage [I] The Src IStorage to get the ProgID
10154 * strProgID [I] the ProgID string to get
10155 * dwSize [I] the size of the string
10159 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10162 * This function is used by OleConvertIStorageToOLESTREAM only.
10166 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10170 LARGE_INTEGER iSeekPos
;
10171 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10172 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10174 /* Open the CompObj Stream */
10175 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10176 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10180 /*Get the OleType from the CompObj Stream */
10181 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10182 iSeekPos
.u
.HighPart
= 0;
10184 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10185 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10186 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10187 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10188 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10189 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10190 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10192 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10195 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10197 IStream_Release(pStream
);
10202 LPOLESTR wstrProgID
;
10204 /* Get the OleType from the registry */
10205 REFCLSID clsid
= &(stat
.clsid
);
10206 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10207 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10210 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10211 CoTaskMemFree(wstrProgID
);
10218 /*************************************************************************
10219 * OLECONVERT_GetOle10PresData [Internal]
10221 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10224 * pStorage [I] Src IStroage
10225 * pOleStream [I] Dest OleStream Mem Struct
10231 * This function is used by OleConvertIStorageToOLESTREAM only.
10233 * Memory allocated for pData must be freed by the caller
10237 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10242 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10244 /* Initialize Default data for OLESTREAM */
10245 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10246 pOleStreamData
[0].dwTypeID
= 2;
10247 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10248 pOleStreamData
[1].dwTypeID
= 0;
10249 pOleStreamData
[0].dwMetaFileWidth
= 0;
10250 pOleStreamData
[0].dwMetaFileHeight
= 0;
10251 pOleStreamData
[0].pData
= NULL
;
10252 pOleStreamData
[1].pData
= NULL
;
10254 /* Open Ole10Native Stream */
10255 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10256 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10260 /* Read Size and Data */
10261 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10262 if(pOleStreamData
->dwDataLength
> 0)
10264 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10265 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10267 IStream_Release(pStream
);
10273 /*************************************************************************
10274 * OLECONVERT_GetOle20PresData[Internal]
10276 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10279 * pStorage [I] Src IStroage
10280 * pOleStreamData [I] Dest OleStream Mem Struct
10286 * This function is used by OleConvertIStorageToOLESTREAM only.
10288 * Memory allocated for pData must be freed by the caller
10290 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10294 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10295 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10297 /* Initialize Default data for OLESTREAM */
10298 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10299 pOleStreamData
[0].dwTypeID
= 2;
10300 pOleStreamData
[0].dwMetaFileWidth
= 0;
10301 pOleStreamData
[0].dwMetaFileHeight
= 0;
10302 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10303 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10304 pOleStreamData
[1].dwTypeID
= 0;
10305 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10306 pOleStreamData
[1].strOleTypeName
[0] = 0;
10307 pOleStreamData
[1].dwMetaFileWidth
= 0;
10308 pOleStreamData
[1].dwMetaFileHeight
= 0;
10309 pOleStreamData
[1].pData
= NULL
;
10310 pOleStreamData
[1].dwDataLength
= 0;
10313 /* Open OlePress000 stream */
10314 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10315 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10318 LARGE_INTEGER iSeekPos
;
10319 METAFILEPICT16 MetaFilePict
;
10320 static const char strMetafilePictName
[] = "METAFILEPICT";
10322 /* Set the TypeID for a Metafile */
10323 pOleStreamData
[1].dwTypeID
= 5;
10325 /* Set the OleTypeName to Metafile */
10326 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10327 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10329 iSeekPos
.u
.HighPart
= 0;
10330 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10332 /* Get Presentation Data */
10333 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10334 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10335 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10336 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10338 /*Set width and Height */
10339 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10340 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10341 if(olePress
.dwSize
> 0)
10344 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10346 /* Set MetaFilePict struct */
10347 MetaFilePict
.mm
= 8;
10348 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10349 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10350 MetaFilePict
.hMF
= 0;
10352 /* Get Metafile Data */
10353 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10354 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10355 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10357 IStream_Release(pStream
);
10361 /*************************************************************************
10362 * OleConvertOLESTREAMToIStorage [OLE32.@]
10364 * Read info on MSDN
10367 * DVTARGETDEVICE parameter is not handled
10368 * Still unsure of some mem fields for OLE 10 Stream
10369 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10370 * and "\001OLE" streams
10373 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10374 LPOLESTREAM pOleStream
,
10376 const DVTARGETDEVICE
* ptd
)
10380 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10382 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10384 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10388 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10391 if(pstg
== NULL
|| pOleStream
== NULL
)
10393 hRes
= E_INVALIDARG
;
10398 /* Load the OLESTREAM to Memory */
10399 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10404 /* Load the OLESTREAM to Memory (part 2)*/
10405 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10411 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10413 /* Do we have the IStorage Data in the OLESTREAM */
10414 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10416 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10417 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10421 /* It must be an original OLE 1.0 source */
10422 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10427 /* It must be an original OLE 1.0 source */
10428 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10431 /* Create CompObj Stream if necessary */
10432 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10435 /*Create the Ole Stream if necessary */
10436 STORAGE_CreateOleStream(pstg
, 0);
10441 /* Free allocated memory */
10442 for(i
=0; i
< 2; i
++)
10444 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10445 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10446 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10451 /*************************************************************************
10452 * OleConvertIStorageToOLESTREAM [OLE32.@]
10454 * Read info on MSDN
10456 * Read info on MSDN
10459 * Still unsure of some mem fields for OLE 10 Stream
10460 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10461 * and "\001OLE" streams.
10464 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10466 LPOLESTREAM pOleStream
)
10469 HRESULT hRes
= S_OK
;
10471 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10472 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10474 TRACE("%p %p\n", pstg
, pOleStream
);
10476 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10478 if(pstg
== NULL
|| pOleStream
== NULL
)
10480 hRes
= E_INVALIDARG
;
10484 /* Get the ProgID */
10485 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10486 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10490 /* Was it originally Ole10 */
10491 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10494 IStream_Release(pStream
);
10495 /* Get Presentation Data for Ole10Native */
10496 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10500 /* Get Presentation Data (OLE20) */
10501 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10504 /* Save OLESTREAM */
10505 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10508 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10513 /* Free allocated memory */
10514 for(i
=0; i
< 2; i
++)
10516 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10522 enum stream_1ole_flags
{
10523 OleStream_LinkedObject
= 0x00000001,
10524 OleStream_Convert
= 0x00000004
10527 /***********************************************************************
10528 * GetConvertStg (OLE32.@)
10530 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10532 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10533 static const DWORD version_magic
= 0x02000001;
10538 TRACE("%p\n", stg
);
10540 if (!stg
) return E_INVALIDARG
;
10542 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10543 if (FAILED(hr
)) return hr
;
10545 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10546 IStream_Release(stream
);
10547 if (FAILED(hr
)) return hr
;
10549 if (header
[0] != version_magic
)
10551 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10555 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10558 /***********************************************************************
10559 * SetConvertStg (OLE32.@)
10561 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10563 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10564 DWORD flags
= convert
? OleStream_Convert
: 0;
10569 TRACE("(%p, %d)\n", storage
, convert
);
10571 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10574 if (hr
!= STG_E_FILENOTFOUND
)
10577 return STORAGE_CreateOleStream(storage
, flags
);
10580 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10583 IStream_Release(stream
);
10587 /* update flag if differs */
10588 if ((header
[1] ^ flags
) & OleStream_Convert
)
10590 LARGE_INTEGER pos
= {{0}};
10592 if (header
[1] & OleStream_Convert
)
10593 flags
= header
[1] & ~OleStream_Convert
;
10595 flags
= header
[1] | OleStream_Convert
;
10597 pos
.QuadPart
= sizeof(DWORD
);
10598 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10601 IStream_Release(stream
);
10605 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10608 IStream_Release(stream
);