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 to find
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 allow to reinvoke 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 StorageUtl_ReadDWord(
3451 OFFSET_PS_SIZE_HIGH
,
3452 &buffer
->size
.u
.HighPart
);
3458 /*********************************************************************
3459 * Write the specified directory entry to the file
3461 static HRESULT
StorageImpl_WriteDirEntry(
3464 const DirEntry
* buffer
)
3466 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3468 UpdateRawDirEntry(currentEntry
, buffer
);
3470 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3474 /************************************************************************
3475 * StorageImpl implementation : Block methods
3476 ***********************************************************************/
3478 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
3480 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
3483 static HRESULT
StorageImpl_ReadBigBlock(
3489 ULARGE_INTEGER ulOffset
;
3493 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3495 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3497 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
3499 /* File ends during this block; fill the rest with 0's. */
3500 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3503 if (out_read
) *out_read
= read
;
3508 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3514 ULARGE_INTEGER ulOffset
;
3518 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3519 ulOffset
.QuadPart
+= offset
;
3521 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3522 *value
= lendian32toh(tmp
);
3523 return (read
== sizeof(DWORD
));
3526 static BOOL
StorageImpl_WriteBigBlock(
3531 ULARGE_INTEGER ulOffset
;
3534 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3536 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3537 return (wrote
== This
->bigBlockSize
);
3540 static BOOL
StorageImpl_WriteDWordToBigBlock(
3546 ULARGE_INTEGER ulOffset
;
3549 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3550 ulOffset
.QuadPart
+= offset
;
3552 value
= htole32(value
);
3553 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3554 return (wrote
== sizeof(DWORD
));
3557 /******************************************************************************
3558 * Storage32Impl_SmallBlocksToBigBlocks
3560 * This method will convert a small block chain to a big block chain.
3561 * The small block chain will be destroyed.
3563 static BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3565 SmallBlockChainStream
** ppsbChain
)
3567 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3568 ULARGE_INTEGER size
, offset
;
3569 ULONG cbRead
, cbWritten
;
3570 ULARGE_INTEGER cbTotalRead
;
3571 DirRef streamEntryRef
;
3572 HRESULT resWrite
= S_OK
;
3574 DirEntry streamEntry
;
3576 BlockChainStream
*bbTempChain
= NULL
;
3577 BlockChainStream
*bigBlockChain
= NULL
;
3580 * Create a temporary big block chain that doesn't have
3581 * an associated directory entry. This temporary chain will be
3582 * used to copy data from small blocks to big blocks.
3584 bbTempChain
= BlockChainStream_Construct(This
,
3587 if(!bbTempChain
) return NULL
;
3589 * Grow the big block chain.
3591 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3592 BlockChainStream_SetSize(bbTempChain
, size
);
3595 * Copy the contents of the small block chain to the big block chain
3596 * by small block size increments.
3598 offset
.u
.LowPart
= 0;
3599 offset
.u
.HighPart
= 0;
3600 cbTotalRead
.QuadPart
= 0;
3602 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3605 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3607 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3610 if (FAILED(resRead
))
3615 cbTotalRead
.QuadPart
+= cbRead
;
3617 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3623 if (FAILED(resWrite
))
3626 offset
.u
.LowPart
+= cbRead
;
3630 resRead
= STG_E_READFAULT
;
3633 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3634 HeapFree(GetProcessHeap(),0,buffer
);
3636 size
.u
.HighPart
= 0;
3639 if (FAILED(resRead
) || FAILED(resWrite
))
3641 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3642 BlockChainStream_SetSize(bbTempChain
, size
);
3643 BlockChainStream_Destroy(bbTempChain
);
3648 * Destroy the small block chain.
3650 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3651 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3652 SmallBlockChainStream_Destroy(*ppsbChain
);
3656 * Change the directory entry. This chain is now a big block chain
3657 * and it doesn't reside in the small blocks chain anymore.
3659 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3661 streamEntry
.startingBlock
= bbHeadOfChain
;
3663 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3666 * Destroy the temporary entryless big block chain.
3667 * Create a new big block chain associated with this entry.
3669 BlockChainStream_Destroy(bbTempChain
);
3670 bigBlockChain
= BlockChainStream_Construct(This
,
3674 return bigBlockChain
;
3677 /******************************************************************************
3678 * Storage32Impl_BigBlocksToSmallBlocks
3680 * This method will convert a big block chain to a small block chain.
3681 * The big block chain will be destroyed on success.
3683 static SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3685 BlockChainStream
** ppbbChain
,
3686 ULARGE_INTEGER newSize
)
3688 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3689 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3690 DirRef streamEntryRef
;
3691 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
3692 DirEntry streamEntry
;
3694 SmallBlockChainStream
* sbTempChain
;
3696 TRACE("%p %p\n", This
, ppbbChain
);
3698 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3704 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
3705 size
= BlockChainStream_GetSize(*ppbbChain
);
3706 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
3708 offset
.u
.HighPart
= 0;
3709 offset
.u
.LowPart
= 0;
3710 cbTotalRead
.QuadPart
= 0;
3711 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3712 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
3714 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3715 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3723 cbTotalRead
.QuadPart
+= cbRead
;
3725 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3726 cbRead
, buffer
, &cbWritten
);
3728 if(FAILED(resWrite
))
3731 offset
.u
.LowPart
+= cbRead
;
3735 resRead
= STG_E_READFAULT
;
3739 HeapFree(GetProcessHeap(), 0, buffer
);
3741 size
.u
.HighPart
= 0;
3744 if(FAILED(resRead
) || FAILED(resWrite
))
3746 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3747 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3748 SmallBlockChainStream_Destroy(sbTempChain
);
3752 /* destroy the original big block chain */
3753 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3754 BlockChainStream_SetSize(*ppbbChain
, size
);
3755 BlockChainStream_Destroy(*ppbbChain
);
3758 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3759 streamEntry
.startingBlock
= sbHeadOfChain
;
3760 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3762 SmallBlockChainStream_Destroy(sbTempChain
);
3763 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3766 /******************************************************************************
3767 * Storage32Impl_AddBlockDepot
3769 * This will create a depot block, essentially it is a block initialized
3772 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3774 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3775 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3776 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3777 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3780 * Initialize blocks as free
3782 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3784 /* Reserve the range lock sector */
3785 if (depotIndex
== rangeLockDepot
)
3787 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3790 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3793 /******************************************************************************
3794 * Storage32Impl_GetExtDepotBlock
3796 * Returns the index of the block that corresponds to the specified depot
3797 * index. This method is only for depot indexes equal or greater than
3798 * COUNT_BBDEPOTINHEADER.
3800 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3802 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3803 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3804 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3805 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3806 ULONG blockIndex
= BLOCK_UNUSED
;
3807 ULONG extBlockIndex
;
3808 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3809 int index
, num_blocks
;
3811 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3813 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3814 return BLOCK_UNUSED
;
3816 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3818 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3820 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3822 num_blocks
= This
->bigBlockSize
/ 4;
3824 for (index
= 0; index
< num_blocks
; index
++)
3826 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3827 This
->extBlockDepotCached
[index
] = blockIndex
;
3830 This
->indexExtBlockDepotCached
= extBlockCount
;
3833 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3838 /******************************************************************************
3839 * Storage32Impl_SetExtDepotBlock
3841 * Associates the specified block index to the specified depot index.
3842 * This method is only for depot indexes equal or greater than
3843 * COUNT_BBDEPOTINHEADER.
3845 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3847 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3848 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3849 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3850 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3851 ULONG extBlockIndex
;
3853 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3855 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3857 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3859 if (extBlockIndex
!= BLOCK_UNUSED
)
3861 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3862 extBlockOffset
* sizeof(ULONG
),
3866 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3868 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3872 /******************************************************************************
3873 * Storage32Impl_AddExtBlockDepot
3875 * Creates an extended depot block.
3877 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3879 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3880 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3881 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3882 ULONG index
= BLOCK_UNUSED
;
3883 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3884 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3885 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3887 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3888 blocksPerDepotBlock
;
3890 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3893 * The first extended block.
3895 This
->extBigBlockDepotStart
= index
;
3900 * Find the last existing extended block.
3902 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3905 * Add the new extended block to the chain.
3907 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3912 * Initialize this block.
3914 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3915 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3917 /* Add the block to our cache. */
3918 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3920 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3921 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3923 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3924 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3926 This
->extBigBlockDepotLocations
= new_cache
;
3927 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3929 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3934 /************************************************************************
3935 * StorageImpl_GetNextBlockInChain
3937 * This method will retrieve the block index of the next big block in
3940 * Params: This - Pointer to the Storage object.
3941 * blockIndex - Index of the block to retrieve the chain
3943 * nextBlockIndex - receives the return value.
3945 * Returns: This method returns the index of the next block in the chain.
3946 * It will return the constants:
3947 * BLOCK_SPECIAL - If the block given was not part of a
3949 * BLOCK_END_OF_CHAIN - If the block given was the last in
3951 * BLOCK_UNUSED - If the block given was not past of a chain
3953 * BLOCK_EXTBBDEPOT - This block is part of the extended
3956 * See Windows documentation for more details on IStorage methods.
3958 static HRESULT
StorageImpl_GetNextBlockInChain(
3961 ULONG
* nextBlockIndex
)
3963 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3964 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3965 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3966 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3968 ULONG depotBlockIndexPos
;
3969 int index
, num_blocks
;
3971 *nextBlockIndex
= BLOCK_SPECIAL
;
3973 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3975 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3976 This
->bigBlockDepotCount
);
3977 return STG_E_READFAULT
;
3981 * Cache the currently accessed depot block.
3983 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3985 This
->indexBlockDepotCached
= depotBlockCount
;
3987 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3989 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3994 * We have to look in the extended depot.
3996 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3999 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4002 return STG_E_READFAULT
;
4004 num_blocks
= This
->bigBlockSize
/ 4;
4006 for (index
= 0; index
< num_blocks
; index
++)
4008 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
4009 This
->blockDepotCached
[index
] = *nextBlockIndex
;
4013 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
4018 /******************************************************************************
4019 * Storage32Impl_GetNextExtendedBlock
4021 * Given an extended block this method will return the next extended block.
4024 * The last ULONG of an extended block is the block index of the next
4025 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4029 * - The index of the next extended block
4030 * - BLOCK_UNUSED: there is no next extended block.
4031 * - Any other return values denotes failure.
4033 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
4035 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4036 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
4038 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
4041 return nextBlockIndex
;
4044 /******************************************************************************
4045 * StorageImpl_SetNextBlockInChain
4047 * This method will write the index of the specified block's next block
4048 * in the big block depot.
4050 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4053 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4054 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4055 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4058 static void StorageImpl_SetNextBlockInChain(
4063 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
4064 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
4065 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
4066 ULONG depotBlockIndexPos
;
4068 assert(depotBlockCount
< This
->bigBlockDepotCount
);
4069 assert(blockIndex
!= nextBlock
);
4071 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
4072 /* This should never happen (storage file format spec forbids it), but
4073 * older versions of Wine may have generated broken files. We don't want to
4074 * assert and potentially lose data, but we do want to know if this ever
4075 * happens in a newly-created file. */
4076 ERR("Using range lock page\n");
4078 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
4080 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
4085 * We have to look in the extended depot.
4087 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
4090 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
4093 * Update the cached block depot, if necessary.
4095 if (depotBlockCount
== This
->indexBlockDepotCached
)
4097 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
4101 /******************************************************************************
4102 * StorageImpl_GetNextFreeBigBlock
4104 * Returns the index of the next free big block.
4105 * If the big block depot is filled, this method will enlarge it.
4108 static ULONG
StorageImpl_GetNextFreeBigBlock(
4111 ULONG depotBlockIndexPos
;
4112 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
4113 ULONG depotBlockOffset
;
4114 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
4115 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
4117 ULONG freeBlock
= BLOCK_UNUSED
;
4119 ULARGE_INTEGER neededSize
;
4122 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
4123 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
4126 * Scan the entire big block depot until we find a block marked free
4128 while (nextBlockIndex
!= BLOCK_UNUSED
)
4130 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
4132 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
4135 * Grow the primary depot.
4137 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4139 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
4142 * Add a block depot.
4144 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4145 This
->bigBlockDepotCount
++;
4146 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
4149 * Flag it as a block depot.
4151 StorageImpl_SetNextBlockInChain(This
,
4155 /* Save new header information.
4157 StorageImpl_SaveFileHeader(This
);
4162 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
4164 if (depotBlockIndexPos
== BLOCK_UNUSED
)
4167 * Grow the extended depot.
4169 ULONG extIndex
= BLOCK_UNUSED
;
4170 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
4171 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
4173 if (extBlockOffset
== 0)
4175 /* We need an extended block.
4177 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
4178 This
->extBigBlockDepotCount
++;
4179 depotBlockIndexPos
= extIndex
+ 1;
4182 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
4185 * Add a block depot and mark it in the extended block.
4187 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
4188 This
->bigBlockDepotCount
++;
4189 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
4191 /* Flag the block depot.
4193 StorageImpl_SetNextBlockInChain(This
,
4197 /* If necessary, flag the extended depot block.
4199 if (extIndex
!= BLOCK_UNUSED
)
4200 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
4202 /* Save header information.
4204 StorageImpl_SaveFileHeader(This
);
4208 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
4212 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
4213 ( nextBlockIndex
!= BLOCK_UNUSED
))
4215 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
4217 if (nextBlockIndex
== BLOCK_UNUSED
)
4219 freeBlock
= (depotIndex
* blocksPerDepot
) +
4220 (depotBlockOffset
/sizeof(ULONG
));
4223 depotBlockOffset
+= sizeof(ULONG
);
4228 depotBlockOffset
= 0;
4232 * make sure that the block physically exists before using it
4234 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
4236 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
4238 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
4239 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
4241 This
->prevFreeBlock
= freeBlock
;
4246 /******************************************************************************
4247 * StorageImpl_FreeBigBlock
4249 * This method will flag the specified block as free in the big block depot.
4251 static void StorageImpl_FreeBigBlock(
4255 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4257 if (blockIndex
< This
->prevFreeBlock
)
4258 This
->prevFreeBlock
= blockIndex
;
4262 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
4263 DirRef index
, const DirEntry
*data
)
4265 StorageImpl
*This
= (StorageImpl
*)base
;
4266 return StorageImpl_WriteDirEntry(This
, index
, data
);
4269 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
4270 DirRef index
, DirEntry
*data
)
4272 StorageImpl
*This
= (StorageImpl
*)base
;
4273 return StorageImpl_ReadDirEntry(This
, index
, data
);
4276 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
4280 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4282 if (!This
->blockChainCache
[i
])
4284 return &This
->blockChainCache
[i
];
4288 i
= This
->blockChainToEvict
;
4290 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4291 This
->blockChainCache
[i
] = NULL
;
4293 This
->blockChainToEvict
++;
4294 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4295 This
->blockChainToEvict
= 0;
4297 return &This
->blockChainCache
[i
];
4300 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
4303 int i
, free_index
=-1;
4305 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4307 if (!This
->blockChainCache
[i
])
4309 if (free_index
== -1) free_index
= i
;
4311 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4313 return &This
->blockChainCache
[i
];
4317 if (free_index
== -1)
4319 free_index
= This
->blockChainToEvict
;
4321 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
4322 This
->blockChainCache
[free_index
] = NULL
;
4324 This
->blockChainToEvict
++;
4325 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
4326 This
->blockChainToEvict
= 0;
4329 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
4330 return &This
->blockChainCache
[free_index
];
4333 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
4337 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4339 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
4341 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4342 This
->blockChainCache
[i
] = NULL
;
4348 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
4349 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4351 StorageImpl
*This
= (StorageImpl
*)base
;
4356 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4357 if (FAILED(hr
)) return hr
;
4359 if (data
.size
.QuadPart
== 0)
4365 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
4367 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
4374 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4376 SmallBlockChainStream
*stream
;
4378 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4379 if (!stream
) return E_OUTOFMEMORY
;
4381 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4383 SmallBlockChainStream_Destroy(stream
);
4389 BlockChainStream
*stream
= NULL
;
4391 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4392 if (!stream
) return E_OUTOFMEMORY
;
4394 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
4400 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
4401 ULARGE_INTEGER newsize
)
4403 StorageImpl
*This
= (StorageImpl
*)base
;
4406 SmallBlockChainStream
*smallblock
=NULL
;
4407 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
4409 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4410 if (FAILED(hr
)) return hr
;
4412 /* In simple mode keep the stream size above the small block limit */
4413 if (This
->base
.openFlags
& STGM_SIMPLE
)
4414 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
4416 if (data
.size
.QuadPart
== newsize
.QuadPart
)
4419 /* Create a block chain object of the appropriate type */
4420 if (data
.size
.QuadPart
== 0)
4422 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4424 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4425 if (!smallblock
) return E_OUTOFMEMORY
;
4429 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4430 bigblock
= *pbigblock
;
4431 if (!bigblock
) return E_OUTOFMEMORY
;
4434 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4436 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4437 if (!smallblock
) return E_OUTOFMEMORY
;
4441 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
4442 bigblock
= *pbigblock
;
4443 if (!bigblock
) return E_OUTOFMEMORY
;
4446 /* Change the block chain type if necessary. */
4447 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
4449 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
4452 SmallBlockChainStream_Destroy(smallblock
);
4456 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
4457 *pbigblock
= bigblock
;
4459 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4461 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
4466 /* Set the size of the block chain. */
4469 SmallBlockChainStream_SetSize(smallblock
, newsize
);
4470 SmallBlockChainStream_Destroy(smallblock
);
4474 BlockChainStream_SetSize(bigblock
, newsize
);
4477 /* Set the size in the directory entry. */
4478 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4481 data
.size
= newsize
;
4483 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
4488 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
4489 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4491 StorageImpl
*This
= (StorageImpl
*)base
;
4494 ULARGE_INTEGER newSize
;
4496 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4497 if (FAILED(hr
)) return hr
;
4499 /* Grow the stream if necessary */
4500 newSize
.QuadPart
= offset
.QuadPart
+ size
;
4502 if (newSize
.QuadPart
> data
.size
.QuadPart
)
4504 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
4508 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
4509 if (FAILED(hr
)) return hr
;
4512 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
4514 SmallBlockChainStream
*stream
;
4516 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
4517 if (!stream
) return E_OUTOFMEMORY
;
4519 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4521 SmallBlockChainStream_Destroy(stream
);
4527 BlockChainStream
*stream
;
4529 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
4530 if (!stream
) return E_OUTOFMEMORY
;
4532 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
4536 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
4539 StorageImpl
*This
= (StorageImpl
*)base
;
4540 DirEntry dst_data
, src_data
;
4543 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
4546 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
4550 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
4551 dst_data
.startingBlock
= src_data
.startingBlock
;
4552 dst_data
.size
= src_data
.size
;
4554 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
4560 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
4563 DirEntry currentEntry
;
4564 DirRef currentEntryRef
;
4565 BlockChainStream
*blockChainStream
;
4569 ULARGE_INTEGER size
;
4570 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
4572 /* Discard any existing data. */
4574 ILockBytes_SetSize(This
->lockBytes
, size
);
4577 * Initialize all header variables:
4578 * - The big block depot consists of one block and it is at block 0
4579 * - The directory table starts at block 1
4580 * - There is no small block depot
4582 memset( This
->bigBlockDepotStart
,
4584 sizeof(This
->bigBlockDepotStart
));
4586 This
->bigBlockDepotCount
= 1;
4587 This
->bigBlockDepotStart
[0] = 0;
4588 This
->rootStartBlock
= 1;
4589 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
4590 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4591 if (This
->bigBlockSize
== 4096)
4592 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
4594 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
4595 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
4596 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
4597 This
->extBigBlockDepotCount
= 0;
4599 StorageImpl_SaveFileHeader(This
);
4602 * Add one block for the big block depot and one block for the directory table
4604 size
.u
.HighPart
= 0;
4605 size
.u
.LowPart
= This
->bigBlockSize
* 3;
4606 ILockBytes_SetSize(This
->lockBytes
, size
);
4609 * Initialize the big block depot
4611 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
4612 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
4613 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
4614 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
4619 * Load the header for the file.
4621 hr
= StorageImpl_LoadFileHeader(This
);
4630 * There is no block depot cached yet.
4632 This
->indexBlockDepotCached
= 0xFFFFFFFF;
4633 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
4636 * Start searching for free blocks with block 0.
4638 This
->prevFreeBlock
= 0;
4640 This
->firstFreeSmallBlock
= 0;
4642 /* Read the extended big block depot locations. */
4643 if (This
->extBigBlockDepotCount
!= 0)
4645 ULONG current_block
= This
->extBigBlockDepotStart
;
4646 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
4649 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
4650 if (!This
->extBigBlockDepotLocations
)
4652 return E_OUTOFMEMORY
;
4655 This
->extBigBlockDepotLocationsSize
= cache_size
;
4657 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
4659 if (current_block
== BLOCK_END_OF_CHAIN
)
4661 WARN("File has too few extended big block depot blocks.\n");
4662 return STG_E_DOCFILECORRUPT
;
4664 This
->extBigBlockDepotLocations
[i
] = current_block
;
4665 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
4670 This
->extBigBlockDepotLocations
= NULL
;
4671 This
->extBigBlockDepotLocationsSize
= 0;
4675 * Create the block chain abstractions.
4677 if(!(blockChainStream
=
4678 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
4680 return STG_E_READFAULT
;
4683 BlockChainStream_Destroy(This
->rootBlockChain
);
4684 This
->rootBlockChain
= blockChainStream
;
4686 if(!(blockChainStream
=
4687 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
4690 return STG_E_READFAULT
;
4693 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
4694 This
->smallBlockDepotChain
= blockChainStream
;
4697 * Write the root storage entry (memory only)
4701 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4704 * Initialize the directory table
4706 memset(&rootEntry
, 0, sizeof(rootEntry
));
4707 strcpyW(rootEntry
.name
, rootentryW
);
4708 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
4709 rootEntry
.stgType
= STGTY_ROOT
;
4710 rootEntry
.leftChild
= DIRENTRY_NULL
;
4711 rootEntry
.rightChild
= DIRENTRY_NULL
;
4712 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
4713 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
4714 rootEntry
.size
.u
.HighPart
= 0;
4715 rootEntry
.size
.u
.LowPart
= 0;
4717 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
4721 * Find the ID of the root storage.
4723 currentEntryRef
= 0;
4727 hr
= StorageImpl_ReadDirEntry(
4734 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
4735 (currentEntry
.stgType
== STGTY_ROOT
) )
4737 This
->base
.storageDirEntry
= currentEntryRef
;
4743 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
4747 return STG_E_READFAULT
;
4751 * Create the block chain abstraction for the small block root chain.
4753 if(!(blockChainStream
=
4754 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
4756 return STG_E_READFAULT
;
4759 BlockChainStream_Destroy(This
->smallBlockRootChain
);
4760 This
->smallBlockRootChain
= blockChainStream
;
4765 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
4767 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
4768 This
->blockChainCache
[i
] = NULL
;
4775 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
4776 ULONG
* result
, BOOL refresh
)
4778 StorageImpl
*This
= (StorageImpl
*)base
;
4780 DWORD oldTransactionSig
= This
->transactionSig
;
4784 ULARGE_INTEGER offset
;
4788 offset
.u
.HighPart
= 0;
4789 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
4790 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
4794 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
4796 if (oldTransactionSig
!= This
->transactionSig
)
4798 /* Someone else wrote to this, so toss all cached information. */
4799 TRACE("signature changed\n");
4801 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
4805 This
->transactionSig
= oldTransactionSig
;
4809 *result
= This
->transactionSig
;
4814 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
4817 StorageImpl
*This
= (StorageImpl
*)base
;
4819 This
->transactionSig
= value
;
4820 StorageImpl_SaveFileHeader(This
);
4825 /* Internal function */
4826 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
4827 ULARGE_INTEGER cb
, DWORD dwLockType
)
4831 DWORD start_time
= GetTickCount();
4832 DWORD last_sanity_check
= start_time
;
4833 ULARGE_INTEGER sanity_offset
, sanity_cb
;
4835 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
4836 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
4840 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
4842 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4844 DWORD current_time
= GetTickCount();
4845 if (current_time
- start_time
>= 20000)
4850 if (current_time
- last_sanity_check
>= 500)
4852 /* Any storage implementation with the file open in a
4853 * shared mode should not lock these bytes for writing. However,
4854 * some programs (LibreOffice Writer) will keep ALL bytes locked
4855 * when opening in exclusive mode. We can use a read lock to
4856 * detect this case early, and not hang a full 20 seconds.
4858 * This can collide with another attempt to open the file in
4859 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4860 hr
= ILockBytes_LockRegion(This
->lockBytes
, sanity_offset
, sanity_cb
, 0);
4861 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4863 if (hr
== STG_E_INVALIDFUNCTION
)
4865 /* ignore this, lockbytes might support dwLockType but not 0 */
4866 hr
= STG_E_ACCESSDENIED
;
4870 ILockBytes_UnlockRegion(This
->lockBytes
, sanity_offset
, sanity_cb
, 0);
4871 hr
= STG_E_ACCESSDENIED
;
4874 last_sanity_check
= current_time
;
4877 if (delay
< 150) delay
++;
4879 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
4884 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
4886 StorageImpl
*This
= (StorageImpl
*)base
;
4888 ULARGE_INTEGER offset
, cb
;
4892 /* Synchronous grab of second priority range, the commit lock, and the
4893 * lock-checking lock. */
4894 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4895 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4899 offset
.QuadPart
= RANGELOCK_COMMIT
;
4903 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
4905 if (hr
== STG_E_INVALIDFUNCTION
)
4911 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
4913 StorageImpl
*This
= (StorageImpl
*)base
;
4915 ULARGE_INTEGER offset
, cb
;
4919 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
4920 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
4924 offset
.QuadPart
= RANGELOCK_COMMIT
;
4928 hr
= ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
4930 if (hr
== STG_E_INVALIDFUNCTION
)
4936 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4938 StorageImpl
*This
= (StorageImpl
*) iface
;
4942 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
4944 *result
= statstg
.pwcsName
;
4949 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
4950 ULONG end
, HRESULT fail_hr
)
4953 ULARGE_INTEGER offset
, cb
;
4955 offset
.QuadPart
= start
;
4956 cb
.QuadPart
= 1 + end
- start
;
4958 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
4959 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
4961 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
4967 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
4971 ULARGE_INTEGER offset
, cb
;
4975 for (i
=start
; i
<=end
; i
++)
4977 offset
.QuadPart
= i
;
4978 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
4979 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
4985 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
4987 if (This
->locked_bytes
[j
] == 0)
4989 This
->locked_bytes
[j
] = i
;
4998 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
5001 ULARGE_INTEGER offset
;
5003 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
5005 if (openFlags
& STGM_NOSNAPSHOT
)
5007 /* STGM_NOSNAPSHOT implies deny write */
5008 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
5009 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
5012 /* Wrap all other locking inside a single lock so we can check ranges safely */
5013 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5015 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
5017 /* If the ILockBytes doesn't support locking that's ok. */
5018 if (hr
== STG_E_INVALIDFUNCTION
) return S_OK
;
5019 else if (FAILED(hr
)) return hr
;
5023 /* First check for any conflicting locks. */
5024 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5025 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
5027 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5028 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
5030 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5031 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
5033 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5034 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
5036 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5037 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
5039 if (SUCCEEDED(hr
) && STGM_ACCESS_MODE(openFlags
) == STGM_READ
&& share_mode
== STGM_SHARE_EXCLUSIVE
)
5041 hr
= StorageImpl_CheckLockRange(This
, 0, RANGELOCK_CHECKLOCKS
-1, STG_E_LOCKVIOLATION
);
5044 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_CHECKLOCKS
+1, RANGELOCK_LAST
, STG_E_LOCKVIOLATION
);
5047 /* Then grab our locks. */
5048 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
5050 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
5052 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
5055 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
5056 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
5058 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
5059 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
5061 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5062 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
5064 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
5065 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
5067 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
5068 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
5070 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
5072 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
5077 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
5079 StorageImpl
*This
= (StorageImpl
*)storage
;
5082 TRACE("(%p)\n", This
);
5084 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
5087 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
5090 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
5092 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5093 if (This
->blockChainCache
[i
])
5094 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
5097 hr
= ILockBytes_Flush(This
->lockBytes
);
5102 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
5104 StorageImpl
*This
= (StorageImpl
*) iface
;
5106 StorageBaseImpl_DeleteAll(&This
->base
);
5108 This
->base
.reverted
= TRUE
;
5111 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
5113 StorageImpl
*This
= (StorageImpl
*) iface
;
5115 TRACE("(%p)\n", This
);
5117 StorageImpl_Flush(iface
);
5119 StorageImpl_Invalidate(iface
);
5121 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
5123 BlockChainStream_Destroy(This
->smallBlockRootChain
);
5124 BlockChainStream_Destroy(This
->rootBlockChain
);
5125 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
5127 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
5128 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
5130 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
5132 ULARGE_INTEGER offset
, cb
;
5134 if (This
->locked_bytes
[i
] != 0)
5136 offset
.QuadPart
= This
->locked_bytes
[i
];
5137 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
5141 if (This
->lockBytes
)
5142 ILockBytes_Release(This
->lockBytes
);
5143 HeapFree(GetProcessHeap(), 0, This
);
5147 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
5149 StorageImpl_Destroy
,
5150 StorageImpl_Invalidate
,
5152 StorageImpl_GetFilename
,
5153 StorageImpl_CreateDirEntry
,
5154 StorageImpl_BaseWriteDirEntry
,
5155 StorageImpl_BaseReadDirEntry
,
5156 StorageImpl_DestroyDirEntry
,
5157 StorageImpl_StreamReadAt
,
5158 StorageImpl_StreamWriteAt
,
5159 StorageImpl_StreamSetSize
,
5160 StorageImpl_StreamLink
,
5161 StorageImpl_GetTransactionSig
,
5162 StorageImpl_SetTransactionSig
,
5163 StorageImpl_LockTransaction
,
5164 StorageImpl_UnlockTransaction
5169 * Virtual function table for the IStorageBaseImpl class.
5171 static const IStorageVtbl StorageImpl_Vtbl
=
5173 StorageBaseImpl_QueryInterface
,
5174 StorageBaseImpl_AddRef
,
5175 StorageBaseImpl_Release
,
5176 StorageBaseImpl_CreateStream
,
5177 StorageBaseImpl_OpenStream
,
5178 StorageBaseImpl_CreateStorage
,
5179 StorageBaseImpl_OpenStorage
,
5180 StorageBaseImpl_CopyTo
,
5181 StorageBaseImpl_MoveElementTo
,
5182 StorageBaseImpl_Commit
,
5183 StorageBaseImpl_Revert
,
5184 StorageBaseImpl_EnumElements
,
5185 StorageBaseImpl_DestroyElement
,
5186 StorageBaseImpl_RenameElement
,
5187 StorageBaseImpl_SetElementTimes
,
5188 StorageBaseImpl_SetClass
,
5189 StorageBaseImpl_SetStateBits
,
5190 StorageBaseImpl_Stat
5193 static HRESULT
StorageImpl_Construct(
5201 StorageImpl
** result
)
5206 if ( FAILED( validateSTGM(openFlags
) ))
5207 return STG_E_INVALIDFLAG
;
5209 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5211 return E_OUTOFMEMORY
;
5213 memset(This
, 0, sizeof(StorageImpl
));
5215 list_init(&This
->base
.strmHead
);
5217 list_init(&This
->base
.storageHead
);
5219 This
->base
.IStorage_iface
.lpVtbl
= &StorageImpl_Vtbl
;
5220 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5221 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
5222 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
5223 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5225 This
->base
.create
= create
;
5227 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
5228 This
->base
.lockingrole
= SWMR_Writer
;
5229 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
5230 This
->base
.lockingrole
= SWMR_Reader
;
5232 This
->base
.lockingrole
= SWMR_None
;
5234 This
->base
.reverted
= FALSE
;
5237 * Initialize the big block cache.
5239 This
->bigBlockSize
= sector_size
;
5240 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
5242 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
5245 This
->lockBytes
= pLkbyt
;
5246 ILockBytes_AddRef(pLkbyt
);
5250 hr
= StorageImpl_GrabLocks(This
, openFlags
);
5253 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
5257 IStorage_Release(&This
->base
.IStorage_iface
);
5262 StorageImpl_Flush(&This
->base
);
5270 /************************************************************************
5271 * StorageInternalImpl implementation
5272 ***********************************************************************/
5274 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5276 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5278 if (!This
->base
.reverted
)
5280 TRACE("Storage invalidated (stg=%p)\n", This
);
5282 This
->base
.reverted
= TRUE
;
5284 This
->parentStorage
= NULL
;
5286 StorageBaseImpl_DeleteAll(&This
->base
);
5288 list_remove(&This
->ParentListEntry
);
5292 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5294 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5296 StorageInternalImpl_Invalidate(&This
->base
);
5298 HeapFree(GetProcessHeap(), 0, This
);
5301 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5303 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5305 return StorageBaseImpl_Flush(This
->parentStorage
);
5308 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5310 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5312 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5315 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5316 const DirEntry
*newData
, DirRef
*index
)
5318 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5320 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5324 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5325 DirRef index
, const DirEntry
*data
)
5327 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5329 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5333 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5334 DirRef index
, DirEntry
*data
)
5336 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5338 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5342 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5345 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5347 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5351 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5352 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5354 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5356 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5357 index
, offset
, size
, buffer
, bytesRead
);
5360 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5361 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5363 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5365 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5366 index
, offset
, size
, buffer
, bytesWritten
);
5369 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5370 DirRef index
, ULARGE_INTEGER newsize
)
5372 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5374 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5378 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5379 DirRef dst
, DirRef src
)
5381 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5383 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5387 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
5388 ULONG
* result
, BOOL refresh
)
5393 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
5399 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5404 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5409 /******************************************************************************
5411 ** StorageInternalImpl_Commit
5414 static HRESULT WINAPI
StorageInternalImpl_Commit(
5416 DWORD grfCommitFlags
) /* [in] */
5418 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5419 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5420 return StorageBaseImpl_Flush(This
);
5423 /******************************************************************************
5425 ** StorageInternalImpl_Revert
5428 static HRESULT WINAPI
StorageInternalImpl_Revert(
5431 FIXME("(%p): stub\n", iface
);
5436 * Virtual function table for the StorageInternalImpl class.
5438 static const IStorageVtbl StorageInternalImpl_Vtbl
=
5440 StorageBaseImpl_QueryInterface
,
5441 StorageBaseImpl_AddRef
,
5442 StorageBaseImpl_Release
,
5443 StorageBaseImpl_CreateStream
,
5444 StorageBaseImpl_OpenStream
,
5445 StorageBaseImpl_CreateStorage
,
5446 StorageBaseImpl_OpenStorage
,
5447 StorageBaseImpl_CopyTo
,
5448 StorageBaseImpl_MoveElementTo
,
5449 StorageInternalImpl_Commit
,
5450 StorageInternalImpl_Revert
,
5451 StorageBaseImpl_EnumElements
,
5452 StorageBaseImpl_DestroyElement
,
5453 StorageBaseImpl_RenameElement
,
5454 StorageBaseImpl_SetElementTimes
,
5455 StorageBaseImpl_SetClass
,
5456 StorageBaseImpl_SetStateBits
,
5457 StorageBaseImpl_Stat
5460 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5462 StorageInternalImpl_Destroy
,
5463 StorageInternalImpl_Invalidate
,
5464 StorageInternalImpl_Flush
,
5465 StorageInternalImpl_GetFilename
,
5466 StorageInternalImpl_CreateDirEntry
,
5467 StorageInternalImpl_WriteDirEntry
,
5468 StorageInternalImpl_ReadDirEntry
,
5469 StorageInternalImpl_DestroyDirEntry
,
5470 StorageInternalImpl_StreamReadAt
,
5471 StorageInternalImpl_StreamWriteAt
,
5472 StorageInternalImpl_StreamSetSize
,
5473 StorageInternalImpl_StreamLink
,
5474 StorageInternalImpl_GetTransactionSig
,
5475 StorageInternalImpl_SetTransactionSig
,
5476 StorageInternalImpl_LockTransaction
,
5477 StorageInternalImpl_UnlockTransaction
5480 static StorageInternalImpl
* StorageInternalImpl_Construct(
5481 StorageBaseImpl
* parentStorage
,
5483 DirRef storageDirEntry
)
5485 StorageInternalImpl
* newStorage
;
5487 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5491 list_init(&newStorage
->base
.strmHead
);
5493 list_init(&newStorage
->base
.storageHead
);
5496 * Initialize the virtual function table.
5498 newStorage
->base
.IStorage_iface
.lpVtbl
= &StorageInternalImpl_Vtbl
;
5499 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5500 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5501 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5503 newStorage
->base
.reverted
= FALSE
;
5505 newStorage
->base
.ref
= 1;
5507 newStorage
->parentStorage
= parentStorage
;
5510 * Keep a reference to the directory entry of this storage
5512 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5514 newStorage
->base
.create
= FALSE
;
5523 /************************************************************************
5524 * TransactedSnapshotImpl implementation
5525 ***********************************************************************/
5527 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
5529 DirRef result
=This
->firstFreeEntry
;
5531 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
5534 if (result
== This
->entries_size
)
5536 ULONG new_size
= This
->entries_size
* 2;
5537 TransactedDirEntry
*new_entries
;
5539 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
5540 if (!new_entries
) return DIRENTRY_NULL
;
5542 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
5543 HeapFree(GetProcessHeap(), 0, This
->entries
);
5545 This
->entries
= new_entries
;
5546 This
->entries_size
= new_size
;
5549 This
->entries
[result
].inuse
= TRUE
;
5551 This
->firstFreeEntry
= result
+1;
5556 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
5557 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
5559 DirRef stubEntryRef
;
5560 TransactedDirEntry
*entry
;
5562 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
5564 if (stubEntryRef
!= DIRENTRY_NULL
)
5566 entry
= &This
->entries
[stubEntryRef
];
5568 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
5570 entry
->read
= FALSE
;
5573 return stubEntryRef
;
5576 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
5577 TransactedSnapshotImpl
*This
, DirRef entry
)
5582 if (!This
->entries
[entry
].read
)
5584 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5585 This
->entries
[entry
].transactedParentEntry
,
5588 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
5590 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
5592 if (data
.leftChild
== DIRENTRY_NULL
)
5596 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
5598 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
5600 if (data
.rightChild
== DIRENTRY_NULL
)
5604 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
5606 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
5608 if (data
.dirRootEntry
== DIRENTRY_NULL
)
5614 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
5615 This
->entries
[entry
].read
= TRUE
;
5622 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
5623 TransactedSnapshotImpl
*This
, DirRef entry
)
5627 if (!This
->entries
[entry
].stream_dirty
)
5629 DirEntry new_entrydata
;
5631 memset(&new_entrydata
, 0, sizeof(DirEntry
));
5632 new_entrydata
.name
[0] = 'S';
5633 new_entrydata
.sizeOfNameString
= 1;
5634 new_entrydata
.stgType
= STGTY_STREAM
;
5635 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
5636 new_entrydata
.leftChild
= DIRENTRY_NULL
;
5637 new_entrydata
.rightChild
= DIRENTRY_NULL
;
5638 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
5640 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
5641 &This
->entries
[entry
].stream_entry
);
5643 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5645 hr
= StorageBaseImpl_CopyStream(
5646 This
->scratch
, This
->entries
[entry
].stream_entry
,
5647 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
5650 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
5654 This
->entries
[entry
].stream_dirty
= TRUE
;
5656 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
5658 /* Since this entry is modified, and we aren't using its stream data, we
5659 * no longer care about the original entry. */
5661 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
5663 if (delete_ref
!= DIRENTRY_NULL
)
5664 This
->entries
[delete_ref
].deleted
= TRUE
;
5666 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
5673 /* Find the first entry in a depth-first traversal. */
5674 static DirRef
TransactedSnapshotImpl_FindFirstChild(
5675 TransactedSnapshotImpl
* This
, DirRef parent
)
5677 DirRef cursor
, prev
;
5678 TransactedDirEntry
*entry
;
5681 entry
= &This
->entries
[cursor
];
5684 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
5687 cursor
= entry
->data
.leftChild
;
5688 entry
= &This
->entries
[cursor
];
5689 entry
->parent
= prev
;
5691 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
5694 cursor
= entry
->data
.rightChild
;
5695 entry
= &This
->entries
[cursor
];
5696 entry
->parent
= prev
;
5698 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5701 cursor
= entry
->data
.dirRootEntry
;
5702 entry
= &This
->entries
[cursor
];
5703 entry
->parent
= prev
;
5712 /* Find the next entry in a depth-first traversal. */
5713 static DirRef
TransactedSnapshotImpl_FindNextChild(
5714 TransactedSnapshotImpl
* This
, DirRef current
)
5717 TransactedDirEntry
*parent_entry
;
5719 parent
= This
->entries
[current
].parent
;
5720 parent_entry
= &This
->entries
[parent
];
5722 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5724 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5726 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5727 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5730 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5732 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5733 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5740 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5741 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5742 TransactedSnapshotImpl
* This
, DirRef entry
)
5744 return entry
!= DIRENTRY_NULL
&&
5745 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5748 /* Destroy the entries created by CopyTree. */
5749 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5750 TransactedSnapshotImpl
* This
, DirRef stop
)
5753 TransactedDirEntry
*entry
;
5754 ULARGE_INTEGER zero
;
5758 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5761 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5763 if (cursor
== DIRENTRY_NULL
)
5766 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5768 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5770 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5772 entry
= &This
->entries
[cursor
];
5774 if (entry
->stream_dirty
)
5775 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5776 entry
->newTransactedParentEntry
, zero
);
5778 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5779 entry
->newTransactedParentEntry
);
5781 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5784 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5788 /* Make a copy of our edited tree that we can use in the parent. */
5789 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5792 TransactedDirEntry
*entry
;
5795 cursor
= This
->base
.storageDirEntry
;
5796 entry
= &This
->entries
[cursor
];
5797 entry
->parent
= DIRENTRY_NULL
;
5798 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5800 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5803 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5805 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5806 entry
= &This
->entries
[cursor
];
5808 while (cursor
!= DIRENTRY_NULL
)
5810 /* Make a copy of this entry in the transacted parent. */
5812 (!entry
->dirty
&& !entry
->stream_dirty
&&
5813 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5814 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5815 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5816 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5821 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5823 newData
.size
.QuadPart
= 0;
5824 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5826 if (newData
.leftChild
!= DIRENTRY_NULL
)
5827 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5829 if (newData
.rightChild
!= DIRENTRY_NULL
)
5830 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5832 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5833 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5835 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5836 &entry
->newTransactedParentEntry
);
5839 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5843 if (entry
->stream_dirty
)
5845 hr
= StorageBaseImpl_CopyStream(
5846 This
->transactedParent
, entry
->newTransactedParentEntry
,
5847 This
->scratch
, entry
->stream_entry
);
5849 else if (entry
->data
.size
.QuadPart
)
5851 hr
= StorageBaseImpl_StreamLink(
5852 This
->transactedParent
, entry
->newTransactedParentEntry
,
5853 entry
->transactedParentEntry
);
5858 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5859 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5864 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5865 entry
= &This
->entries
[cursor
];
5871 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5873 DWORD grfCommitFlags
) /* [in] */
5875 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5876 TransactedDirEntry
*root_entry
;
5877 DirRef i
, dir_root_ref
;
5879 ULARGE_INTEGER zero
;
5881 ULONG transactionSig
;
5885 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5887 /* Cannot commit a read-only transacted storage */
5888 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5889 return STG_E_ACCESSDENIED
;
5891 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5892 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5895 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5898 if (transactionSig
!= This
->lastTransactionSig
)
5900 ERR("file was externally modified\n");
5901 hr
= STG_E_NOTCURRENT
;
5906 This
->lastTransactionSig
= transactionSig
+1;
5907 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5910 else if (hr
== E_NOTIMPL
)
5913 if (FAILED(hr
)) goto end
;
5915 /* To prevent data loss, we create the new structure in the file before we
5916 * delete the old one, so that in case of errors the old data is intact. We
5917 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5918 * needed in the rare situation where we have just enough free disk space to
5919 * overwrite the existing data. */
5921 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5923 if (!root_entry
->read
)
5926 hr
= TransactedSnapshotImpl_CopyTree(This
);
5927 if (FAILED(hr
)) goto end
;
5929 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5930 dir_root_ref
= DIRENTRY_NULL
;
5932 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5934 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5936 /* Update the storage to use the new data in one step. */
5938 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5939 root_entry
->transactedParentEntry
, &data
);
5943 data
.dirRootEntry
= dir_root_ref
;
5944 data
.clsid
= root_entry
->data
.clsid
;
5945 data
.ctime
= root_entry
->data
.ctime
;
5946 data
.mtime
= root_entry
->data
.mtime
;
5948 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5949 root_entry
->transactedParentEntry
, &data
);
5952 /* Try to flush after updating the root storage, but if the flush fails, keep
5953 * going, on the theory that it'll either succeed later or the subsequent
5954 * writes will fail. */
5955 StorageBaseImpl_Flush(This
->transactedParent
);
5959 /* Destroy the old now-orphaned data. */
5960 for (i
=0; i
<This
->entries_size
; i
++)
5962 TransactedDirEntry
*entry
= &This
->entries
[i
];
5967 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5968 entry
->transactedParentEntry
, zero
);
5969 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5970 entry
->transactedParentEntry
);
5971 memset(entry
, 0, sizeof(TransactedDirEntry
));
5972 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5974 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5976 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5977 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5978 entry
->transactedParentEntry
);
5979 if (entry
->stream_dirty
)
5981 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5982 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5983 entry
->stream_dirty
= FALSE
;
5985 entry
->dirty
= FALSE
;
5986 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5993 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5997 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5999 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6005 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
6008 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
6009 ULARGE_INTEGER zero
;
6012 TRACE("(%p)\n", iface
);
6014 /* Destroy the open objects. */
6015 StorageBaseImpl_DeleteAll(&This
->base
);
6017 /* Clear out the scratch file. */
6019 for (i
=0; i
<This
->entries_size
; i
++)
6021 if (This
->entries
[i
].stream_dirty
)
6023 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
6026 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
6030 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
6032 This
->firstFreeEntry
= 0;
6033 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
6038 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
6040 if (!This
->reverted
)
6042 TRACE("Storage invalidated (stg=%p)\n", This
);
6044 This
->reverted
= TRUE
;
6046 StorageBaseImpl_DeleteAll(This
);
6050 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
6052 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6054 IStorage_Revert(&This
->base
.IStorage_iface
);
6055 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6056 IStorage_Release(&This
->scratch
->IStorage_iface
);
6057 HeapFree(GetProcessHeap(), 0, This
->entries
);
6058 HeapFree(GetProcessHeap(), 0, This
);
6061 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
6063 /* We only need to flush when committing. */
6067 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6069 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
6071 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6074 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
6075 const DirEntry
*newData
, DirRef
*index
)
6077 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6079 TransactedDirEntry
*new_entry
;
6081 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
6082 if (new_ref
== DIRENTRY_NULL
)
6083 return E_OUTOFMEMORY
;
6085 new_entry
= &This
->entries
[new_ref
];
6087 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
6088 new_entry
->read
= TRUE
;
6089 new_entry
->dirty
= TRUE
;
6090 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
6094 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
6099 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
6100 DirRef index
, const DirEntry
*data
)
6102 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6105 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6107 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6108 if (FAILED(hr
)) return hr
;
6110 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
6112 if (index
!= This
->base
.storageDirEntry
)
6114 This
->entries
[index
].dirty
= TRUE
;
6116 if (data
->size
.QuadPart
== 0 &&
6117 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6119 /* Since this entry is modified, and we aren't using its stream data, we
6120 * no longer care about the original entry. */
6122 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6124 if (delete_ref
!= DIRENTRY_NULL
)
6125 This
->entries
[delete_ref
].deleted
= TRUE
;
6127 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6134 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
6135 DirRef index
, DirEntry
*data
)
6137 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6140 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6141 if (FAILED(hr
)) return hr
;
6143 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
6145 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
6150 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6153 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6155 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
6156 This
->entries
[index
].data
.size
.QuadPart
!= 0)
6158 /* If we deleted this entry while it has stream data. We must have left the
6159 * data because some other entry is using it, and we need to leave the
6160 * original entry alone. */
6161 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
6162 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
6166 This
->entries
[index
].deleted
= TRUE
;
6172 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
6173 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6175 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6177 if (This
->entries
[index
].stream_dirty
)
6179 return StorageBaseImpl_StreamReadAt(This
->scratch
,
6180 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
6182 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
6184 /* This stream doesn't live in the parent, and we haven't allocated storage
6191 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
6192 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
6196 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
6197 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6199 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6202 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6203 if (FAILED(hr
)) return hr
;
6205 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6206 if (FAILED(hr
)) return hr
;
6208 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
6209 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
6211 if (SUCCEEDED(hr
) && size
!= 0)
6212 This
->entries
[index
].data
.size
.QuadPart
= max(
6213 This
->entries
[index
].data
.size
.QuadPart
,
6214 offset
.QuadPart
+ size
);
6219 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
6220 DirRef index
, ULARGE_INTEGER newsize
)
6222 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6225 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
6226 if (FAILED(hr
)) return hr
;
6228 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
6231 if (newsize
.QuadPart
== 0)
6233 /* Destroy any parent references or entries in the scratch file. */
6234 if (This
->entries
[index
].stream_dirty
)
6236 ULARGE_INTEGER zero
;
6238 StorageBaseImpl_StreamSetSize(This
->scratch
,
6239 This
->entries
[index
].stream_entry
, zero
);
6240 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
6241 This
->entries
[index
].stream_entry
);
6242 This
->entries
[index
].stream_dirty
= FALSE
;
6244 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
6247 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
6249 if (delete_ref
!= DIRENTRY_NULL
)
6250 This
->entries
[delete_ref
].deleted
= TRUE
;
6252 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
6257 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
6258 if (FAILED(hr
)) return hr
;
6260 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
6261 This
->entries
[index
].stream_entry
, newsize
);
6265 This
->entries
[index
].data
.size
= newsize
;
6270 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
6271 DirRef dst
, DirRef src
)
6273 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
6275 TransactedDirEntry
*dst_entry
, *src_entry
;
6277 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
6278 if (FAILED(hr
)) return hr
;
6280 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
6281 if (FAILED(hr
)) return hr
;
6283 dst_entry
= &This
->entries
[dst
];
6284 src_entry
= &This
->entries
[src
];
6286 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
6287 dst_entry
->stream_entry
= src_entry
->stream_entry
;
6288 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
6289 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
6290 dst_entry
->data
.size
= src_entry
->data
.size
;
6295 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
6296 ULONG
* result
, BOOL refresh
)
6301 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
6307 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6312 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6317 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
6319 StorageBaseImpl_QueryInterface
,
6320 StorageBaseImpl_AddRef
,
6321 StorageBaseImpl_Release
,
6322 StorageBaseImpl_CreateStream
,
6323 StorageBaseImpl_OpenStream
,
6324 StorageBaseImpl_CreateStorage
,
6325 StorageBaseImpl_OpenStorage
,
6326 StorageBaseImpl_CopyTo
,
6327 StorageBaseImpl_MoveElementTo
,
6328 TransactedSnapshotImpl_Commit
,
6329 TransactedSnapshotImpl_Revert
,
6330 StorageBaseImpl_EnumElements
,
6331 StorageBaseImpl_DestroyElement
,
6332 StorageBaseImpl_RenameElement
,
6333 StorageBaseImpl_SetElementTimes
,
6334 StorageBaseImpl_SetClass
,
6335 StorageBaseImpl_SetStateBits
,
6336 StorageBaseImpl_Stat
6339 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
6341 TransactedSnapshotImpl_Destroy
,
6342 TransactedSnapshotImpl_Invalidate
,
6343 TransactedSnapshotImpl_Flush
,
6344 TransactedSnapshotImpl_GetFilename
,
6345 TransactedSnapshotImpl_CreateDirEntry
,
6346 TransactedSnapshotImpl_WriteDirEntry
,
6347 TransactedSnapshotImpl_ReadDirEntry
,
6348 TransactedSnapshotImpl_DestroyDirEntry
,
6349 TransactedSnapshotImpl_StreamReadAt
,
6350 TransactedSnapshotImpl_StreamWriteAt
,
6351 TransactedSnapshotImpl_StreamSetSize
,
6352 TransactedSnapshotImpl_StreamLink
,
6353 TransactedSnapshotImpl_GetTransactionSig
,
6354 TransactedSnapshotImpl_SetTransactionSig
,
6355 TransactedSnapshotImpl_LockTransaction
,
6356 TransactedSnapshotImpl_UnlockTransaction
6359 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
6360 TransactedSnapshotImpl
** result
)
6364 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
6369 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
6371 /* This is OK because the property set storage functions use the IStorage functions. */
6372 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6373 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
6375 list_init(&(*result
)->base
.strmHead
);
6377 list_init(&(*result
)->base
.storageHead
);
6379 (*result
)->base
.ref
= 1;
6381 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6383 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6384 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6386 /* Create a new temporary storage to act as the scratch file. */
6387 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
6389 (*result
)->scratch
= impl_from_IStorage(scratch
);
6393 ULONG num_entries
= 20;
6395 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
6396 (*result
)->entries_size
= num_entries
;
6397 (*result
)->firstFreeEntry
= 0;
6399 if ((*result
)->entries
)
6401 /* parentStorage already has 1 reference, which we take over here. */
6402 (*result
)->transactedParent
= parentStorage
;
6404 parentStorage
->transactedChild
= &(*result
)->base
;
6406 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
6410 IStorage_Release(scratch
);
6416 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6421 return E_OUTOFMEMORY
;
6425 /************************************************************************
6426 * TransactedSharedImpl implementation
6427 ***********************************************************************/
6429 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
6431 if (!This
->reverted
)
6433 TRACE("Storage invalidated (stg=%p)\n", This
);
6435 This
->reverted
= TRUE
;
6437 StorageBaseImpl_DeleteAll(This
);
6441 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
6443 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6445 TransactedSharedImpl_Invalidate(&This
->base
);
6446 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
6447 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
6448 HeapFree(GetProcessHeap(), 0, This
);
6451 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
6453 /* We only need to flush when committing. */
6457 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6459 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
6461 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
6464 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
6465 const DirEntry
*newData
, DirRef
*index
)
6467 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6469 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
6473 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
6474 DirRef index
, const DirEntry
*data
)
6476 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6478 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
6482 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
6483 DirRef index
, DirEntry
*data
)
6485 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6487 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
6491 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6494 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6496 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
6500 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
6501 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6503 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6505 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
6506 index
, offset
, size
, buffer
, bytesRead
);
6509 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
6510 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6512 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6514 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
6515 index
, offset
, size
, buffer
, bytesWritten
);
6518 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
6519 DirRef index
, ULARGE_INTEGER newsize
)
6521 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6523 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
6527 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
6528 DirRef dst
, DirRef src
)
6530 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
6532 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
6536 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
6537 ULONG
* result
, BOOL refresh
)
6542 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
6548 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6553 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6558 static HRESULT WINAPI
TransactedSharedImpl_Commit(
6560 DWORD grfCommitFlags
) /* [in] */
6562 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6563 DirRef new_storage_ref
, prev_storage_ref
;
6564 DirEntry src_data
, dst_data
;
6566 ULONG transactionSig
;
6568 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6570 /* Cannot commit a read-only transacted storage */
6571 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
6572 return STG_E_ACCESSDENIED
;
6574 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
6575 if (hr
== E_NOTIMPL
) hr
= S_OK
;
6578 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
6581 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
6582 hr
= STG_E_NOTCURRENT
;
6585 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
6587 else if (hr
== E_NOTIMPL
)
6591 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
6593 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6595 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
6598 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6601 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6605 prev_storage_ref
= dst_data
.dirRootEntry
;
6606 dst_data
.dirRootEntry
= new_storage_ref
;
6607 dst_data
.clsid
= src_data
.clsid
;
6608 dst_data
.ctime
= src_data
.ctime
;
6609 dst_data
.mtime
= src_data
.mtime
;
6610 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
6615 /* Try to flush after updating the root storage, but if the flush fails, keep
6616 * going, on the theory that it'll either succeed later or the subsequent
6617 * writes will fail. */
6618 StorageBaseImpl_Flush(This
->transactedParent
);
6620 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
6624 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
6626 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
6629 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
6633 This
->lastTransactionSig
= transactionSig
+1;
6640 static HRESULT WINAPI
TransactedSharedImpl_Revert(
6643 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
6645 TRACE("(%p)\n", iface
);
6647 /* Destroy the open objects. */
6648 StorageBaseImpl_DeleteAll(&This
->base
);
6650 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
6653 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
6655 StorageBaseImpl_QueryInterface
,
6656 StorageBaseImpl_AddRef
,
6657 StorageBaseImpl_Release
,
6658 StorageBaseImpl_CreateStream
,
6659 StorageBaseImpl_OpenStream
,
6660 StorageBaseImpl_CreateStorage
,
6661 StorageBaseImpl_OpenStorage
,
6662 StorageBaseImpl_CopyTo
,
6663 StorageBaseImpl_MoveElementTo
,
6664 TransactedSharedImpl_Commit
,
6665 TransactedSharedImpl_Revert
,
6666 StorageBaseImpl_EnumElements
,
6667 StorageBaseImpl_DestroyElement
,
6668 StorageBaseImpl_RenameElement
,
6669 StorageBaseImpl_SetElementTimes
,
6670 StorageBaseImpl_SetClass
,
6671 StorageBaseImpl_SetStateBits
,
6672 StorageBaseImpl_Stat
6675 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
6677 TransactedSharedImpl_Destroy
,
6678 TransactedSharedImpl_Invalidate
,
6679 TransactedSharedImpl_Flush
,
6680 TransactedSharedImpl_GetFilename
,
6681 TransactedSharedImpl_CreateDirEntry
,
6682 TransactedSharedImpl_WriteDirEntry
,
6683 TransactedSharedImpl_ReadDirEntry
,
6684 TransactedSharedImpl_DestroyDirEntry
,
6685 TransactedSharedImpl_StreamReadAt
,
6686 TransactedSharedImpl_StreamWriteAt
,
6687 TransactedSharedImpl_StreamSetSize
,
6688 TransactedSharedImpl_StreamLink
,
6689 TransactedSharedImpl_GetTransactionSig
,
6690 TransactedSharedImpl_SetTransactionSig
,
6691 TransactedSharedImpl_LockTransaction
,
6692 TransactedSharedImpl_UnlockTransaction
6695 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
6696 TransactedSharedImpl
** result
)
6700 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6705 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6707 /* This is OK because the property set storage functions use the IStorage functions. */
6708 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6709 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6711 list_init(&(*result
)->base
.strmHead
);
6713 list_init(&(*result
)->base
.storageHead
);
6715 (*result
)->base
.ref
= 1;
6717 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6719 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6725 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6726 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6730 stgo
.ulSectorSize
= 4096;
6731 stgo
.pwcsTemplateFile
= NULL
;
6733 /* Create a new temporary storage to act as the scratch file. */
6734 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6735 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6736 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6740 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6741 parentStorage
, parentStorage
->storageDirEntry
);
6745 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6747 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6748 (*result
)->transactedParent
= parentStorage
;
6752 IStorage_Release(scratch
);
6755 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6758 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6763 return E_OUTOFMEMORY
;
6766 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6767 BOOL toplevel
, StorageBaseImpl
** result
)
6769 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6771 if (parentStorage
->openFlags
& fixme_flags
)
6773 fixme_flags
&= ~parentStorage
->openFlags
;
6774 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6777 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6778 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6779 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6781 /* Need to create a temp file for the snapshot */
6782 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6785 return TransactedSnapshotImpl_Construct(parentStorage
,
6786 (TransactedSnapshotImpl
**)result
);
6789 static HRESULT
Storage_Construct(
6797 StorageBaseImpl
** result
)
6799 StorageImpl
*newStorage
;
6800 StorageBaseImpl
*newTransactedStorage
;
6803 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6804 if (FAILED(hr
)) goto end
;
6806 if (openFlags
& STGM_TRANSACTED
)
6808 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6810 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6812 *result
= newTransactedStorage
;
6815 *result
= &newStorage
->base
;
6822 /************************************************************************
6823 * StorageUtl helper functions
6824 ***********************************************************************/
6826 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6830 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6831 *value
= lendian16toh(tmp
);
6834 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6836 value
= htole16(value
);
6837 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6840 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6844 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6845 *value
= lendian32toh(tmp
);
6848 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6850 value
= htole32(value
);
6851 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6854 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6855 ULARGE_INTEGER
* value
)
6857 #ifdef WORDS_BIGENDIAN
6860 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6861 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6862 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6864 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6868 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6869 const ULARGE_INTEGER
*value
)
6871 #ifdef WORDS_BIGENDIAN
6874 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6875 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6876 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6878 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6882 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6884 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6885 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6886 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6888 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6891 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6893 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6894 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6895 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6897 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6900 void StorageUtl_CopyDirEntryToSTATSTG(
6901 StorageBaseImpl
* storage
,
6902 STATSTG
* destination
,
6903 const DirEntry
* source
,
6907 * The copy of the string occurs only when the flag is not set
6909 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6911 /* Use the filename for the root storage. */
6912 destination
->pwcsName
= 0;
6913 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6915 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6916 (source
->name
[0] == 0) )
6918 destination
->pwcsName
= 0;
6922 destination
->pwcsName
=
6923 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6925 strcpyW(destination
->pwcsName
, source
->name
);
6928 switch (source
->stgType
)
6932 destination
->type
= STGTY_STORAGE
;
6935 destination
->type
= STGTY_STREAM
;
6938 destination
->type
= STGTY_STREAM
;
6942 destination
->cbSize
= source
->size
;
6944 currentReturnStruct->mtime = {0}; TODO
6945 currentReturnStruct->ctime = {0};
6946 currentReturnStruct->atime = {0};
6948 destination
->grfMode
= 0;
6949 destination
->grfLocksSupported
= 0;
6950 destination
->clsid
= source
->clsid
;
6951 destination
->grfStateBits
= 0;
6952 destination
->reserved
= 0;
6956 /************************************************************************
6957 * BlockChainStream implementation
6958 ***********************************************************************/
6960 /******************************************************************************
6961 * BlockChainStream_GetHeadOfChain
6963 * Returns the head of this stream chain.
6964 * Some special chains don't have directory entries, their heads are kept in
6965 * This->headOfStreamPlaceHolder.
6968 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6970 DirEntry chainEntry
;
6973 if (This
->headOfStreamPlaceHolder
!= 0)
6974 return *(This
->headOfStreamPlaceHolder
);
6976 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6978 hr
= StorageImpl_ReadDirEntry(
6979 This
->parentStorage
,
6980 This
->ownerDirEntry
,
6983 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
6984 return chainEntry
.startingBlock
;
6987 return BLOCK_END_OF_CHAIN
;
6990 /* Read and save the index of all blocks in this stream. */
6991 static HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6993 ULONG next_sector
, next_offset
;
6995 struct BlockChainRun
*last_run
;
6997 if (This
->indexCacheLen
== 0)
7001 next_sector
= BlockChainStream_GetHeadOfChain(This
);
7005 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7006 next_offset
= last_run
->lastOffset
+1;
7007 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
7008 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
7010 if (FAILED(hr
)) return hr
;
7013 while (next_sector
!= BLOCK_END_OF_CHAIN
)
7015 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
7017 /* Add the current block to the cache. */
7018 if (This
->indexCacheSize
== 0)
7020 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
7021 if (!This
->indexCache
) return E_OUTOFMEMORY
;
7022 This
->indexCacheSize
= 16;
7024 else if (This
->indexCacheSize
== This
->indexCacheLen
)
7026 struct BlockChainRun
*new_cache
;
7029 new_size
= This
->indexCacheSize
* 2;
7030 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
7031 if (!new_cache
) return E_OUTOFMEMORY
;
7032 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
7034 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7035 This
->indexCache
= new_cache
;
7036 This
->indexCacheSize
= new_size
;
7039 This
->indexCacheLen
++;
7040 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7041 last_run
->firstSector
= next_sector
;
7042 last_run
->firstOffset
= next_offset
;
7045 last_run
->lastOffset
= next_offset
;
7047 /* Find the next block. */
7049 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
7050 if (FAILED(hr
)) return hr
;
7053 if (This
->indexCacheLen
)
7055 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
7056 This
->numBlocks
= last_run
->lastOffset
+1;
7060 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7061 This
->numBlocks
= 0;
7067 /* Locate the nth block in this stream. */
7068 static ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
7070 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
7071 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
7073 if (offset
>= This
->numBlocks
)
7074 return BLOCK_END_OF_CHAIN
;
7076 while (min_run
< max_run
)
7078 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
7079 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
7081 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
7082 max_run
= run_to_check
-1;
7084 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
7086 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
7087 min_run
= run_to_check
+1;
7090 /* Block is in this run. */
7091 min_run
= max_run
= run_to_check
;
7094 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
7097 static HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
7098 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
7100 BlockChainBlock
*result
=NULL
;
7104 if (This
->cachedBlocks
[i
].index
== index
)
7106 *sector
= This
->cachedBlocks
[i
].sector
;
7107 *block
= &This
->cachedBlocks
[i
];
7111 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
7112 if (*sector
== BLOCK_END_OF_CHAIN
)
7113 return STG_E_DOCFILECORRUPT
;
7117 if (This
->cachedBlocks
[0].index
== 0xffffffff)
7118 result
= &This
->cachedBlocks
[0];
7119 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
7120 result
= &This
->cachedBlocks
[1];
7123 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
7124 if (This
->blockToEvict
== 2)
7125 This
->blockToEvict
= 0;
7130 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
7131 return STG_E_WRITEFAULT
;
7132 result
->dirty
= FALSE
;
7135 result
->read
= FALSE
;
7136 result
->index
= index
;
7137 result
->sector
= *sector
;
7144 BlockChainStream
* BlockChainStream_Construct(
7145 StorageImpl
* parentStorage
,
7146 ULONG
* headOfStreamPlaceHolder
,
7149 BlockChainStream
* newStream
;
7151 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
7153 newStream
->parentStorage
= parentStorage
;
7154 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7155 newStream
->ownerDirEntry
= dirEntry
;
7156 newStream
->indexCache
= NULL
;
7157 newStream
->indexCacheLen
= 0;
7158 newStream
->indexCacheSize
= 0;
7159 newStream
->cachedBlocks
[0].index
= 0xffffffff;
7160 newStream
->cachedBlocks
[0].dirty
= FALSE
;
7161 newStream
->cachedBlocks
[1].index
= 0xffffffff;
7162 newStream
->cachedBlocks
[1].dirty
= FALSE
;
7163 newStream
->blockToEvict
= 0;
7165 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
7167 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
7168 HeapFree(GetProcessHeap(), 0, newStream
);
7175 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
7178 if (!This
) return S_OK
;
7181 if (This
->cachedBlocks
[i
].dirty
)
7183 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
7184 This
->cachedBlocks
[i
].dirty
= FALSE
;
7186 return STG_E_WRITEFAULT
;
7192 void BlockChainStream_Destroy(BlockChainStream
* This
)
7196 BlockChainStream_Flush(This
);
7197 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7199 HeapFree(GetProcessHeap(), 0, This
);
7202 /******************************************************************************
7203 * BlockChainStream_Shrink
7205 * Shrinks this chain in the big block depot.
7207 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7208 ULARGE_INTEGER newSize
)
7215 * Figure out how many blocks are needed to contain the new size
7217 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7219 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7225 * Go to the new end of chain
7227 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7229 /* Mark the new end of chain */
7230 StorageImpl_SetNextBlockInChain(
7231 This
->parentStorage
,
7233 BLOCK_END_OF_CHAIN
);
7235 This
->tailIndex
= blockIndex
;
7239 if (This
->headOfStreamPlaceHolder
!= 0)
7241 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7245 DirEntry chainEntry
;
7246 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7248 StorageImpl_ReadDirEntry(
7249 This
->parentStorage
,
7250 This
->ownerDirEntry
,
7253 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7255 StorageImpl_WriteDirEntry(
7256 This
->parentStorage
,
7257 This
->ownerDirEntry
,
7261 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7264 This
->numBlocks
= numBlocks
;
7267 * Mark the extra blocks as free
7269 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7271 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7272 StorageImpl_FreeBigBlock(This
->parentStorage
,
7273 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7274 if (last_run
->lastOffset
== last_run
->firstOffset
)
7275 This
->indexCacheLen
--;
7277 last_run
->lastOffset
--;
7281 * Reset the last accessed block cache.
7285 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7287 This
->cachedBlocks
[i
].index
= 0xffffffff;
7288 This
->cachedBlocks
[i
].dirty
= FALSE
;
7295 /******************************************************************************
7296 * BlockChainStream_Enlarge
7298 * Grows this chain in the big block depot.
7300 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7301 ULARGE_INTEGER newSize
)
7303 ULONG blockIndex
, currentBlock
;
7305 ULONG oldNumBlocks
= 0;
7307 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7310 * Empty chain. Create the head.
7312 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7314 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7315 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7317 BLOCK_END_OF_CHAIN
);
7319 if (This
->headOfStreamPlaceHolder
!= 0)
7321 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7325 DirEntry chainEntry
;
7326 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7328 StorageImpl_ReadDirEntry(
7329 This
->parentStorage
,
7330 This
->ownerDirEntry
,
7333 chainEntry
.startingBlock
= blockIndex
;
7335 StorageImpl_WriteDirEntry(
7336 This
->parentStorage
,
7337 This
->ownerDirEntry
,
7341 This
->tailIndex
= blockIndex
;
7342 This
->numBlocks
= 1;
7346 * Figure out how many blocks are needed to contain this stream
7348 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7350 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7354 * Go to the current end of chain
7356 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7358 currentBlock
= blockIndex
;
7360 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7363 currentBlock
= blockIndex
;
7365 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7370 This
->tailIndex
= currentBlock
;
7373 currentBlock
= This
->tailIndex
;
7374 oldNumBlocks
= This
->numBlocks
;
7377 * Add new blocks to the chain
7379 if (oldNumBlocks
< newNumBlocks
)
7381 while (oldNumBlocks
< newNumBlocks
)
7383 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7385 StorageImpl_SetNextBlockInChain(
7386 This
->parentStorage
,
7390 StorageImpl_SetNextBlockInChain(
7391 This
->parentStorage
,
7393 BLOCK_END_OF_CHAIN
);
7395 currentBlock
= blockIndex
;
7399 This
->tailIndex
= blockIndex
;
7400 This
->numBlocks
= newNumBlocks
;
7403 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7410 /******************************************************************************
7411 * BlockChainStream_GetSize
7413 * Returns the size of this chain.
7414 * Will return the block count if this chain doesn't have a directory entry.
7416 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7418 DirEntry chainEntry
;
7420 if(This
->headOfStreamPlaceHolder
== NULL
)
7423 * This chain has a directory entry so use the size value from there.
7425 StorageImpl_ReadDirEntry(
7426 This
->parentStorage
,
7427 This
->ownerDirEntry
,
7430 return chainEntry
.size
;
7435 * this chain is a chain that does not have a directory entry, figure out the
7436 * size by making the product number of used blocks times the
7439 ULARGE_INTEGER result
;
7441 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7442 This
->parentStorage
->bigBlockSize
;
7448 /******************************************************************************
7449 * BlockChainStream_SetSize
7451 * Sets the size of this stream. The big block depot will be updated.
7452 * The file will grow if we grow the chain.
7454 * TODO: Free the actual blocks in the file when we shrink the chain.
7455 * Currently, the blocks are still in the file. So the file size
7456 * doesn't shrink even if we shrink streams.
7458 BOOL
BlockChainStream_SetSize(
7459 BlockChainStream
* This
,
7460 ULARGE_INTEGER newSize
)
7462 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7464 if (newSize
.QuadPart
== size
.QuadPart
)
7467 if (newSize
.QuadPart
< size
.QuadPart
)
7469 BlockChainStream_Shrink(This
, newSize
);
7473 BlockChainStream_Enlarge(This
, newSize
);
7479 /******************************************************************************
7480 * BlockChainStream_ReadAt
7482 * Reads a specified number of bytes from this chain at the specified offset.
7483 * bytesRead may be NULL.
7484 * Failure will be returned if the specified number of bytes has not been read.
7486 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7487 ULARGE_INTEGER offset
,
7492 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7493 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7494 ULONG bytesToReadInBuffer
;
7497 ULARGE_INTEGER stream_size
;
7499 BlockChainBlock
*cachedBlock
;
7501 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7504 * Find the first block in the stream that contains part of the buffer.
7506 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7510 stream_size
= BlockChainStream_GetSize(This
);
7511 if (stream_size
.QuadPart
> offset
.QuadPart
)
7512 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7517 * Start reading the buffer.
7519 bufferWalker
= buffer
;
7523 ULARGE_INTEGER ulOffset
;
7527 * Calculate how many bytes we can copy from this big block.
7529 bytesToReadInBuffer
=
7530 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7532 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7539 /* Not in cache, and we're going to read past the end of the block. */
7540 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7543 StorageImpl_ReadAt(This
->parentStorage
,
7546 bytesToReadInBuffer
,
7551 if (!cachedBlock
->read
)
7554 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7555 return STG_E_READFAULT
;
7557 cachedBlock
->read
= TRUE
;
7560 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7561 bytesReadAt
= bytesToReadInBuffer
;
7564 blockNoInSequence
++;
7565 bufferWalker
+= bytesReadAt
;
7566 size
-= bytesReadAt
;
7567 *bytesRead
+= bytesReadAt
;
7568 offsetInBlock
= 0; /* There is no offset on the next block */
7570 if (bytesToReadInBuffer
!= bytesReadAt
)
7577 /******************************************************************************
7578 * BlockChainStream_WriteAt
7580 * Writes the specified number of bytes to this chain at the specified offset.
7581 * Will fail if not all specified number of bytes have been written.
7583 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7584 ULARGE_INTEGER offset
,
7587 ULONG
* bytesWritten
)
7589 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7590 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7593 const BYTE
* bufferWalker
;
7595 BlockChainBlock
*cachedBlock
;
7598 bufferWalker
= buffer
;
7602 ULARGE_INTEGER ulOffset
;
7603 DWORD bytesWrittenAt
;
7606 * Calculate how many bytes we can copy to this big block.
7609 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7611 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7613 /* BlockChainStream_SetSize should have already been called to ensure we have
7614 * enough blocks in the chain to write into */
7617 ERR("not enough blocks in chain to write data\n");
7623 /* Not in cache, and we're going to write past the end of the block. */
7624 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7627 StorageImpl_WriteAt(This
->parentStorage
,
7635 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7638 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7639 return STG_E_READFAULT
;
7642 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7643 bytesWrittenAt
= bytesToWrite
;
7644 cachedBlock
->read
= TRUE
;
7645 cachedBlock
->dirty
= TRUE
;
7648 blockNoInSequence
++;
7649 bufferWalker
+= bytesWrittenAt
;
7650 size
-= bytesWrittenAt
;
7651 *bytesWritten
+= bytesWrittenAt
;
7652 offsetInBlock
= 0; /* There is no offset on the next block */
7654 if (bytesWrittenAt
!= bytesToWrite
)
7658 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7662 /************************************************************************
7663 * SmallBlockChainStream implementation
7664 ***********************************************************************/
7666 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7667 StorageImpl
* parentStorage
,
7668 ULONG
* headOfStreamPlaceHolder
,
7671 SmallBlockChainStream
* newStream
;
7673 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7675 newStream
->parentStorage
= parentStorage
;
7676 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7677 newStream
->ownerDirEntry
= dirEntry
;
7682 void SmallBlockChainStream_Destroy(
7683 SmallBlockChainStream
* This
)
7685 HeapFree(GetProcessHeap(), 0, This
);
7688 /******************************************************************************
7689 * SmallBlockChainStream_GetHeadOfChain
7691 * Returns the head of this chain of small blocks.
7693 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7694 SmallBlockChainStream
* This
)
7696 DirEntry chainEntry
;
7699 if (This
->headOfStreamPlaceHolder
!= NULL
)
7700 return *(This
->headOfStreamPlaceHolder
);
7702 if (This
->ownerDirEntry
)
7704 hr
= StorageImpl_ReadDirEntry(
7705 This
->parentStorage
,
7706 This
->ownerDirEntry
,
7709 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7710 return chainEntry
.startingBlock
;
7713 return BLOCK_END_OF_CHAIN
;
7716 /******************************************************************************
7717 * SmallBlockChainStream_GetNextBlockInChain
7719 * Returns the index of the next small block in this chain.
7722 * - BLOCK_END_OF_CHAIN: end of this chain
7723 * - BLOCK_UNUSED: small block 'blockIndex' is free
7725 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7726 SmallBlockChainStream
* This
,
7728 ULONG
* nextBlockInChain
)
7730 ULARGE_INTEGER offsetOfBlockInDepot
;
7735 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7737 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7740 * Read those bytes in the buffer from the small block file.
7742 res
= BlockChainStream_ReadAt(
7743 This
->parentStorage
->smallBlockDepotChain
,
7744 offsetOfBlockInDepot
,
7749 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7750 res
= STG_E_READFAULT
;
7754 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7761 /******************************************************************************
7762 * SmallBlockChainStream_SetNextBlockInChain
7764 * Writes the index of the next block of the specified block in the small
7766 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7767 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7769 static void SmallBlockChainStream_SetNextBlockInChain(
7770 SmallBlockChainStream
* This
,
7774 ULARGE_INTEGER offsetOfBlockInDepot
;
7778 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7780 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7783 * Read those bytes in the buffer from the small block file.
7785 BlockChainStream_WriteAt(
7786 This
->parentStorage
->smallBlockDepotChain
,
7787 offsetOfBlockInDepot
,
7793 /******************************************************************************
7794 * SmallBlockChainStream_FreeBlock
7796 * Flag small block 'blockIndex' as free in the small block depot.
7798 static void SmallBlockChainStream_FreeBlock(
7799 SmallBlockChainStream
* This
,
7802 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7805 /******************************************************************************
7806 * SmallBlockChainStream_GetNextFreeBlock
7808 * Returns the index of a free small block. The small block depot will be
7809 * enlarged if necessary. The small block chain will also be enlarged if
7812 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7813 SmallBlockChainStream
* This
)
7815 ULARGE_INTEGER offsetOfBlockInDepot
;
7818 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7819 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7821 ULONG smallBlocksPerBigBlock
;
7823 ULONG blocksRequired
;
7824 ULARGE_INTEGER old_size
, size_required
;
7826 offsetOfBlockInDepot
.u
.HighPart
= 0;
7829 * Scan the small block depot for a free block
7831 while (nextBlockIndex
!= BLOCK_UNUSED
)
7833 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7835 res
= BlockChainStream_ReadAt(
7836 This
->parentStorage
->smallBlockDepotChain
,
7837 offsetOfBlockInDepot
,
7843 * If we run out of space for the small block depot, enlarge it
7845 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7847 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7849 if (nextBlockIndex
!= BLOCK_UNUSED
)
7855 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7857 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7858 ULARGE_INTEGER newSize
, offset
;
7861 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7862 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7865 * Initialize all the small blocks to free
7867 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7868 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7869 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7870 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7872 StorageImpl_SaveFileHeader(This
->parentStorage
);
7876 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7878 smallBlocksPerBigBlock
=
7879 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7882 * Verify if we have to allocate big blocks to contain small blocks
7884 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7886 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7888 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7890 if (size_required
.QuadPart
> old_size
.QuadPart
)
7892 BlockChainStream_SetSize(
7893 This
->parentStorage
->smallBlockRootChain
,
7896 StorageImpl_ReadDirEntry(
7897 This
->parentStorage
,
7898 This
->parentStorage
->base
.storageDirEntry
,
7901 rootEntry
.size
= size_required
;
7903 StorageImpl_WriteDirEntry(
7904 This
->parentStorage
,
7905 This
->parentStorage
->base
.storageDirEntry
,
7912 /******************************************************************************
7913 * SmallBlockChainStream_ReadAt
7915 * Reads a specified number of bytes from this chain at the specified offset.
7916 * bytesRead may be NULL.
7917 * Failure will be returned if the specified number of bytes has not been read.
7919 HRESULT
SmallBlockChainStream_ReadAt(
7920 SmallBlockChainStream
* This
,
7921 ULARGE_INTEGER offset
,
7927 ULARGE_INTEGER offsetInBigBlockFile
;
7928 ULONG blockNoInSequence
=
7929 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7931 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7932 ULONG bytesToReadInBuffer
;
7934 ULONG bytesReadFromBigBlockFile
;
7936 ULARGE_INTEGER stream_size
;
7939 * This should never happen on a small block file.
7941 assert(offset
.u
.HighPart
==0);
7945 stream_size
= SmallBlockChainStream_GetSize(This
);
7946 if (stream_size
.QuadPart
> offset
.QuadPart
)
7947 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7952 * Find the first block in the stream that contains part of the buffer.
7954 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7956 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7958 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7961 blockNoInSequence
--;
7965 * Start reading the buffer.
7967 bufferWalker
= buffer
;
7969 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7972 * Calculate how many bytes we can copy from this small block.
7974 bytesToReadInBuffer
=
7975 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7978 * Calculate the offset of the small block in the small block file.
7980 offsetInBigBlockFile
.QuadPart
=
7981 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7983 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7986 * Read those bytes in the buffer from the small block file.
7987 * The small block has already been identified so it shouldn't fail
7988 * unless the file is corrupt.
7990 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7991 offsetInBigBlockFile
,
7992 bytesToReadInBuffer
,
7994 &bytesReadFromBigBlockFile
);
7999 if (!bytesReadFromBigBlockFile
)
8000 return STG_E_DOCFILECORRUPT
;
8003 * Step to the next big block.
8005 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8007 return STG_E_DOCFILECORRUPT
;
8009 bufferWalker
+= bytesReadFromBigBlockFile
;
8010 size
-= bytesReadFromBigBlockFile
;
8011 *bytesRead
+= bytesReadFromBigBlockFile
;
8012 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8018 /******************************************************************************
8019 * SmallBlockChainStream_WriteAt
8021 * Writes the specified number of bytes to this chain at the specified offset.
8022 * Will fail if not all specified number of bytes have been written.
8024 HRESULT
SmallBlockChainStream_WriteAt(
8025 SmallBlockChainStream
* This
,
8026 ULARGE_INTEGER offset
,
8029 ULONG
* bytesWritten
)
8031 ULARGE_INTEGER offsetInBigBlockFile
;
8032 ULONG blockNoInSequence
=
8033 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8035 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
8036 ULONG bytesToWriteInBuffer
;
8038 ULONG bytesWrittenToBigBlockFile
;
8039 const BYTE
* bufferWalker
;
8043 * This should never happen on a small block file.
8045 assert(offset
.u
.HighPart
==0);
8048 * Find the first block in the stream that contains part of the buffer.
8050 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8052 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
8054 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
8055 return STG_E_DOCFILECORRUPT
;
8056 blockNoInSequence
--;
8060 * Start writing the buffer.
8063 bufferWalker
= buffer
;
8064 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
8067 * Calculate how many bytes we can copy to this small block.
8069 bytesToWriteInBuffer
=
8070 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
8073 * Calculate the offset of the small block in the small block file.
8075 offsetInBigBlockFile
.QuadPart
=
8076 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
8078 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
8081 * Write those bytes in the buffer to the small block file.
8083 res
= BlockChainStream_WriteAt(
8084 This
->parentStorage
->smallBlockRootChain
,
8085 offsetInBigBlockFile
,
8086 bytesToWriteInBuffer
,
8088 &bytesWrittenToBigBlockFile
);
8093 * Step to the next big block.
8095 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
8098 bufferWalker
+= bytesWrittenToBigBlockFile
;
8099 size
-= bytesWrittenToBigBlockFile
;
8100 *bytesWritten
+= bytesWrittenToBigBlockFile
;
8101 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
8104 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
8107 /******************************************************************************
8108 * SmallBlockChainStream_Shrink
8110 * Shrinks this chain in the small block depot.
8112 static BOOL
SmallBlockChainStream_Shrink(
8113 SmallBlockChainStream
* This
,
8114 ULARGE_INTEGER newSize
)
8116 ULONG blockIndex
, extraBlock
;
8120 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8122 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8125 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8128 * Go to the new end of chain
8130 while (count
< numBlocks
)
8132 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8139 * If the count is 0, we have a special case, the head of the chain was
8144 DirEntry chainEntry
;
8146 StorageImpl_ReadDirEntry(This
->parentStorage
,
8147 This
->ownerDirEntry
,
8150 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
8152 StorageImpl_WriteDirEntry(This
->parentStorage
,
8153 This
->ownerDirEntry
,
8157 * We start freeing the chain at the head block.
8159 extraBlock
= blockIndex
;
8163 /* Get the next block before marking the new end */
8164 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8168 /* Mark the new end of chain */
8169 SmallBlockChainStream_SetNextBlockInChain(
8172 BLOCK_END_OF_CHAIN
);
8176 * Mark the extra blocks as free
8178 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8180 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8183 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8184 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8185 extraBlock
= blockIndex
;
8191 /******************************************************************************
8192 * SmallBlockChainStream_Enlarge
8194 * Grows this chain in the small block depot.
8196 static BOOL
SmallBlockChainStream_Enlarge(
8197 SmallBlockChainStream
* This
,
8198 ULARGE_INTEGER newSize
)
8200 ULONG blockIndex
, currentBlock
;
8202 ULONG oldNumBlocks
= 0;
8204 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8207 * Empty chain. Create the head.
8209 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8211 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8212 SmallBlockChainStream_SetNextBlockInChain(
8215 BLOCK_END_OF_CHAIN
);
8217 if (This
->headOfStreamPlaceHolder
!= NULL
)
8219 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8223 DirEntry chainEntry
;
8225 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8228 chainEntry
.startingBlock
= blockIndex
;
8230 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8235 currentBlock
= blockIndex
;
8238 * Figure out how many blocks are needed to contain this stream
8240 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8242 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8246 * Go to the current end of chain
8248 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8251 currentBlock
= blockIndex
;
8252 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8257 * Add new blocks to the chain
8259 while (oldNumBlocks
< newNumBlocks
)
8261 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8262 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8264 SmallBlockChainStream_SetNextBlockInChain(
8267 BLOCK_END_OF_CHAIN
);
8269 currentBlock
= blockIndex
;
8276 /******************************************************************************
8277 * SmallBlockChainStream_SetSize
8279 * Sets the size of this stream.
8280 * The file will grow if we grow the chain.
8282 * TODO: Free the actual blocks in the file when we shrink the chain.
8283 * Currently, the blocks are still in the file. So the file size
8284 * doesn't shrink even if we shrink streams.
8286 BOOL
SmallBlockChainStream_SetSize(
8287 SmallBlockChainStream
* This
,
8288 ULARGE_INTEGER newSize
)
8290 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8292 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8295 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8297 SmallBlockChainStream_Shrink(This
, newSize
);
8301 SmallBlockChainStream_Enlarge(This
, newSize
);
8307 /******************************************************************************
8308 * SmallBlockChainStream_GetCount
8310 * Returns the number of small blocks that comprises this chain.
8311 * This is not the size of the stream as the last block may not be full!
8314 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8319 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8321 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8325 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8326 blockIndex
, &blockIndex
)))
8333 /******************************************************************************
8334 * SmallBlockChainStream_GetSize
8336 * Returns the size of this chain.
8338 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8340 DirEntry chainEntry
;
8342 if(This
->headOfStreamPlaceHolder
!= NULL
)
8344 ULARGE_INTEGER result
;
8345 result
.u
.HighPart
= 0;
8347 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8348 This
->parentStorage
->smallBlockSize
;
8353 StorageImpl_ReadDirEntry(
8354 This
->parentStorage
,
8355 This
->ownerDirEntry
,
8358 return chainEntry
.size
;
8362 /************************************************************************
8363 * Miscellaneous storage functions
8364 ***********************************************************************/
8366 static HRESULT
create_storagefile(
8370 STGOPTIONS
* pStgOptions
,
8374 StorageBaseImpl
* newStorage
= 0;
8375 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8376 HRESULT hr
= STG_E_INVALIDFLAG
;
8380 DWORD fileAttributes
;
8381 WCHAR tempFileName
[MAX_PATH
];
8384 return STG_E_INVALIDPOINTER
;
8386 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8387 return STG_E_INVALIDPARAMETER
;
8389 /* if no share mode given then DENY_NONE is the default */
8390 if (STGM_SHARE_MODE(grfMode
) == 0)
8391 grfMode
|= STGM_SHARE_DENY_NONE
;
8393 if ( FAILED( validateSTGM(grfMode
) ))
8396 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8397 switch(STGM_ACCESS_MODE(grfMode
))
8400 case STGM_READWRITE
:
8406 /* in direct mode, can only use SHARE_EXCLUSIVE */
8407 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8410 /* but in transacted mode, any share mode is valid */
8413 * Generate a unique name.
8417 WCHAR tempPath
[MAX_PATH
];
8418 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8420 memset(tempPath
, 0, sizeof(tempPath
));
8421 memset(tempFileName
, 0, sizeof(tempFileName
));
8423 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8426 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8427 pwcsName
= tempFileName
;
8430 hr
= STG_E_INSUFFICIENTMEMORY
;
8434 creationMode
= TRUNCATE_EXISTING
;
8438 creationMode
= GetCreationModeFromSTGM(grfMode
);
8442 * Interpret the STGM value grfMode
8444 shareMode
= GetShareModeFromSTGM(grfMode
);
8445 accessMode
= GetAccessModeFromSTGM(grfMode
);
8447 if (grfMode
& STGM_DELETEONRELEASE
)
8448 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8450 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8454 hFile
= CreateFileW(pwcsName
,
8462 if (hFile
== INVALID_HANDLE_VALUE
)
8464 if(GetLastError() == ERROR_FILE_EXISTS
)
8465 hr
= STG_E_FILEALREADYEXISTS
;
8472 * Allocate and initialize the new IStorage object.
8474 hr
= Storage_Construct(
8481 pStgOptions
->ulSectorSize
,
8489 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8490 IStorage_Release(&newStorage
->IStorage_iface
);
8493 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8498 /******************************************************************************
8499 * StgCreateDocfile [OLE32.@]
8500 * Creates a new compound file storage object
8503 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8504 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8505 * reserved [ ?] unused?, usually 0
8506 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8509 * S_OK if the file was successfully created
8510 * some STG_E_ value if error
8512 * if pwcsName is NULL, create file with new unique name
8513 * the function can returns
8514 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8517 HRESULT WINAPI
StgCreateDocfile(
8521 IStorage
**ppstgOpen
)
8523 STGOPTIONS stgoptions
= {1, 0, 512};
8525 TRACE("(%s, %x, %d, %p)\n",
8526 debugstr_w(pwcsName
), grfMode
,
8527 reserved
, ppstgOpen
);
8530 return STG_E_INVALIDPOINTER
;
8532 return STG_E_INVALIDPARAMETER
;
8534 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8537 /******************************************************************************
8538 * StgCreateStorageEx [OLE32.@]
8540 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8542 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8543 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8545 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8547 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8548 return STG_E_INVALIDPARAMETER
;
8551 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8553 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8554 return STG_E_INVALIDPARAMETER
;
8557 if (stgfmt
== STGFMT_FILE
)
8559 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8560 return STG_E_INVALIDPARAMETER
;
8563 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8565 STGOPTIONS defaultOptions
= {1, 0, 512};
8567 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8568 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8572 ERR("Invalid stgfmt argument\n");
8573 return STG_E_INVALIDPARAMETER
;
8576 /******************************************************************************
8577 * StgCreatePropSetStg [OLE32.@]
8579 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8580 IPropertySetStorage
**propset
)
8582 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8584 return STG_E_INVALIDPARAMETER
;
8586 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8589 /******************************************************************************
8590 * StgOpenStorageEx [OLE32.@]
8592 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8594 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8595 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8597 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8599 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8600 return STG_E_INVALIDPARAMETER
;
8606 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8607 return STG_E_INVALIDPARAMETER
;
8609 case STGFMT_STORAGE
:
8612 case STGFMT_DOCFILE
:
8613 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8615 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8616 return STG_E_INVALIDPARAMETER
;
8618 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8622 WARN("STGFMT_ANY assuming storage\n");
8626 return STG_E_INVALIDPARAMETER
;
8629 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8633 /******************************************************************************
8634 * StgOpenStorage [OLE32.@]
8636 HRESULT WINAPI
StgOpenStorage(
8637 const OLECHAR
*pwcsName
,
8638 IStorage
*pstgPriority
,
8642 IStorage
**ppstgOpen
)
8644 StorageBaseImpl
* newStorage
= 0;
8649 LPWSTR temp_name
= NULL
;
8651 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8652 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8653 snbExclude
, reserved
, ppstgOpen
);
8657 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8658 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8659 if (FAILED(hr
)) goto end
;
8660 pwcsName
= temp_name
;
8661 TRACE("using filename %s\n", debugstr_w(temp_name
));
8666 hr
= STG_E_INVALIDNAME
;
8672 hr
= STG_E_INVALIDPOINTER
;
8678 hr
= STG_E_INVALIDPARAMETER
;
8682 if (grfMode
& STGM_PRIORITY
)
8684 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8685 return STG_E_INVALIDFLAG
;
8686 if (grfMode
& STGM_DELETEONRELEASE
)
8687 return STG_E_INVALIDFUNCTION
;
8688 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8689 return STG_E_INVALIDFLAG
;
8690 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8691 grfMode
|= STGM_SHARE_DENY_NONE
;
8695 * Validate the sharing mode
8697 if (grfMode
& STGM_DIRECT_SWMR
)
8699 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8700 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8702 hr
= STG_E_INVALIDFLAG
;
8706 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8707 switch(STGM_SHARE_MODE(grfMode
))
8709 case STGM_SHARE_EXCLUSIVE
:
8710 case STGM_SHARE_DENY_WRITE
:
8713 hr
= STG_E_INVALIDFLAG
;
8717 if ( FAILED( validateSTGM(grfMode
) ) ||
8718 (grfMode
&STGM_CREATE
))
8720 hr
= STG_E_INVALIDFLAG
;
8724 /* shared reading requires transacted or single writer mode */
8725 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8726 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8727 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8729 hr
= STG_E_INVALIDFLAG
;
8734 * Interpret the STGM value grfMode
8736 shareMode
= GetShareModeFromSTGM(grfMode
);
8737 accessMode
= GetAccessModeFromSTGM(grfMode
);
8741 hFile
= CreateFileW( pwcsName
,
8746 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8749 if (hFile
==INVALID_HANDLE_VALUE
)
8751 DWORD last_error
= GetLastError();
8757 case ERROR_FILE_NOT_FOUND
:
8758 hr
= STG_E_FILENOTFOUND
;
8761 case ERROR_PATH_NOT_FOUND
:
8762 hr
= STG_E_PATHNOTFOUND
;
8765 case ERROR_ACCESS_DENIED
:
8766 case ERROR_WRITE_PROTECT
:
8767 hr
= STG_E_ACCESSDENIED
;
8770 case ERROR_SHARING_VIOLATION
:
8771 hr
= STG_E_SHAREVIOLATION
;
8782 * Refuse to open the file if it's too small to be a structured storage file
8783 * FIXME: verify the file when reading instead of here
8785 if (GetFileSize(hFile
, NULL
) < 0x100)
8788 hr
= STG_E_FILEALREADYEXISTS
;
8793 * Allocate and initialize the new IStorage object.
8795 hr
= Storage_Construct(
8808 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8810 if(hr
== STG_E_INVALIDHEADER
)
8811 hr
= STG_E_FILEALREADYEXISTS
;
8815 *ppstgOpen
= &newStorage
->IStorage_iface
;
8818 CoTaskMemFree(temp_name
);
8819 if (pstgPriority
) IStorage_Release(pstgPriority
);
8820 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8824 /******************************************************************************
8825 * StgCreateDocfileOnILockBytes [OLE32.@]
8827 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8831 IStorage
** ppstgOpen
)
8833 StorageBaseImpl
* newStorage
= 0;
8836 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8837 return STG_E_INVALIDPOINTER
;
8840 * Allocate and initialize the new IStorage object.
8842 hr
= Storage_Construct(
8857 *ppstgOpen
= &newStorage
->IStorage_iface
;
8862 /******************************************************************************
8863 * StgOpenStorageOnILockBytes [OLE32.@]
8865 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8867 IStorage
*pstgPriority
,
8871 IStorage
**ppstgOpen
)
8873 StorageBaseImpl
* newStorage
= 0;
8876 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8877 return STG_E_INVALIDPOINTER
;
8879 if ( FAILED( validateSTGM(grfMode
) ))
8880 return STG_E_INVALIDFLAG
;
8885 * Allocate and initialize the new IStorage object.
8887 hr
= Storage_Construct(
8902 *ppstgOpen
= &newStorage
->IStorage_iface
;
8907 /******************************************************************************
8908 * StgSetTimes [ole32.@]
8909 * StgSetTimes [OLE32.@]
8913 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8914 FILETIME
const *patime
, FILETIME
const *pmtime
)
8916 IStorage
*stg
= NULL
;
8919 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8921 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8925 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8926 IStorage_Release(stg
);
8932 /******************************************************************************
8933 * StgIsStorageILockBytes [OLE32.@]
8935 * Determines if the ILockBytes contains a storage object.
8937 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8939 BYTE sig
[sizeof(STORAGE_magic
)];
8940 ULARGE_INTEGER offset
;
8943 offset
.u
.HighPart
= 0;
8944 offset
.u
.LowPart
= 0;
8946 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8948 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8954 /******************************************************************************
8955 * WriteClassStg [OLE32.@]
8957 * This method will store the specified CLSID in the specified storage object
8959 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8962 return E_INVALIDARG
;
8965 return STG_E_INVALIDPOINTER
;
8967 return IStorage_SetClass(pStg
, rclsid
);
8970 /***********************************************************************
8971 * ReadClassStg (OLE32.@)
8973 * This method reads the CLSID previously written to a storage object with
8974 * the WriteClassStg.
8977 * pstg [I] IStorage pointer
8978 * pclsid [O] Pointer to where the CLSID is written
8982 * Failure: HRESULT code.
8984 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8989 TRACE("(%p, %p)\n", pstg
, pclsid
);
8991 if(!pstg
|| !pclsid
)
8992 return E_INVALIDARG
;
8995 * read a STATSTG structure (contains the clsid) from the storage
8997 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
9000 *pclsid
=pstatstg
.clsid
;
9005 /***********************************************************************
9006 * OleLoadFromStream (OLE32.@)
9008 * This function loads an object from stream
9010 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
9014 LPPERSISTSTREAM xstm
;
9016 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
9018 res
=ReadClassStm(pStm
,&clsid
);
9021 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
9024 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
9026 IUnknown_Release((IUnknown
*)*ppvObj
);
9029 res
=IPersistStream_Load(xstm
,pStm
);
9030 IPersistStream_Release(xstm
);
9031 /* FIXME: all refcounts ok at this point? I think they should be:
9034 * xstm : 0 (released)
9039 /***********************************************************************
9040 * OleSaveToStream (OLE32.@)
9042 * This function saves an object with the IPersistStream interface on it
9043 * to the specified stream.
9045 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
9051 TRACE("(%p,%p)\n",pPStm
,pStm
);
9053 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
9055 if (SUCCEEDED(res
)){
9057 res
=WriteClassStm(pStm
,&clsid
);
9061 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
9064 TRACE("Finished Save\n");
9068 /*************************************************************************
9069 * STORAGE_CreateOleStream [Internal]
9071 * Creates the "\001OLE" stream in the IStorage if necessary.
9074 * storage [I] Dest storage to create the stream in
9075 * flags [I] flags to be set for newly created stream
9078 * HRESULT return value
9082 * This stream is still unknown, MS Word seems to have extra data
9083 * but since the data is stored in the OLESTREAM there should be
9084 * no need to recreate the stream. If the stream is manually
9085 * deleted it will create it with this default data.
9088 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9090 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9091 static const DWORD version_magic
= 0x02000001;
9095 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9098 struct empty_1ole_stream
{
9099 DWORD version_magic
;
9101 DWORD update_options
;
9103 DWORD mon_stream_size
;
9105 struct empty_1ole_stream stream_data
;
9107 stream_data
.version_magic
= version_magic
;
9108 stream_data
.flags
= flags
;
9109 stream_data
.update_options
= 0;
9110 stream_data
.reserved
= 0;
9111 stream_data
.mon_stream_size
= 0;
9113 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9114 IStream_Release(stream
);
9120 /* write a string to a stream, preceded by its length */
9121 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9128 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9129 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9134 str
= CoTaskMemAlloc( len
);
9135 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9136 r
= IStream_Write( stm
, str
, len
, NULL
);
9137 CoTaskMemFree( str
);
9141 /* read a string preceded by its length from a stream */
9142 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9145 DWORD len
, count
= 0;
9149 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9152 if( count
!= sizeof(len
) )
9153 return E_OUTOFMEMORY
;
9155 TRACE("%d bytes\n",len
);
9157 str
= CoTaskMemAlloc( len
);
9159 return E_OUTOFMEMORY
;
9161 r
= IStream_Read( stm
, str
, len
, &count
);
9166 CoTaskMemFree( str
);
9167 return E_OUTOFMEMORY
;
9170 TRACE("Read string %s\n",debugstr_an(str
,len
));
9172 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9173 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9176 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9179 CoTaskMemFree( str
);
9187 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9188 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9192 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9194 static const BYTE unknown1
[12] =
9195 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9196 0xFF, 0xFF, 0xFF, 0xFF};
9197 static const BYTE unknown2
[16] =
9198 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9201 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9202 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9203 debugstr_w(szProgIDName
));
9205 /* Create a CompObj stream */
9206 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9207 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9211 /* Write CompObj Structure to stream */
9212 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9214 if( SUCCEEDED( r
) )
9215 r
= WriteClassStm( pstm
, clsid
);
9217 if( SUCCEEDED( r
) )
9218 r
= STREAM_WriteString( pstm
, lpszUserType
);
9219 if( SUCCEEDED( r
) )
9220 r
= STREAM_WriteString( pstm
, szClipName
);
9221 if( SUCCEEDED( r
) )
9222 r
= STREAM_WriteString( pstm
, szProgIDName
);
9223 if( SUCCEEDED( r
) )
9224 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9226 IStream_Release( pstm
);
9231 /***********************************************************************
9232 * WriteFmtUserTypeStg (OLE32.@)
9234 HRESULT WINAPI
WriteFmtUserTypeStg(
9235 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9239 WCHAR szwClipName
[0x40];
9241 LPWSTR wstrProgID
= NULL
;
9244 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9246 /* get the clipboard format name */
9249 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9250 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9254 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9256 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9262 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9264 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9266 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9267 cf
? szwClipName
: NULL
, wstrProgID
);
9269 CoTaskMemFree(wstrProgID
);
9275 /******************************************************************************
9276 * ReadFmtUserTypeStg [OLE32.@]
9278 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9282 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9283 unsigned char unknown1
[12];
9284 unsigned char unknown2
[16];
9286 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9289 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9291 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9292 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9295 WARN("Failed to open stream r = %08x\n", r
);
9299 /* read the various parts of the structure */
9300 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9301 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9303 r
= ReadClassStm( stm
, &clsid
);
9307 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9311 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9315 r
= STREAM_ReadString( stm
, &szProgIDName
);
9319 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9320 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9323 /* ok, success... now we just need to store what we found */
9325 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9327 if( lplpszUserType
)
9329 *lplpszUserType
= szCLSIDName
;
9334 CoTaskMemFree( szCLSIDName
);
9335 CoTaskMemFree( szOleTypeName
);
9336 CoTaskMemFree( szProgIDName
);
9337 IStream_Release( stm
);
9342 /******************************************************************************
9343 * StgIsStorageFile [OLE32.@]
9344 * Verify if the file contains a storage object
9350 * S_OK if file has magic bytes as a storage object
9351 * S_FALSE if file is not storage
9354 StgIsStorageFile(LPCOLESTR fn
)
9360 TRACE("%s\n", debugstr_w(fn
));
9361 hf
= CreateFileW(fn
, GENERIC_READ
,
9362 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9363 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9365 if (hf
== INVALID_HANDLE_VALUE
)
9366 return STG_E_FILENOTFOUND
;
9368 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9370 WARN(" unable to read file\n");
9377 if (bytes_read
!= 8) {
9378 TRACE(" too short\n");
9382 if (!memcmp(magic
,STORAGE_magic
,8)) {
9387 TRACE(" -> Invalid header.\n");
9391 /***********************************************************************
9392 * WriteClassStm (OLE32.@)
9394 * Writes a CLSID to a stream.
9397 * pStm [I] Stream to write to.
9398 * rclsid [I] CLSID to write.
9402 * Failure: HRESULT code.
9404 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9406 TRACE("(%p,%p)\n",pStm
,rclsid
);
9408 if (!pStm
|| !rclsid
)
9409 return E_INVALIDARG
;
9411 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9414 /***********************************************************************
9415 * ReadClassStm (OLE32.@)
9417 * Reads a CLSID from a stream.
9420 * pStm [I] Stream to read from.
9421 * rclsid [O] CLSID to read.
9425 * Failure: HRESULT code.
9427 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9432 TRACE("(%p,%p)\n",pStm
,pclsid
);
9434 if (!pStm
|| !pclsid
)
9435 return E_INVALIDARG
;
9437 /* clear the output args */
9438 *pclsid
= CLSID_NULL
;
9440 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9445 if (nbByte
!= sizeof(CLSID
))
9446 return STG_E_READFAULT
;
9452 /************************************************************************
9453 * OleConvert Functions
9454 ***********************************************************************/
9456 #define OLESTREAM_ID 0x501
9457 #define OLESTREAM_MAX_STR_LEN 255
9459 /* OLESTREAM memory structure to use for Get and Put Routines */
9464 DWORD dwOleTypeNameLength
;
9465 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9466 CHAR
*pstrOleObjFileName
;
9467 DWORD dwOleObjFileNameLength
;
9468 DWORD dwMetaFileWidth
;
9469 DWORD dwMetaFileHeight
;
9470 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
9473 } OLECONVERT_OLESTREAM_DATA
;
9475 /* CompObj Stream structure */
9478 BYTE byUnknown1
[12];
9480 DWORD dwCLSIDNameLength
;
9481 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
9482 DWORD dwOleTypeNameLength
;
9483 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
9484 DWORD dwProgIDNameLength
;
9485 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
9486 BYTE byUnknown2
[16];
9487 } OLECONVERT_ISTORAGE_COMPOBJ
;
9489 /* Ole Presentation Stream structure */
9492 BYTE byUnknown1
[28];
9497 } OLECONVERT_ISTORAGE_OLEPRES
;
9500 /*************************************************************************
9501 * OLECONVERT_LoadOLE10 [Internal]
9503 * Loads the OLE10 STREAM to memory
9506 * pOleStream [I] The OLESTREAM
9507 * pData [I] Data Structure for the OLESTREAM Data
9511 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9512 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9515 * This function is used by OleConvertOLESTREAMToIStorage only.
9517 * Memory allocated for pData must be freed by the caller
9519 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9522 HRESULT hRes
= S_OK
;
9526 pData
->pData
= NULL
;
9527 pData
->pstrOleObjFileName
= NULL
;
9529 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9532 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9533 if(dwSize
!= sizeof(pData
->dwOleID
))
9535 hRes
= CONVERT10_E_OLESTREAM_GET
;
9537 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9539 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9550 /* Get the TypeID... more info needed for this field */
9551 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9552 if(dwSize
!= sizeof(pData
->dwTypeID
))
9554 hRes
= CONVERT10_E_OLESTREAM_GET
;
9559 if(pData
->dwTypeID
!= 0)
9561 /* Get the length of the OleTypeName */
9562 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9563 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9565 hRes
= CONVERT10_E_OLESTREAM_GET
;
9570 if(pData
->dwOleTypeNameLength
> 0)
9572 /* Get the OleTypeName */
9573 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9574 if(dwSize
!= pData
->dwOleTypeNameLength
)
9576 hRes
= CONVERT10_E_OLESTREAM_GET
;
9582 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9583 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9585 hRes
= CONVERT10_E_OLESTREAM_GET
;
9589 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9590 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9591 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9592 if(pData
->pstrOleObjFileName
)
9594 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9595 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9597 hRes
= CONVERT10_E_OLESTREAM_GET
;
9601 hRes
= CONVERT10_E_OLESTREAM_GET
;
9606 /* Get the Width of the Metafile */
9607 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9608 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9610 hRes
= CONVERT10_E_OLESTREAM_GET
;
9614 /* Get the Height of the Metafile */
9615 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9616 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9618 hRes
= CONVERT10_E_OLESTREAM_GET
;
9624 /* Get the Length of the Data */
9625 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9626 if(dwSize
!= sizeof(pData
->dwDataLength
))
9628 hRes
= CONVERT10_E_OLESTREAM_GET
;
9632 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9634 if(!bStrem1
) /* if it is a second OLE stream data */
9636 pData
->dwDataLength
-= 8;
9637 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9638 if(dwSize
!= sizeof(pData
->strUnknown
))
9640 hRes
= CONVERT10_E_OLESTREAM_GET
;
9646 if(pData
->dwDataLength
> 0)
9648 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9650 /* Get Data (ex. IStorage, Metafile, or BMP) */
9653 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9654 if(dwSize
!= pData
->dwDataLength
)
9656 hRes
= CONVERT10_E_OLESTREAM_GET
;
9661 hRes
= CONVERT10_E_OLESTREAM_GET
;
9670 /*************************************************************************
9671 * OLECONVERT_SaveOLE10 [Internal]
9673 * Saves the OLE10 STREAM From memory
9676 * pData [I] Data Structure for the OLESTREAM Data
9677 * pOleStream [I] The OLESTREAM to save
9681 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9684 * This function is used by OleConvertIStorageToOLESTREAM only.
9687 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9690 HRESULT hRes
= S_OK
;
9694 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9695 if(dwSize
!= sizeof(pData
->dwOleID
))
9697 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9702 /* Set the TypeID */
9703 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9704 if(dwSize
!= sizeof(pData
->dwTypeID
))
9706 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9710 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9712 /* Set the Length of the OleTypeName */
9713 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9714 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9716 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9721 if(pData
->dwOleTypeNameLength
> 0)
9723 /* Set the OleTypeName */
9724 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9725 if(dwSize
!= pData
->dwOleTypeNameLength
)
9727 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9734 /* Set the width of the Metafile */
9735 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9736 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9738 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9744 /* Set the height of the Metafile */
9745 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9746 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9748 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9754 /* Set the length of the Data */
9755 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9756 if(dwSize
!= sizeof(pData
->dwDataLength
))
9758 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9764 if(pData
->dwDataLength
> 0)
9766 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9767 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9768 if(dwSize
!= pData
->dwDataLength
)
9770 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9778 /*************************************************************************
9779 * OLECONVERT_GetOLE20FromOLE10[Internal]
9781 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9782 * opens it, and copies the content to the dest IStorage for
9783 * OleConvertOLESTREAMToIStorage
9787 * pDestStorage [I] The IStorage to copy the data to
9788 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9789 * nBufferLength [I] The size of the buffer
9798 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9802 IStorage
*pTempStorage
;
9803 DWORD dwNumOfBytesWritten
;
9804 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9805 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9807 /* Create a temp File */
9808 GetTempPathW(MAX_PATH
, wstrTempDir
);
9809 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9810 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9812 if(hFile
!= INVALID_HANDLE_VALUE
)
9814 /* Write IStorage Data to File */
9815 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9818 /* Open and copy temp storage to the Dest Storage */
9819 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9822 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9823 IStorage_Release(pTempStorage
);
9825 DeleteFileW(wstrTempFile
);
9830 /*************************************************************************
9831 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9833 * Saves the OLE10 STREAM From memory
9836 * pStorage [I] The Src IStorage to copy
9837 * pData [I] The Dest Memory to write to.
9840 * The size in bytes allocated for pData
9843 * Memory allocated for pData must be freed by the caller
9845 * Used by OleConvertIStorageToOLESTREAM only.
9848 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9852 DWORD nDataLength
= 0;
9853 IStorage
*pTempStorage
;
9854 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9855 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9859 /* Create temp Storage */
9860 GetTempPathW(MAX_PATH
, wstrTempDir
);
9861 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9862 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9866 /* Copy Src Storage to the Temp Storage */
9867 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9868 IStorage_Release(pTempStorage
);
9870 /* Open Temp Storage as a file and copy to memory */
9871 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9872 if(hFile
!= INVALID_HANDLE_VALUE
)
9874 nDataLength
= GetFileSize(hFile
, NULL
);
9875 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9876 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9879 DeleteFileW(wstrTempFile
);
9884 /*************************************************************************
9885 * OLECONVERT_CreateCompObjStream [Internal]
9887 * Creates a "\001CompObj" is the destination IStorage if necessary.
9890 * pStorage [I] The dest IStorage to create the CompObj Stream
9892 * strOleTypeName [I] The ProgID
9896 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9899 * This function is used by OleConvertOLESTREAMToIStorage only.
9901 * The stream data is stored in the OLESTREAM and there should be
9902 * no need to recreate the stream. If the stream is manually
9903 * deleted it will attempt to create it by querying the registry.
9907 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9910 HRESULT hStorageRes
, hRes
= S_OK
;
9911 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9912 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9913 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9915 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9916 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9918 /* Initialize the CompObj structure */
9919 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9920 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9921 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9924 /* Create a CompObj stream if it doesn't exist */
9925 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9926 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9927 if(hStorageRes
== S_OK
)
9929 /* copy the OleTypeName to the compobj struct */
9930 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9931 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9933 /* copy the OleTypeName to the compobj struct */
9934 /* Note: in the test made, these were Identical */
9935 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9936 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9939 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9940 bufferW
, OLESTREAM_MAX_STR_LEN
);
9941 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9947 /* Get the CLSID Default Name from the Registry */
9948 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9949 if(hErr
== ERROR_SUCCESS
)
9951 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9952 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9953 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9954 if(hErr
== ERROR_SUCCESS
)
9956 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9962 /* Write CompObj Structure to stream */
9963 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9965 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9967 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9968 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9970 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9972 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9973 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9975 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9977 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9978 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9980 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9982 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9983 IStream_Release(pStream
);
9989 /*************************************************************************
9990 * OLECONVERT_CreateOlePresStream[Internal]
9992 * Creates the "\002OlePres000" Stream with the Metafile data
9995 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9996 * dwExtentX [I] Width of the Metafile
9997 * dwExtentY [I] Height of the Metafile
9998 * pData [I] Metafile data
9999 * dwDataLength [I] Size of the Metafile data
10003 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10006 * This function is used by OleConvertOLESTREAMToIStorage only.
10009 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
10013 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10014 BYTE pOlePresStreamHeader
[] =
10016 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10017 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10018 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10019 0x00, 0x00, 0x00, 0x00
10022 BYTE pOlePresStreamHeaderEmpty
[] =
10024 0x00, 0x00, 0x00, 0x00,
10025 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10026 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10027 0x00, 0x00, 0x00, 0x00
10030 /* Create the OlePres000 Stream */
10031 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10032 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10037 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
10039 memset(&OlePres
, 0, sizeof(OlePres
));
10040 /* Do we have any metafile data to save */
10041 if(dwDataLength
> 0)
10043 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
10044 nHeaderSize
= sizeof(pOlePresStreamHeader
);
10048 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
10049 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
10051 /* Set width and height of the metafile */
10052 OlePres
.dwExtentX
= dwExtentX
;
10053 OlePres
.dwExtentY
= -dwExtentY
;
10055 /* Set Data and Length */
10056 if(dwDataLength
> sizeof(METAFILEPICT16
))
10058 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
10059 OlePres
.pData
= &(pData
[8]);
10061 /* Save OlePres000 Data to Stream */
10062 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
10063 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
10064 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
10065 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
10066 if(OlePres
.dwSize
> 0)
10068 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
10070 IStream_Release(pStream
);
10074 /*************************************************************************
10075 * OLECONVERT_CreateOle10NativeStream [Internal]
10077 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10080 * pStorage [I] Dest storage to create the stream in
10081 * pData [I] Ole10 Native Data (ex. bmp)
10082 * dwDataLength [I] Size of the Ole10 Native Data
10088 * This function is used by OleConvertOLESTREAMToIStorage only.
10090 * Might need to verify the data and return appropriate error message
10093 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
10097 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10099 /* Create the Ole10Native Stream */
10100 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
10101 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
10105 /* Write info to stream */
10106 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
10107 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
10108 IStream_Release(pStream
);
10113 /*************************************************************************
10114 * OLECONVERT_GetOLE10ProgID [Internal]
10116 * Finds the ProgID (or OleTypeID) from the IStorage
10119 * pStorage [I] The Src IStorage to get the ProgID
10120 * strProgID [I] the ProgID string to get
10121 * dwSize [I] the size of the string
10125 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10128 * This function is used by OleConvertIStorageToOLESTREAM only.
10132 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
10136 LARGE_INTEGER iSeekPos
;
10137 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
10138 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10140 /* Open the CompObj Stream */
10141 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10142 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10146 /*Get the OleType from the CompObj Stream */
10147 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10148 iSeekPos
.u
.HighPart
= 0;
10150 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10151 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10152 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10153 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10154 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10155 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10156 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10158 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10161 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10163 IStream_Release(pStream
);
10168 LPOLESTR wstrProgID
;
10170 /* Get the OleType from the registry */
10171 REFCLSID clsid
= &(stat
.clsid
);
10172 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10173 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10176 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10177 CoTaskMemFree(wstrProgID
);
10184 /*************************************************************************
10185 * OLECONVERT_GetOle10PresData [Internal]
10187 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10190 * pStorage [I] Src IStroage
10191 * pOleStream [I] Dest OleStream Mem Struct
10197 * This function is used by OleConvertIStorageToOLESTREAM only.
10199 * Memory allocated for pData must be freed by the caller
10203 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10208 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10210 /* Initialize Default data for OLESTREAM */
10211 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10212 pOleStreamData
[0].dwTypeID
= 2;
10213 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10214 pOleStreamData
[1].dwTypeID
= 0;
10215 pOleStreamData
[0].dwMetaFileWidth
= 0;
10216 pOleStreamData
[0].dwMetaFileHeight
= 0;
10217 pOleStreamData
[0].pData
= NULL
;
10218 pOleStreamData
[1].pData
= NULL
;
10220 /* Open Ole10Native Stream */
10221 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10222 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10226 /* Read Size and Data */
10227 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10228 if(pOleStreamData
->dwDataLength
> 0)
10230 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10231 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10233 IStream_Release(pStream
);
10239 /*************************************************************************
10240 * OLECONVERT_GetOle20PresData[Internal]
10242 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10245 * pStorage [I] Src IStroage
10246 * pOleStreamData [I] Dest OleStream Mem Struct
10252 * This function is used by OleConvertIStorageToOLESTREAM only.
10254 * Memory allocated for pData must be freed by the caller
10256 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10260 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10261 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10263 /* Initialize Default data for OLESTREAM */
10264 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10265 pOleStreamData
[0].dwTypeID
= 2;
10266 pOleStreamData
[0].dwMetaFileWidth
= 0;
10267 pOleStreamData
[0].dwMetaFileHeight
= 0;
10268 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10269 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10270 pOleStreamData
[1].dwTypeID
= 0;
10271 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10272 pOleStreamData
[1].strOleTypeName
[0] = 0;
10273 pOleStreamData
[1].dwMetaFileWidth
= 0;
10274 pOleStreamData
[1].dwMetaFileHeight
= 0;
10275 pOleStreamData
[1].pData
= NULL
;
10276 pOleStreamData
[1].dwDataLength
= 0;
10279 /* Open OlePress000 stream */
10280 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10281 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10284 LARGE_INTEGER iSeekPos
;
10285 METAFILEPICT16 MetaFilePict
;
10286 static const char strMetafilePictName
[] = "METAFILEPICT";
10288 /* Set the TypeID for a Metafile */
10289 pOleStreamData
[1].dwTypeID
= 5;
10291 /* Set the OleTypeName to Metafile */
10292 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10293 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10295 iSeekPos
.u
.HighPart
= 0;
10296 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10298 /* Get Presentation Data */
10299 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10300 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10301 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10302 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10304 /*Set width and Height */
10305 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10306 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10307 if(olePress
.dwSize
> 0)
10310 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10312 /* Set MetaFilePict struct */
10313 MetaFilePict
.mm
= 8;
10314 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10315 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10316 MetaFilePict
.hMF
= 0;
10318 /* Get Metafile Data */
10319 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10320 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10321 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10323 IStream_Release(pStream
);
10327 /*************************************************************************
10328 * OleConvertOLESTREAMToIStorage [OLE32.@]
10330 * Read info on MSDN
10333 * DVTARGETDEVICE parameter is not handled
10334 * Still unsure of some mem fields for OLE 10 Stream
10335 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10336 * and "\001OLE" streams
10339 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10340 LPOLESTREAM pOleStream
,
10342 const DVTARGETDEVICE
* ptd
)
10346 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10348 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10350 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10354 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10357 if(pstg
== NULL
|| pOleStream
== NULL
)
10359 hRes
= E_INVALIDARG
;
10364 /* Load the OLESTREAM to Memory */
10365 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10370 /* Load the OLESTREAM to Memory (part 2)*/
10371 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10377 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10379 /* Do we have the IStorage Data in the OLESTREAM */
10380 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10382 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10383 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10387 /* It must be an original OLE 1.0 source */
10388 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10393 /* It must be an original OLE 1.0 source */
10394 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10397 /* Create CompObj Stream if necessary */
10398 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10401 /*Create the Ole Stream if necessary */
10402 STORAGE_CreateOleStream(pstg
, 0);
10407 /* Free allocated memory */
10408 for(i
=0; i
< 2; i
++)
10410 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10411 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10412 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10417 /*************************************************************************
10418 * OleConvertIStorageToOLESTREAM [OLE32.@]
10420 * Read info on MSDN
10422 * Read info on MSDN
10425 * Still unsure of some mem fields for OLE 10 Stream
10426 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10427 * and "\001OLE" streams.
10430 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10432 LPOLESTREAM pOleStream
)
10435 HRESULT hRes
= S_OK
;
10437 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10438 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10440 TRACE("%p %p\n", pstg
, pOleStream
);
10442 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10444 if(pstg
== NULL
|| pOleStream
== NULL
)
10446 hRes
= E_INVALIDARG
;
10450 /* Get the ProgID */
10451 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10452 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10456 /* Was it originally Ole10 */
10457 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10460 IStream_Release(pStream
);
10461 /* Get Presentation Data for Ole10Native */
10462 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10466 /* Get Presentation Data (OLE20) */
10467 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10470 /* Save OLESTREAM */
10471 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10474 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10479 /* Free allocated memory */
10480 for(i
=0; i
< 2; i
++)
10482 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10488 enum stream_1ole_flags
{
10489 OleStream_LinkedObject
= 0x00000001,
10490 OleStream_Convert
= 0x00000004
10493 /***********************************************************************
10494 * GetConvertStg (OLE32.@)
10496 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10498 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10499 static const DWORD version_magic
= 0x02000001;
10504 TRACE("%p\n", stg
);
10506 if (!stg
) return E_INVALIDARG
;
10508 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10509 if (FAILED(hr
)) return hr
;
10511 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10512 IStream_Release(stream
);
10513 if (FAILED(hr
)) return hr
;
10515 if (header
[0] != version_magic
)
10517 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10521 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10524 /***********************************************************************
10525 * SetConvertStg (OLE32.@)
10527 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10529 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10530 DWORD flags
= convert
? OleStream_Convert
: 0;
10535 TRACE("(%p, %d)\n", storage
, convert
);
10537 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10540 if (hr
!= STG_E_FILENOTFOUND
)
10543 return STORAGE_CreateOleStream(storage
, flags
);
10546 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10549 IStream_Release(stream
);
10553 /* update flag if differs */
10554 if ((header
[1] ^ flags
) & OleStream_Convert
)
10556 LARGE_INTEGER pos
= {{0}};
10558 if (header
[1] & OleStream_Convert
)
10559 flags
= header
[1] & ~OleStream_Convert
;
10561 flags
= header
[1] | OleStream_Convert
;
10563 pos
.QuadPart
= sizeof(DWORD
);
10564 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10567 IStream_Release(stream
);
10571 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10574 IStream_Release(stream
);