fltmgr.sys: Add a stub driver.
[wine.git] / dlls / ole32 / storage32.c
blobbfef4a23d7c3a608097d6dde88cdee999dcd2bf9
1 /*
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
27 * NOTES
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winnls.h"
45 #include "winuser.h"
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
49 #include "storage32.h"
50 #include "ole2.h" /* For Write/ReadClassStm */
52 #include "winreg.h"
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. */
97 BOOL inuse;
99 /* True if data is up to date. */
100 BOOL read;
102 /* True if this entry has been modified. */
103 BOOL dirty;
105 /* True if this entry's stream has been modified. */
106 BOOL stream_dirty;
108 /* True if this entry has been deleted in the transacted storage, but the
109 * delete has not yet been committed. */
110 BOOL deleted;
112 /* If this entry's stream has been modified, a reference to where the stream
113 * is stored in the snapshot file. */
114 DirRef stream_entry;
116 /* This directory entry's data, including any changes that have been made. */
117 DirEntry data;
119 /* A reference to the parent of this node. This is only valid while we are
120 * committing changes. */
121 DirRef parent;
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;
144 ULONG entries_size;
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.
185 struct BlockChainRun
187 /* This represents a range of blocks that happen reside in consecutive sectors. */
188 ULONG firstSector;
189 ULONG firstOffset;
190 ULONG lastOffset;
193 typedef struct BlockChainBlock
195 ULONG index;
196 ULONG sector;
197 BOOL read;
198 BOOL dirty;
199 BYTE data[MAX_BIG_BLOCK_SIZE];
200 } BlockChainBlock;
202 struct BlockChainStream
204 StorageImpl* parentStorage;
205 ULONG* headOfStreamPlaceHolder;
206 DirRef ownerDirEntry;
207 struct BlockChainRun* indexCache;
208 ULONG indexCacheLen;
209 ULONG indexCacheSize;
210 BlockChainBlock cachedBlocks[2];
211 ULONG blockToEvict;
212 ULONG tailIndex;
213 ULONG numBlocks;
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 /************************************************************************
256 * STGM Functions
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);
297 return E_FAIL;
300 switch (access)
302 case STGM_READ:
303 case STGM_WRITE:
304 case STGM_READWRITE:
305 break;
306 default:
307 return E_FAIL;
310 switch (share)
312 case STGM_SHARE_DENY_NONE:
313 case STGM_SHARE_DENY_READ:
314 case STGM_SHARE_DENY_WRITE:
315 case STGM_SHARE_EXCLUSIVE:
316 break;
317 case 0:
318 if (!(stgm & STGM_TRANSACTED))
319 return E_FAIL;
320 break;
321 default:
322 return E_FAIL;
325 switch (create)
327 case STGM_CREATE:
328 case STGM_FAILIFTHERE:
329 break;
330 default:
331 return E_FAIL;
335 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
337 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
338 return E_FAIL;
341 * STGM_CREATE | STGM_CONVERT
342 * if both are false, STGM_FAILIFTHERE is set to TRUE
344 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
345 return E_FAIL;
348 * STGM_NOSCRATCH requires STGM_TRANSACTED
350 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
351 return E_FAIL;
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) )
361 return E_FAIL;
363 return S_OK;
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))
376 case 0:
377 assert(stgm & STGM_TRANSACTED);
378 /* fall-through */
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");
388 assert(0);
389 return 0;
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))
402 case STGM_READ:
403 return GENERIC_READ;
404 case STGM_WRITE:
405 case STGM_READWRITE:
406 return GENERIC_READ | GENERIC_WRITE;
408 ERR("Invalid access mode!\n");
409 assert(0);
410 return 0;
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))
423 case STGM_CREATE:
424 return CREATE_ALWAYS;
425 case STGM_CONVERT:
426 FIXME("STGM_CONVERT not implemented!\n");
427 return CREATE_NEW;
428 case STGM_FAILIFTHERE:
429 return CREATE_NEW;
431 ERR("Invalid create mode!\n");
432 assert(0);
433 return 0;
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);
468 return E_NOTIMPL;
471 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
473 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
474 FIXME("(%p): stub\n", This);
475 return E_NOTIMPL;
478 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
480 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
481 FIXME("(%p): stub\n", This);
482 return E_NOTIMPL;
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 /****************************************************************************
502 * Internal Method
504 * Case insensitive comparison of DirEntry.name by first considering
505 * their size.
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++);
525 return diff;
528 /****************************************************************************
530 * Internal Method
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)
537 DirRef currentEntry;
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)
546 LONG cmp;
548 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
550 cmp = entryNameCmp(name, data->name);
552 if (cmp == 0)
553 /* found it */
554 break;
556 else if (cmp < 0)
557 currentEntry = data->leftChild;
559 else if (cmp > 0)
560 currentEntry = data->rightChild;
563 return currentEntry;
566 /****************************************************************************
568 * Internal Method
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,
577 ULONG *relation)
579 DirRef childEntry;
580 DirEntry childData;
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)
592 LONG cmp;
594 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
596 cmp = entryNameCmp(childName, childData.name);
598 if (cmp == 0)
599 /* found it */
600 break;
602 else if (cmp < 0)
604 *parentData = childData;
605 *parentEntry = childEntry;
606 *relation = DIRENTRY_RELATION_PREVIOUS;
608 childEntry = parentData->leftChild;
611 else if (cmp > 0)
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;
623 else
624 return S_OK;
627 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
629 switch (relation)
631 case DIRENTRY_RELATION_PREVIOUS:
632 entry->leftChild = new_target;
633 break;
634 case DIRENTRY_RELATION_NEXT:
635 entry->rightChild = new_target;
636 break;
637 case DIRENTRY_RELATION_DIR:
638 entry->dirRootEntry = new_target;
639 break;
640 default:
641 assert(0);
645 /****************************************************************************
647 * Internal Method
649 * Add a directory entry to a storage
651 static HRESULT insertIntoTree(
652 StorageBaseImpl *This,
653 DirRef parentStorageIndex,
654 DirRef newEntryIndex)
656 DirEntry currentEntry;
657 DirEntry newEntry;
660 * Read the inserted entry
662 StorageBaseImpl_ReadDirEntry(This,
663 newEntryIndex,
664 &newEntry);
667 * Read the storage entry
669 StorageBaseImpl_ReadDirEntry(This,
670 parentStorageIndex,
671 &currentEntry);
673 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
676 * The root storage contains some element, therefore, start the research
677 * for the appropriate location.
679 BOOL found = FALSE;
680 DirRef current, next, previous, currentEntryId;
683 * Keep a reference to the root of the storage's element tree
685 currentEntryId = currentEntry.dirRootEntry;
688 * Read
690 StorageBaseImpl_ReadDirEntry(This,
691 currentEntry.dirRootEntry,
692 &currentEntry);
694 previous = currentEntry.leftChild;
695 next = currentEntry.rightChild;
696 current = currentEntryId;
698 while (!found)
700 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
702 if (diff < 0)
704 if (previous != DIRENTRY_NULL)
706 StorageBaseImpl_ReadDirEntry(This,
707 previous,
708 &currentEntry);
709 current = previous;
711 else
713 currentEntry.leftChild = newEntryIndex;
714 StorageBaseImpl_WriteDirEntry(This,
715 current,
716 &currentEntry);
717 found = TRUE;
720 else if (diff > 0)
722 if (next != DIRENTRY_NULL)
724 StorageBaseImpl_ReadDirEntry(This,
725 next,
726 &currentEntry);
727 current = next;
729 else
731 currentEntry.rightChild = newEntryIndex;
732 StorageBaseImpl_WriteDirEntry(This,
733 current,
734 &currentEntry);
735 found = TRUE;
738 else
741 * Trying to insert an item with the same name in the
742 * subtree structure.
744 return STG_E_FILEALREADYEXISTS;
747 previous = currentEntry.leftChild;
748 next = currentEntry.rightChild;
751 else
754 * The storage is empty, make the new entry the root of its element tree
756 currentEntry.dirRootEntry = newEntryIndex;
757 StorageBaseImpl_WriteDirEntry(This,
758 parentStorageIndex,
759 &currentEntry);
762 return S_OK;
765 /*************************************************************************
767 * Internal Method
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,
775 DirRef deletedIndex)
777 DirEntry entryToDelete;
778 DirEntry parentEntry;
779 DirRef parentEntryRef;
780 ULONG typeOfRelation;
781 HRESULT hr;
783 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
785 if (hr != S_OK)
786 return hr;
789 * Find the element that links to the one we want to delete.
791 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
792 &parentEntry, &parentEntryRef, &typeOfRelation);
794 if (hr != S_OK)
795 return hr;
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(
805 This,
806 parentEntryRef,
807 &parentEntry);
808 if(FAILED(hr))
810 return hr;
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(
826 This,
827 newRightChildParent,
828 &newRightChildParentEntry);
829 if (FAILED(hr))
831 return hr;
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(
841 This,
842 newRightChildParent,
843 &newRightChildParentEntry);
844 if (FAILED(hr))
846 return hr;
850 else
853 * Replace the deleted entry with its right child
855 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
857 hr = StorageBaseImpl_WriteDirEntry(
858 This,
859 parentEntryRef,
860 &parentEntry);
861 if(FAILED(hr))
863 return hr;
867 return hr;
871 /************************************************************************
872 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
873 ***********************************************************************/
876 * IEnumSTATSTGImpl definitions.
878 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
879 * This class allows iterating through the content of a storage and finding
880 * specific items inside it.
882 struct IEnumSTATSTGImpl
884 IEnumSTATSTG IEnumSTATSTG_iface;
886 LONG ref; /* Reference count */
887 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
888 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
890 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
893 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
895 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
898 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
900 IStorage_Release(&This->parentStorage->IStorage_iface);
901 HeapFree(GetProcessHeap(), 0, This);
904 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
905 IEnumSTATSTG* iface,
906 REFIID riid,
907 void** ppvObject)
909 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
911 if (ppvObject==0)
912 return E_INVALIDARG;
914 *ppvObject = 0;
916 if (IsEqualGUID(&IID_IUnknown, riid) ||
917 IsEqualGUID(&IID_IEnumSTATSTG, riid))
919 *ppvObject = &This->IEnumSTATSTG_iface;
920 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
921 return S_OK;
924 return E_NOINTERFACE;
927 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
928 IEnumSTATSTG* iface)
930 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
931 return InterlockedIncrement(&This->ref);
934 static ULONG WINAPI IEnumSTATSTGImpl_Release(
935 IEnumSTATSTG* iface)
937 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
939 ULONG newRef;
941 newRef = InterlockedDecrement(&This->ref);
943 if (newRef==0)
945 IEnumSTATSTGImpl_Destroy(This);
948 return newRef;
951 static HRESULT IEnumSTATSTGImpl_GetNextRef(
952 IEnumSTATSTGImpl* This,
953 DirRef *ref)
955 DirRef result = DIRENTRY_NULL;
956 DirRef searchNode;
957 DirEntry entry;
958 HRESULT hr;
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);
969 if (SUCCEEDED(hr))
971 LONG diff = entryNameCmp( entry.name, This->name);
973 if (diff <= 0)
975 searchNode = entry.rightChild;
977 else
979 result = searchNode;
980 memcpy(result_name, entry.name, sizeof(result_name));
981 searchNode = entry.leftChild;
986 if (SUCCEEDED(hr))
988 *ref = result;
989 if (result != DIRENTRY_NULL)
990 memcpy(This->name, result_name, sizeof(result_name));
993 return hr;
996 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
997 IEnumSTATSTG* iface,
998 ULONG celt,
999 STATSTG* rgelt,
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;
1008 HRESULT hr=S_OK;
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
1027 *pceltFetched = 0;
1029 while ( *pceltFetched < celt )
1031 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1033 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1034 break;
1037 * Read the entry from the storage.
1039 StorageBaseImpl_ReadDirEntry(This->parentStorage,
1040 currentSearchNode,
1041 &currentEntry);
1044 * Copy the information to the return buffer.
1046 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1047 currentReturnStruct,
1048 &currentEntry,
1049 STATFLAG_DEFAULT);
1052 * Step to the next item in the iteration
1054 (*pceltFetched)++;
1055 currentReturnStruct++;
1058 if (SUCCEEDED(hr) && *pceltFetched != celt)
1059 hr = S_FALSE;
1061 return hr;
1065 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1066 IEnumSTATSTG* iface,
1067 ULONG celt)
1069 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1071 ULONG objectFetched = 0;
1072 DirRef currentSearchNode;
1073 HRESULT hr=S_OK;
1075 if (This->parentStorage->reverted)
1076 return STG_E_REVERTED;
1078 while ( (objectFetched < celt) )
1080 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1082 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1083 break;
1085 objectFetched++;
1088 if (SUCCEEDED(hr) && objectFetched != celt)
1089 return S_FALSE;
1091 return hr;
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;
1102 This->name[0] = 0;
1104 return S_OK;
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;
1119 if (ppenum==0)
1120 return E_INVALIDARG;
1122 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1123 This->storageDirEntry);
1124 if (!newClone)
1126 *ppenum = NULL;
1127 return E_OUTOFMEMORY;
1131 * The new clone enumeration must point to the same current node as
1132 * the old one.
1134 memcpy(newClone->name, This->name, sizeof(newClone->name));
1136 *ppenum = &newClone->IEnumSTATSTG_iface;
1138 return S_OK;
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));
1163 if (newEnumeration)
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(
1201 IStorage* iface,
1202 REFIID riid,
1203 void** ppvObject)
1205 StorageBaseImpl *This = impl_from_IStorage(iface);
1207 if (!ppvObject)
1208 return E_INVALIDARG;
1210 *ppvObject = 0;
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;
1226 else
1227 return E_NOINTERFACE;
1229 IStorage_AddRef(iface);
1231 return S_OK;
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(
1243 IStorage* iface)
1245 StorageBaseImpl *This = impl_from_IStorage(iface);
1246 ULONG ref = InterlockedIncrement(&This->ref);
1248 TRACE("(%p) AddRef to %d\n", This, ref);
1250 return 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(
1262 IStorage* iface)
1264 StorageBaseImpl *This = impl_from_IStorage(iface);
1266 ULONG ref = InterlockedDecrement(&This->ref);
1268 TRACE("(%p) ReleaseRef to %d\n", This, ref);
1270 if (ref == 0)
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);
1280 return ref;
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)
1291 DirEntry data;
1292 HRESULT hr;
1293 BOOL skip = FALSE;
1294 IStorage *pstgTmp;
1295 IStream *pstrChild, *pstrTmp;
1296 STATSTG strStat;
1298 if (srcEntry == DIRENTRY_NULL)
1299 return S_OK;
1301 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1303 if (FAILED(hr))
1304 return hr;
1306 if ( snbExclude )
1308 WCHAR **snb = snbExclude;
1310 while ( *snb != NULL && !skip )
1312 if ( lstrcmpW(data.name, *snb) == 0 )
1313 skip = TRUE;
1314 ++snb;
1318 if (!skip)
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,
1327 0, 0,
1328 &pstgTmp );
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 );
1340 if (SUCCEEDED(hr))
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,
1356 0, 0, &pstrTmp );
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.
1362 if (hr == S_OK)
1364 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1366 if (streamimpl)
1368 pstrChild = &streamimpl->IStream_iface;
1369 if (pstrChild)
1370 IStream_AddRef(pstrChild);
1372 else
1374 pstrChild = NULL;
1375 hr = E_OUTOFMEMORY;
1379 if (hr == S_OK)
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);
1392 * do the copy
1394 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1395 NULL, NULL );
1397 IStream_Release( pstrChild );
1400 IStream_Release( pstrTmp );
1404 /* copy siblings */
1405 if (SUCCEEDED(hr))
1406 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1407 skip_stream, snbExclude, pstgDest );
1409 if (SUCCEEDED(hr))
1410 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1411 skip_stream, snbExclude, pstgDest );
1413 return hr;
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)
1424 return TRUE;
1428 return FALSE;
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)
1439 return TRUE;
1443 return FALSE;
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(
1454 IStorage* iface,
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) )
1472 res = E_INVALIDARG;
1473 goto end;
1476 *ppstm = NULL;
1478 if ( FAILED( validateSTGM(grfMode) ) ||
1479 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1481 res = STG_E_INVALIDFLAG;
1482 goto end;
1486 * As documented.
1488 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1490 res = STG_E_INVALIDFUNCTION;
1491 goto end;
1494 if (This->reverted)
1496 res = STG_E_REVERTED;
1497 goto end;
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;
1508 goto end;
1513 * Search for the element with the given name
1515 streamEntryRef = findElement(
1516 This,
1517 This->storageDirEntry,
1518 pwcsName,
1519 &currentEntry);
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;
1531 goto end;
1534 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1536 if (newStream)
1538 newStream->grfMode = grfMode;
1539 *ppstm = &newStream->IStream_iface;
1541 IStream_AddRef(*ppstm);
1543 res = S_OK;
1544 goto end;
1547 res = E_OUTOFMEMORY;
1548 goto end;
1551 res = STG_E_FILENOTFOUND;
1553 end:
1554 if (res == S_OK)
1555 TRACE("<-- IStream %p\n", *ppstm);
1556 TRACE("<-- %08x\n", res);
1557 return 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(
1568 IStorage* iface,
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) )
1589 res = E_INVALIDARG;
1590 goto end;
1593 if (This->openFlags & STGM_SIMPLE)
1595 res = STG_E_INVALIDFUNCTION;
1596 goto end;
1599 /* as documented */
1600 if (snbExclude != NULL)
1602 res = STG_E_INVALIDPARAMETER;
1603 goto end;
1606 if ( FAILED( validateSTGM(grfMode) ))
1608 res = STG_E_INVALIDFLAG;
1609 goto end;
1613 * As documented.
1615 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1616 (grfMode & STGM_DELETEONRELEASE) ||
1617 (grfMode & STGM_PRIORITY) )
1619 res = STG_E_INVALIDFUNCTION;
1620 goto end;
1623 if (This->reverted)
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;
1634 goto end;
1638 *ppstg = NULL;
1640 storageEntryRef = findElement(
1641 This,
1642 This->storageDirEntry,
1643 pwcsName,
1644 &currentEntry);
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;
1653 goto end;
1656 newStorage = StorageInternalImpl_Construct(
1657 This,
1658 grfMode,
1659 storageEntryRef);
1661 if (newStorage != 0)
1663 if (grfMode & STGM_TRANSACTED)
1665 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1667 if (FAILED(res))
1669 HeapFree(GetProcessHeap(), 0, newStorage);
1670 goto end;
1673 *ppstg = &newTransactedStorage->IStorage_iface;
1675 else
1677 *ppstg = &newStorage->base.IStorage_iface;
1680 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1682 res = S_OK;
1683 goto end;
1686 res = STG_E_INSUFFICIENTMEMORY;
1687 goto end;
1690 res = STG_E_FILENOTFOUND;
1692 end:
1693 TRACE("<-- %08x\n", res);
1694 return 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(
1706 IStorage* iface,
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);
1718 if (!ppenum)
1719 return E_INVALIDARG;
1721 if (This->reverted)
1722 return STG_E_REVERTED;
1724 newEnum = IEnumSTATSTGImpl_Construct(
1725 This,
1726 This->storageDirEntry);
1728 if (newEnum)
1730 *ppenum = &newEnum->IEnumSTATSTG_iface;
1731 return S_OK;
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(
1745 IStorage* iface,
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);
1756 if (!pstatstg)
1758 res = E_INVALIDARG;
1759 goto end;
1762 if (This->reverted)
1764 res = STG_E_REVERTED;
1765 goto end;
1768 res = StorageBaseImpl_ReadDirEntry(
1769 This,
1770 This->storageDirEntry,
1771 &currentEntry);
1773 if (SUCCEEDED(res))
1775 StorageUtl_CopyDirEntryToSTATSTG(
1776 This,
1777 pstatstg,
1778 &currentEntry,
1779 grfStatFlag);
1781 pstatstg->grfMode = This->openFlags;
1782 pstatstg->grfStateBits = This->stateBits;
1785 end:
1786 if (res == S_OK)
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);
1791 return 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(
1802 IStorage* iface,
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));
1813 if (This->reverted)
1814 return STG_E_REVERTED;
1816 currentEntryRef = findElement(This,
1817 This->storageDirEntry,
1818 pwcsNewName,
1819 &currentEntry);
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,
1834 pwcsOldName,
1835 &currentEntry);
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,
1848 currentEntryRef);
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,
1858 &currentEntry);
1860 /* Insert the element in a new position in the tree */
1861 insertIntoTree(This, This->storageDirEntry,
1862 currentEntryRef);
1864 else
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(
1883 IStorage* iface,
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;
1894 HRESULT hr;
1896 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1897 iface, debugstr_w(pwcsName), grfMode,
1898 reserved1, reserved2, ppstm);
1900 if (ppstm == 0)
1901 return STG_E_INVALIDPOINTER;
1903 if (pwcsName == 0)
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;
1915 if (This->reverted)
1916 return STG_E_REVERTED;
1919 * As documented.
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;
1942 *ppstm = 0;
1944 currentEntryRef = findElement(This,
1945 This->storageDirEntry,
1946 pwcsName,
1947 &currentEntry);
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);
1958 else
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);
1995 if (FAILED(hr))
1996 return hr;
1999 * Insert the new entry in the parent storage's tree.
2001 hr = insertIntoTree(
2002 This,
2003 This->storageDirEntry,
2004 newStreamEntryRef);
2005 if (FAILED(hr))
2007 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2008 return hr;
2012 * Open the stream to return it.
2014 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2016 if (newStream)
2018 *ppstm = &newStream->IStream_iface;
2019 IStream_AddRef(*ppstm);
2021 else
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
2033 * storage.
2035 * See Windows documentation for more details on IStorage methods.
2037 static HRESULT WINAPI StorageBaseImpl_SetClass(
2038 IStorage* iface,
2039 REFCLSID clsid) /* [in] */
2041 StorageBaseImpl *This = impl_from_IStorage(iface);
2042 HRESULT hRes;
2043 DirEntry currentEntry;
2045 TRACE("(%p, %p)\n", iface, clsid);
2047 if (This->reverted)
2048 return STG_E_REVERTED;
2050 hRes = StorageBaseImpl_ReadDirEntry(This,
2051 This->storageDirEntry,
2052 &currentEntry);
2053 if (SUCCEEDED(hRes))
2055 currentEntry.clsid = *clsid;
2057 hRes = StorageBaseImpl_WriteDirEntry(This,
2058 This->storageDirEntry,
2059 &currentEntry);
2062 if (SUCCEEDED(hRes))
2063 hRes = StorageBaseImpl_Flush(This);
2065 return hRes;
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(
2076 IStorage* iface,
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;
2086 DirEntry newEntry;
2087 DirRef currentEntryRef;
2088 DirRef newEntryRef;
2089 HRESULT hr;
2091 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2092 iface, debugstr_w(pwcsName), grfMode,
2093 reserved1, reserved2, ppstg);
2095 if (ppstg == 0)
2096 return STG_E_INVALIDPOINTER;
2098 if (This->openFlags & STGM_SIMPLE)
2100 return STG_E_INVALIDFUNCTION;
2103 if (pwcsName == 0)
2104 return STG_E_INVALIDNAME;
2106 *ppstg = NULL;
2108 if ( FAILED( validateSTGM(grfMode) ) ||
2109 (grfMode & STGM_DELETEONRELEASE) )
2111 WARN("bad grfMode: 0x%x\n", grfMode);
2112 return STG_E_INVALIDFLAG;
2115 if (This->reverted)
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,
2130 pwcsName,
2131 &currentEntry);
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);
2143 if (FAILED(hr))
2144 return hr;
2146 else
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
2181 newEntry.ctime
2182 newEntry.mtime
2185 /* newEntry.clsid */
2188 * Create a new directory entry for the storage
2190 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2191 if (FAILED(hr))
2192 return hr;
2195 * Insert the new directory entry into the parent storage's tree
2197 hr = insertIntoTree(
2198 This,
2199 This->storageDirEntry,
2200 newEntryRef);
2201 if (FAILED(hr))
2203 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2204 return hr;
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))
2214 return hr;
2217 if (SUCCEEDED(hr))
2218 hr = StorageBaseImpl_Flush(This);
2220 return S_OK;
2223 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2224 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2225 SNB snbExclude, IStorage *pstgDest)
2227 DirEntry data;
2228 HRESULT hr;
2230 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2232 if (SUCCEEDED(hr))
2233 hr = IStorage_SetClass( pstgDest, &data.clsid );
2235 if (SUCCEEDED(hr))
2236 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2237 skip_stream, snbExclude, pstgDest );
2239 return hr;
2242 /*************************************************************************
2243 * CopyTo (IStorage)
2245 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2246 IStorage* iface,
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;
2255 DWORD i;
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]))
2269 skip_stream = TRUE;
2270 else
2271 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2274 if (!skip_storage)
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;
2298 else
2299 break;
2302 if (pstgDestAncestor == iface)
2304 BOOL fail = TRUE;
2306 if (pstgDestAncestorChild && snbExclude)
2308 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2309 DirEntry data;
2310 WCHAR **snb = snbExclude;
2312 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2314 while ( *snb != NULL && fail )
2316 if ( lstrcmpW(data.name, *snb) == 0 )
2317 fail = FALSE;
2318 ++snb;
2322 if (fail)
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(
2335 IStorage* iface,
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);
2344 return E_NOTIMPL;
2347 /*************************************************************************
2348 * Commit (IStorage)
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(
2356 IStorage* iface,
2357 DWORD grfCommitFlags)/* [in] */
2359 StorageBaseImpl* This = impl_from_IStorage(iface);
2360 TRACE("(%p %d)\n", iface, grfCommitFlags);
2361 return StorageBaseImpl_Flush(This);
2364 /*************************************************************************
2365 * Revert (IStorage)
2367 * Discard all changes that have been made since the last commit operation
2369 static HRESULT WINAPI StorageBaseImpl_Revert(
2370 IStorage* iface)
2372 TRACE("(%p)\n", iface);
2373 return S_OK;
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;
2391 HRESULT hr;
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,
2414 &childStorage);
2416 if (hr != S_OK)
2418 return hr;
2422 * Enumerate the elements
2424 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2425 if (FAILED(hr))
2427 IStorage_Release(childStorage);
2428 return hr;
2434 * Obtain the next element
2436 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2437 if (hr==S_OK)
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);
2455 return destroyHr;
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)
2470 IStream *pis;
2471 HRESULT hr;
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;
2487 size.u.LowPart = 0;
2489 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2490 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2492 if (hr!=S_OK)
2494 return(hr);
2498 * Zap the stream
2500 hr = IStream_SetSize(pis, size);
2502 if(hr != S_OK)
2504 return hr;
2508 * Release the stream object.
2510 IStream_Release(pis);
2512 return S_OK;
2515 /*************************************************************************
2516 * DestroyElement (IStorage)
2518 * Strategy: This implementation is built this way for simplicity not for speed.
2519 * I always delete the topmost element of the enumeration and adjust
2520 * the deleted element pointer all the time. This takes longer to
2521 * do but allows reinvoking DestroyElement whenever we encounter a
2522 * storage object. The optimisation resides in the usage of another
2523 * enumeration strategy that would give all the leaves of a storage
2524 * first. (postfix order)
2526 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2527 IStorage* iface,
2528 const OLECHAR *pwcsName)/* [string][in] */
2530 StorageBaseImpl *This = impl_from_IStorage(iface);
2532 HRESULT hr = S_OK;
2533 DirEntry entryToDelete;
2534 DirRef entryToDeleteRef;
2536 TRACE("(%p, %s)\n",
2537 iface, debugstr_w(pwcsName));
2539 if (pwcsName==NULL)
2540 return STG_E_INVALIDPOINTER;
2542 if (This->reverted)
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(
2550 This,
2551 This->storageDirEntry,
2552 pwcsName,
2553 &entryToDelete);
2555 if ( entryToDeleteRef == DIRENTRY_NULL )
2557 return STG_E_FILENOTFOUND;
2560 if ( entryToDelete.stgType == STGTY_STORAGE )
2562 hr = deleteStorageContents(
2563 This,
2564 entryToDeleteRef,
2565 entryToDelete);
2567 else if ( entryToDelete.stgType == STGTY_STREAM )
2569 hr = deleteStreamContents(
2570 This,
2571 entryToDeleteRef,
2572 entryToDelete);
2575 if (hr!=S_OK)
2576 return hr;
2579 * Remove the entry from its parent storage
2581 hr = removeFromTree(
2582 This,
2583 This->storageDirEntry,
2584 entryToDeleteRef);
2587 * Invalidate the entry
2589 if (SUCCEEDED(hr))
2590 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2592 if (SUCCEEDED(hr))
2593 hr = StorageBaseImpl_Flush(This);
2595 return hr;
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;
2608 list_remove(cur);
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(
2628 IStorage* iface,
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));
2635 return S_OK;
2638 /******************************************************************************
2639 * SetStateBits (IStorage)
2641 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2642 IStorage* iface,
2643 DWORD grfStateBits,/* [in] */
2644 DWORD grfMask) /* [in] */
2646 StorageBaseImpl *This = impl_from_IStorage(iface);
2648 if (This->reverted)
2649 return STG_E_REVERTED;
2651 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2652 return S_OK;
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)
2675 HRESULT hr;
2676 BYTE data[4096];
2677 DirEntry srcdata;
2678 ULARGE_INTEGER bytes_copied;
2679 ULONG bytestocopy, bytesread, byteswritten;
2681 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2683 if (SUCCEEDED(hr))
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,
2693 data, &bytesread);
2694 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2696 if (SUCCEEDED(hr))
2697 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2698 data, &byteswritten);
2699 if (SUCCEEDED(hr))
2701 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2702 bytes_copied.QuadPart += byteswritten;
2707 return hr;
2710 static HRESULT StorageBaseImpl_DupStorageTree(
2711 StorageBaseImpl *dst, DirRef *dst_entry,
2712 StorageBaseImpl *src, DirRef src_entry)
2714 HRESULT hr;
2715 DirEntry data;
2716 BOOL has_stream=FALSE;
2718 if (src_entry == DIRENTRY_NULL)
2720 *dst_entry = DIRENTRY_NULL;
2721 return S_OK;
2724 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2725 if (SUCCEEDED(hr))
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);
2734 if (SUCCEEDED(hr))
2735 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2737 if (SUCCEEDED(hr))
2738 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2740 if (SUCCEEDED(hr))
2741 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2743 if (SUCCEEDED(hr) && has_stream)
2744 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2746 return hr;
2749 static HRESULT StorageBaseImpl_CopyStorageTree(
2750 StorageBaseImpl *dst, DirRef dst_entry,
2751 StorageBaseImpl *src, DirRef src_entry)
2753 HRESULT hr;
2754 DirEntry src_data, dst_data;
2755 DirRef new_root_entry;
2757 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2759 if (SUCCEEDED(hr))
2761 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2764 if (SUCCEEDED(hr))
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;
2773 if (SUCCEEDED(hr))
2774 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2776 return hr;
2779 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2781 HRESULT hr;
2782 DirEntry data;
2783 ULARGE_INTEGER zero;
2785 if (entry == DIRENTRY_NULL)
2786 return S_OK;
2788 zero.QuadPart = 0;
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);
2798 if (SUCCEEDED(hr))
2799 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2801 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2802 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2804 if (SUCCEEDED(hr))
2805 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2807 return hr;
2811 /************************************************************************
2812 * StorageImpl implementation
2813 ***********************************************************************/
2815 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2816 ULARGE_INTEGER offset,
2817 void* buffer,
2818 ULONG size,
2819 ULONG* bytesRead)
2821 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2824 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2825 ULARGE_INTEGER offset,
2826 const void* buffer,
2827 const ULONG size,
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(
2839 StorageImpl* This)
2841 HRESULT hr;
2842 BYTE headerBigBlock[HEADER_SIZE];
2843 int index;
2844 ULARGE_INTEGER offset;
2845 DWORD bytes_read;
2847 TRACE("\n");
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.
2860 if (SUCCEEDED(hr))
2863 * Check for the "magic number" signature and return an error if it is not
2864 * found.
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(
2877 headerBigBlock,
2878 OFFSET_BIGBLOCKSIZEBITS,
2879 &This->bigBlockSizeBits);
2881 StorageUtl_ReadWord(
2882 headerBigBlock,
2883 OFFSET_SMALLBLOCKSIZEBITS,
2884 &This->smallBlockSizeBits);
2886 StorageUtl_ReadDWord(
2887 headerBigBlock,
2888 OFFSET_BBDEPOTCOUNT,
2889 &This->bigBlockDepotCount);
2891 StorageUtl_ReadDWord(
2892 headerBigBlock,
2893 OFFSET_ROOTSTARTBLOCK,
2894 &This->rootStartBlock);
2896 StorageUtl_ReadDWord(
2897 headerBigBlock,
2898 OFFSET_TRANSACTIONSIG,
2899 &This->transactionSig);
2901 StorageUtl_ReadDWord(
2902 headerBigBlock,
2903 OFFSET_SMALLBLOCKLIMIT,
2904 &This->smallBlockLimit);
2906 StorageUtl_ReadDWord(
2907 headerBigBlock,
2908 OFFSET_SBDEPOTSTART,
2909 &This->smallBlockDepotStart);
2911 StorageUtl_ReadDWord(
2912 headerBigBlock,
2913 OFFSET_EXTBBDEPOTSTART,
2914 &This->extBigBlockDepotStart);
2916 StorageUtl_ReadDWord(
2917 headerBigBlock,
2918 OFFSET_EXTBBDEPOTCOUNT,
2919 &This->extBigBlockDepotCount);
2921 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2923 StorageUtl_ReadDWord(
2924 headerBigBlock,
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;
2947 else
2948 hr = S_OK;
2951 return hr;
2954 /******************************************************************************
2955 * StorageImpl_SaveFileHeader
2957 * This method will save to the file the header
2959 static void StorageImpl_SaveFileHeader(
2960 StorageImpl* This)
2962 BYTE headerBigBlock[HEADER_SIZE];
2963 int index;
2964 HRESULT hr;
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)
2979 major_version = 3;
2980 else if (This->bigBlockSizeBits == 0xc)
2981 major_version = 4;
2982 else
2984 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
2985 major_version = 4;
2989 * If the block read failed, the file is probably new.
2991 if (FAILED(hr))
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(
3008 headerBigBlock,
3009 OFFSET_MINORVERSION,
3010 0x3e);
3012 StorageUtl_WriteWord(
3013 headerBigBlock,
3014 OFFSET_MAJORVERSION,
3015 major_version);
3017 StorageUtl_WriteWord(
3018 headerBigBlock,
3019 OFFSET_BYTEORDERMARKER,
3020 (WORD)-2);
3022 StorageUtl_WriteWord(
3023 headerBigBlock,
3024 OFFSET_BIGBLOCKSIZEBITS,
3025 This->bigBlockSizeBits);
3027 StorageUtl_WriteWord(
3028 headerBigBlock,
3029 OFFSET_SMALLBLOCKSIZEBITS,
3030 This->smallBlockSizeBits);
3032 if (major_version >= 4)
3034 if (This->rootBlockChain)
3035 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3036 else
3037 /* This file is being created, and it will start out with one block. */
3038 dirsectorcount = 1;
3040 else
3041 /* This field must be 0 in versions older than 4 */
3042 dirsectorcount = 0;
3044 StorageUtl_WriteDWord(
3045 headerBigBlock,
3046 OFFSET_DIRSECTORCOUNT,
3047 dirsectorcount);
3049 StorageUtl_WriteDWord(
3050 headerBigBlock,
3051 OFFSET_BBDEPOTCOUNT,
3052 This->bigBlockDepotCount);
3054 StorageUtl_WriteDWord(
3055 headerBigBlock,
3056 OFFSET_ROOTSTARTBLOCK,
3057 This->rootStartBlock);
3059 StorageUtl_WriteDWord(
3060 headerBigBlock,
3061 OFFSET_TRANSACTIONSIG,
3062 This->transactionSig);
3064 StorageUtl_WriteDWord(
3065 headerBigBlock,
3066 OFFSET_SMALLBLOCKLIMIT,
3067 This->smallBlockLimit);
3069 StorageUtl_WriteDWord(
3070 headerBigBlock,
3071 OFFSET_SBDEPOTSTART,
3072 This->smallBlockDepotStart);
3074 StorageUtl_WriteDWord(
3075 headerBigBlock,
3076 OFFSET_SBDEPOTCOUNT,
3077 This->smallBlockDepotChain ?
3078 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3080 StorageUtl_WriteDWord(
3081 headerBigBlock,
3082 OFFSET_EXTBBDEPOTSTART,
3083 This->extBigBlockDepotStart);
3085 StorageUtl_WriteDWord(
3086 headerBigBlock,
3087 OFFSET_EXTBBDEPOTCOUNT,
3088 This->extBigBlockDepotCount);
3090 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3092 StorageUtl_WriteDWord(
3093 headerBigBlock,
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;
3119 HRESULT hr;
3120 ULONG bytesRead;
3122 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3124 hr = BlockChainStream_ReadAt(
3125 This->rootBlockChain,
3126 offset,
3127 RAW_DIRENTRY_SIZE,
3128 buffer,
3129 &bytesRead);
3131 if (bytesRead != RAW_DIRENTRY_SIZE)
3132 return STG_E_READFAULT;
3134 return hr;
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;
3147 ULONG bytesRead;
3149 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3151 return BlockChainStream_WriteAt(
3152 This->rootBlockChain,
3153 offset,
3154 RAW_DIRENTRY_SIZE,
3155 buffer,
3156 &bytesRead);
3159 /***************************************************************************
3161 * Internal Method
3163 * Mark a directory entry in the file as free.
3165 static HRESULT StorageImpl_DestroyDirEntry(
3166 StorageBaseImpl *base,
3167 DirRef index)
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 /******************************************************************************
3178 * UpdateRawDirEntry
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);
3188 memcpy(
3189 buffer + OFFSET_PS_NAME,
3190 newData->name,
3191 DIRENTRY_NAME_BUFFER_LEN );
3193 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3195 StorageUtl_WriteWord(
3196 buffer,
3197 OFFSET_PS_NAMELENGTH,
3198 newData->sizeOfNameString);
3200 StorageUtl_WriteDWord(
3201 buffer,
3202 OFFSET_PS_LEFTCHILD,
3203 newData->leftChild);
3205 StorageUtl_WriteDWord(
3206 buffer,
3207 OFFSET_PS_RIGHTCHILD,
3208 newData->rightChild);
3210 StorageUtl_WriteDWord(
3211 buffer,
3212 OFFSET_PS_DIRROOT,
3213 newData->dirRootEntry);
3215 StorageUtl_WriteGUID(
3216 buffer,
3217 OFFSET_PS_GUID,
3218 &newData->clsid);
3220 StorageUtl_WriteDWord(
3221 buffer,
3222 OFFSET_PS_CTIMELOW,
3223 newData->ctime.dwLowDateTime);
3225 StorageUtl_WriteDWord(
3226 buffer,
3227 OFFSET_PS_CTIMEHIGH,
3228 newData->ctime.dwHighDateTime);
3230 StorageUtl_WriteDWord(
3231 buffer,
3232 OFFSET_PS_MTIMELOW,
3233 newData->mtime.dwLowDateTime);
3235 StorageUtl_WriteDWord(
3236 buffer,
3237 OFFSET_PS_MTIMEHIGH,
3238 newData->ctime.dwHighDateTime);
3240 StorageUtl_WriteDWord(
3241 buffer,
3242 OFFSET_PS_STARTBLOCK,
3243 newData->startingBlock);
3245 StorageUtl_WriteDWord(
3246 buffer,
3247 OFFSET_PS_SIZE,
3248 newData->size.u.LowPart);
3250 StorageUtl_WriteDWord(
3251 buffer,
3252 OFFSET_PS_SIZE_HIGH,
3253 newData->size.u.HighPart);
3256 /***************************************************************************
3258 * Internal Method
3260 * Reserve a directory entry in the file and initialize it.
3262 static HRESULT StorageImpl_CreateDirEntry(
3263 StorageBaseImpl *base,
3264 const DirEntry *newData,
3265 DirRef *index)
3267 StorageImpl *storage = (StorageImpl*)base;
3268 ULONG currentEntryIndex = 0;
3269 ULONG newEntryIndex = DIRENTRY_NULL;
3270 HRESULT hr = S_OK;
3271 BYTE currentData[RAW_DIRENTRY_SIZE];
3272 WORD sizeOfNameString;
3276 hr = StorageImpl_ReadRawDirEntry(storage,
3277 currentEntryIndex,
3278 currentData);
3280 if (SUCCEEDED(hr))
3282 StorageUtl_ReadWord(
3283 currentData,
3284 OFFSET_PS_NAMELENGTH,
3285 &sizeOfNameString);
3287 if (sizeOfNameString == 0)
3290 * The entry exists and is available, we found it.
3292 newEntryIndex = currentEntryIndex;
3295 else
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
3309 if (FAILED(hr))
3311 BYTE emptyData[RAW_DIRENTRY_SIZE];
3312 ULARGE_INTEGER newSize;
3313 ULONG entryIndex;
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
3335 * created entries
3337 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3340 * initialize them
3342 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3344 for(
3345 entryIndex = newEntryIndex + 1;
3346 entryIndex < lastEntry;
3347 entryIndex++)
3349 StorageImpl_WriteRawDirEntry(
3350 storage,
3351 entryIndex,
3352 emptyData);
3355 StorageImpl_SaveFileHeader(storage);
3358 UpdateRawDirEntry(currentData, newData);
3360 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3362 if (SUCCEEDED(hr))
3363 *index = newEntryIndex;
3365 return hr;
3368 /******************************************************************************
3369 * StorageImpl_ReadDirEntry
3371 * This method will read the specified directory entry.
3373 static HRESULT StorageImpl_ReadDirEntry(
3374 StorageImpl* This,
3375 DirRef index,
3376 DirEntry* buffer)
3378 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3379 HRESULT readRes;
3381 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3383 if (SUCCEEDED(readRes))
3385 memset(buffer->name, 0, sizeof(buffer->name));
3386 memcpy(
3387 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(
3395 currentEntry,
3396 OFFSET_PS_NAMELENGTH,
3397 &buffer->sizeOfNameString);
3399 StorageUtl_ReadDWord(
3400 currentEntry,
3401 OFFSET_PS_LEFTCHILD,
3402 &buffer->leftChild);
3404 StorageUtl_ReadDWord(
3405 currentEntry,
3406 OFFSET_PS_RIGHTCHILD,
3407 &buffer->rightChild);
3409 StorageUtl_ReadDWord(
3410 currentEntry,
3411 OFFSET_PS_DIRROOT,
3412 &buffer->dirRootEntry);
3414 StorageUtl_ReadGUID(
3415 currentEntry,
3416 OFFSET_PS_GUID,
3417 &buffer->clsid);
3419 StorageUtl_ReadDWord(
3420 currentEntry,
3421 OFFSET_PS_CTIMELOW,
3422 &buffer->ctime.dwLowDateTime);
3424 StorageUtl_ReadDWord(
3425 currentEntry,
3426 OFFSET_PS_CTIMEHIGH,
3427 &buffer->ctime.dwHighDateTime);
3429 StorageUtl_ReadDWord(
3430 currentEntry,
3431 OFFSET_PS_MTIMELOW,
3432 &buffer->mtime.dwLowDateTime);
3434 StorageUtl_ReadDWord(
3435 currentEntry,
3436 OFFSET_PS_MTIMEHIGH,
3437 &buffer->mtime.dwHighDateTime);
3439 StorageUtl_ReadDWord(
3440 currentEntry,
3441 OFFSET_PS_STARTBLOCK,
3442 &buffer->startingBlock);
3444 StorageUtl_ReadDWord(
3445 currentEntry,
3446 OFFSET_PS_SIZE,
3447 &buffer->size.u.LowPart);
3449 if (This->bigBlockSize < 4096)
3451 /* Version 3 files may have junk in the high part of size. */
3452 buffer->size.u.HighPart = 0;
3454 else
3456 StorageUtl_ReadDWord(
3457 currentEntry,
3458 OFFSET_PS_SIZE_HIGH,
3459 &buffer->size.u.HighPart);
3463 return readRes;
3466 /*********************************************************************
3467 * Write the specified directory entry to the file
3469 static HRESULT StorageImpl_WriteDirEntry(
3470 StorageImpl* This,
3471 DirRef index,
3472 const DirEntry* buffer)
3474 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3476 UpdateRawDirEntry(currentEntry, buffer);
3478 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3482 /************************************************************************
3483 * StorageImpl implementation : Block methods
3484 ***********************************************************************/
3486 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3488 return (ULONGLONG)(index+1) * This->bigBlockSize;
3491 static HRESULT StorageImpl_ReadBigBlock(
3492 StorageImpl* This,
3493 ULONG blockIndex,
3494 void* buffer,
3495 ULONG* out_read)
3497 ULARGE_INTEGER ulOffset;
3498 DWORD read=0;
3499 HRESULT hr;
3501 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3503 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3505 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3507 /* File ends during this block; fill the rest with 0's. */
3508 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3511 if (out_read) *out_read = read;
3513 return hr;
3516 static BOOL StorageImpl_ReadDWordFromBigBlock(
3517 StorageImpl* This,
3518 ULONG blockIndex,
3519 ULONG offset,
3520 DWORD* value)
3522 ULARGE_INTEGER ulOffset;
3523 DWORD read;
3524 DWORD tmp;
3526 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3527 ulOffset.QuadPart += offset;
3529 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3530 *value = lendian32toh(tmp);
3531 return (read == sizeof(DWORD));
3534 static BOOL StorageImpl_WriteBigBlock(
3535 StorageImpl* This,
3536 ULONG blockIndex,
3537 const void* buffer)
3539 ULARGE_INTEGER ulOffset;
3540 DWORD wrote;
3542 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3544 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3545 return (wrote == This->bigBlockSize);
3548 static BOOL StorageImpl_WriteDWordToBigBlock(
3549 StorageImpl* This,
3550 ULONG blockIndex,
3551 ULONG offset,
3552 DWORD value)
3554 ULARGE_INTEGER ulOffset;
3555 DWORD wrote;
3557 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3558 ulOffset.QuadPart += offset;
3560 value = htole32(value);
3561 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3562 return (wrote == sizeof(DWORD));
3565 /******************************************************************************
3566 * Storage32Impl_SmallBlocksToBigBlocks
3568 * This method will convert a small block chain to a big block chain.
3569 * The small block chain will be destroyed.
3571 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3572 StorageImpl* This,
3573 SmallBlockChainStream** ppsbChain)
3575 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3576 ULARGE_INTEGER size, offset;
3577 ULONG cbRead, cbWritten;
3578 ULARGE_INTEGER cbTotalRead;
3579 DirRef streamEntryRef;
3580 HRESULT resWrite = S_OK;
3581 HRESULT resRead;
3582 DirEntry streamEntry;
3583 BYTE *buffer;
3584 BlockChainStream *bbTempChain = NULL;
3585 BlockChainStream *bigBlockChain = NULL;
3588 * Create a temporary big block chain that doesn't have
3589 * an associated directory entry. This temporary chain will be
3590 * used to copy data from small blocks to big blocks.
3592 bbTempChain = BlockChainStream_Construct(This,
3593 &bbHeadOfChain,
3594 DIRENTRY_NULL);
3595 if(!bbTempChain) return NULL;
3597 * Grow the big block chain.
3599 size = SmallBlockChainStream_GetSize(*ppsbChain);
3600 BlockChainStream_SetSize(bbTempChain, size);
3603 * Copy the contents of the small block chain to the big block chain
3604 * by small block size increments.
3606 offset.u.LowPart = 0;
3607 offset.u.HighPart = 0;
3608 cbTotalRead.QuadPart = 0;
3610 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3613 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3614 offset,
3615 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3616 buffer,
3617 &cbRead);
3618 if (FAILED(resRead))
3619 break;
3621 if (cbRead > 0)
3623 cbTotalRead.QuadPart += cbRead;
3625 resWrite = BlockChainStream_WriteAt(bbTempChain,
3626 offset,
3627 cbRead,
3628 buffer,
3629 &cbWritten);
3631 if (FAILED(resWrite))
3632 break;
3634 offset.u.LowPart += cbRead;
3636 else
3638 resRead = STG_E_READFAULT;
3639 break;
3641 } while (cbTotalRead.QuadPart < size.QuadPart);
3642 HeapFree(GetProcessHeap(),0,buffer);
3644 size.u.HighPart = 0;
3645 size.u.LowPart = 0;
3647 if (FAILED(resRead) || FAILED(resWrite))
3649 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3650 BlockChainStream_SetSize(bbTempChain, size);
3651 BlockChainStream_Destroy(bbTempChain);
3652 return NULL;
3656 * Destroy the small block chain.
3658 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3659 SmallBlockChainStream_SetSize(*ppsbChain, size);
3660 SmallBlockChainStream_Destroy(*ppsbChain);
3661 *ppsbChain = 0;
3664 * Change the directory entry. This chain is now a big block chain
3665 * and it doesn't reside in the small blocks chain anymore.
3667 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3669 streamEntry.startingBlock = bbHeadOfChain;
3671 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3674 * Destroy the temporary entryless big block chain.
3675 * Create a new big block chain associated with this entry.
3677 BlockChainStream_Destroy(bbTempChain);
3678 bigBlockChain = BlockChainStream_Construct(This,
3679 NULL,
3680 streamEntryRef);
3682 return bigBlockChain;
3685 /******************************************************************************
3686 * Storage32Impl_BigBlocksToSmallBlocks
3688 * This method will convert a big block chain to a small block chain.
3689 * The big block chain will be destroyed on success.
3691 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3692 StorageImpl* This,
3693 BlockChainStream** ppbbChain,
3694 ULARGE_INTEGER newSize)
3696 ULARGE_INTEGER size, offset, cbTotalRead;
3697 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3698 DirRef streamEntryRef;
3699 HRESULT resWrite = S_OK, resRead = S_OK;
3700 DirEntry streamEntry;
3701 BYTE* buffer;
3702 SmallBlockChainStream* sbTempChain;
3704 TRACE("%p %p\n", This, ppbbChain);
3706 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3707 DIRENTRY_NULL);
3709 if(!sbTempChain)
3710 return NULL;
3712 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3713 size = BlockChainStream_GetSize(*ppbbChain);
3714 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3716 offset.u.HighPart = 0;
3717 offset.u.LowPart = 0;
3718 cbTotalRead.QuadPart = 0;
3719 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3720 while(cbTotalRead.QuadPart < size.QuadPart)
3722 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3723 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3724 buffer, &cbRead);
3726 if(FAILED(resRead))
3727 break;
3729 if(cbRead > 0)
3731 cbTotalRead.QuadPart += cbRead;
3733 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3734 cbRead, buffer, &cbWritten);
3736 if(FAILED(resWrite))
3737 break;
3739 offset.u.LowPart += cbRead;
3741 else
3743 resRead = STG_E_READFAULT;
3744 break;
3747 HeapFree(GetProcessHeap(), 0, buffer);
3749 size.u.HighPart = 0;
3750 size.u.LowPart = 0;
3752 if(FAILED(resRead) || FAILED(resWrite))
3754 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3755 SmallBlockChainStream_SetSize(sbTempChain, size);
3756 SmallBlockChainStream_Destroy(sbTempChain);
3757 return NULL;
3760 /* destroy the original big block chain */
3761 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3762 BlockChainStream_SetSize(*ppbbChain, size);
3763 BlockChainStream_Destroy(*ppbbChain);
3764 *ppbbChain = NULL;
3766 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3767 streamEntry.startingBlock = sbHeadOfChain;
3768 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3770 SmallBlockChainStream_Destroy(sbTempChain);
3771 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3774 /******************************************************************************
3775 * Storage32Impl_AddBlockDepot
3777 * This will create a depot block, essentially it is a block initialized
3778 * to BLOCK_UNUSEDs.
3780 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3782 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3783 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3784 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3785 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3788 * Initialize blocks as free
3790 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3792 /* Reserve the range lock sector */
3793 if (depotIndex == rangeLockDepot)
3795 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3798 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3801 /******************************************************************************
3802 * Storage32Impl_GetExtDepotBlock
3804 * Returns the index of the block that corresponds to the specified depot
3805 * index. This method is only for depot indexes equal or greater than
3806 * COUNT_BBDEPOTINHEADER.
3808 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3810 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3811 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3812 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3813 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3814 ULONG blockIndex = BLOCK_UNUSED;
3815 ULONG extBlockIndex;
3816 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3817 int index, num_blocks;
3819 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3821 if (extBlockCount >= This->extBigBlockDepotCount)
3822 return BLOCK_UNUSED;
3824 if (This->indexExtBlockDepotCached != extBlockCount)
3826 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3828 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3830 num_blocks = This->bigBlockSize / 4;
3832 for (index = 0; index < num_blocks; index++)
3834 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3835 This->extBlockDepotCached[index] = blockIndex;
3838 This->indexExtBlockDepotCached = extBlockCount;
3841 blockIndex = This->extBlockDepotCached[extBlockOffset];
3843 return blockIndex;
3846 /******************************************************************************
3847 * Storage32Impl_SetExtDepotBlock
3849 * Associates the specified block index to the specified depot index.
3850 * This method is only for depot indexes equal or greater than
3851 * COUNT_BBDEPOTINHEADER.
3853 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3855 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3856 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3857 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3858 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3859 ULONG extBlockIndex;
3861 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3863 assert(extBlockCount < This->extBigBlockDepotCount);
3865 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3867 if (extBlockIndex != BLOCK_UNUSED)
3869 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3870 extBlockOffset * sizeof(ULONG),
3871 blockIndex);
3874 if (This->indexExtBlockDepotCached == extBlockCount)
3876 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3880 /******************************************************************************
3881 * Storage32Impl_AddExtBlockDepot
3883 * Creates an extended depot block.
3885 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3887 ULONG numExtBlocks = This->extBigBlockDepotCount;
3888 ULONG nextExtBlock = This->extBigBlockDepotStart;
3889 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3890 ULONG index = BLOCK_UNUSED;
3891 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3892 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3893 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3895 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3896 blocksPerDepotBlock;
3898 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3901 * The first extended block.
3903 This->extBigBlockDepotStart = index;
3905 else
3908 * Find the last existing extended block.
3910 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3913 * Add the new extended block to the chain.
3915 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3916 index);
3920 * Initialize this block.
3922 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3923 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3925 /* Add the block to our cache. */
3926 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3928 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3929 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3931 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3932 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3934 This->extBigBlockDepotLocations = new_cache;
3935 This->extBigBlockDepotLocationsSize = new_cache_size;
3937 This->extBigBlockDepotLocations[numExtBlocks] = index;
3939 return index;
3942 /************************************************************************
3943 * StorageImpl_GetNextBlockInChain
3945 * This method will retrieve the block index of the next big block in
3946 * in the chain.
3948 * Params: This - Pointer to the Storage object.
3949 * blockIndex - Index of the block to retrieve the chain
3950 * for.
3951 * nextBlockIndex - receives the return value.
3953 * Returns: This method returns the index of the next block in the chain.
3954 * It will return the constants:
3955 * BLOCK_SPECIAL - If the block given was not part of a
3956 * chain.
3957 * BLOCK_END_OF_CHAIN - If the block given was the last in
3958 * a chain.
3959 * BLOCK_UNUSED - If the block given was not past of a chain
3960 * and is available.
3961 * BLOCK_EXTBBDEPOT - This block is part of the extended
3962 * big block depot.
3964 * See Windows documentation for more details on IStorage methods.
3966 static HRESULT StorageImpl_GetNextBlockInChain(
3967 StorageImpl* This,
3968 ULONG blockIndex,
3969 ULONG* nextBlockIndex)
3971 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3972 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3973 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3974 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3975 ULONG read;
3976 ULONG depotBlockIndexPos;
3977 int index, num_blocks;
3979 *nextBlockIndex = BLOCK_SPECIAL;
3981 if(depotBlockCount >= This->bigBlockDepotCount)
3983 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3984 This->bigBlockDepotCount);
3985 return STG_E_READFAULT;
3989 * Cache the currently accessed depot block.
3991 if (depotBlockCount != This->indexBlockDepotCached)
3993 This->indexBlockDepotCached = depotBlockCount;
3995 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3997 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3999 else
4002 * We have to look in the extended depot.
4004 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4007 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4009 if (!read)
4010 return STG_E_READFAULT;
4012 num_blocks = This->bigBlockSize / 4;
4014 for (index = 0; index < num_blocks; index++)
4016 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4017 This->blockDepotCached[index] = *nextBlockIndex;
4021 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4023 return S_OK;
4026 /******************************************************************************
4027 * Storage32Impl_GetNextExtendedBlock
4029 * Given an extended block this method will return the next extended block.
4031 * NOTES:
4032 * The last ULONG of an extended block is the block index of the next
4033 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4034 * depot.
4036 * Return values:
4037 * - The index of the next extended block
4038 * - BLOCK_UNUSED: there is no next extended block.
4039 * - Any other return values denotes failure.
4041 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4043 ULONG nextBlockIndex = BLOCK_SPECIAL;
4044 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4046 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4047 &nextBlockIndex);
4049 return nextBlockIndex;
4052 /******************************************************************************
4053 * StorageImpl_SetNextBlockInChain
4055 * This method will write the index of the specified block's next block
4056 * in the big block depot.
4058 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4059 * do the following
4061 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4062 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4063 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4066 static void StorageImpl_SetNextBlockInChain(
4067 StorageImpl* This,
4068 ULONG blockIndex,
4069 ULONG nextBlock)
4071 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4072 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4073 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4074 ULONG depotBlockIndexPos;
4076 assert(depotBlockCount < This->bigBlockDepotCount);
4077 assert(blockIndex != nextBlock);
4079 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4080 /* This should never happen (storage file format spec forbids it), but
4081 * older versions of Wine may have generated broken files. We don't want to
4082 * assert and potentially lose data, but we do want to know if this ever
4083 * happens in a newly-created file. */
4084 ERR("Using range lock page\n");
4086 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4088 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4090 else
4093 * We have to look in the extended depot.
4095 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4098 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4099 nextBlock);
4101 * Update the cached block depot, if necessary.
4103 if (depotBlockCount == This->indexBlockDepotCached)
4105 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4109 /******************************************************************************
4110 * StorageImpl_GetNextFreeBigBlock
4112 * Returns the index of the next free big block.
4113 * If the big block depot is filled, this method will enlarge it.
4116 static ULONG StorageImpl_GetNextFreeBigBlock(
4117 StorageImpl* This)
4119 ULONG depotBlockIndexPos;
4120 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4121 ULONG depotBlockOffset;
4122 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4123 ULONG nextBlockIndex = BLOCK_SPECIAL;
4124 int depotIndex = 0;
4125 ULONG freeBlock = BLOCK_UNUSED;
4126 ULONG read;
4127 ULARGE_INTEGER neededSize;
4128 STATSTG statstg;
4130 depotIndex = This->prevFreeBlock / blocksPerDepot;
4131 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4134 * Scan the entire big block depot until we find a block marked free
4136 while (nextBlockIndex != BLOCK_UNUSED)
4138 if (depotIndex < COUNT_BBDEPOTINHEADER)
4140 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4143 * Grow the primary depot.
4145 if (depotBlockIndexPos == BLOCK_UNUSED)
4147 depotBlockIndexPos = depotIndex*blocksPerDepot;
4150 * Add a block depot.
4152 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4153 This->bigBlockDepotCount++;
4154 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4157 * Flag it as a block depot.
4159 StorageImpl_SetNextBlockInChain(This,
4160 depotBlockIndexPos,
4161 BLOCK_SPECIAL);
4163 /* Save new header information.
4165 StorageImpl_SaveFileHeader(This);
4168 else
4170 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4172 if (depotBlockIndexPos == BLOCK_UNUSED)
4175 * Grow the extended depot.
4177 ULONG extIndex = BLOCK_UNUSED;
4178 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4179 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4181 if (extBlockOffset == 0)
4183 /* We need an extended block.
4185 extIndex = Storage32Impl_AddExtBlockDepot(This);
4186 This->extBigBlockDepotCount++;
4187 depotBlockIndexPos = extIndex + 1;
4189 else
4190 depotBlockIndexPos = depotIndex * blocksPerDepot;
4193 * Add a block depot and mark it in the extended block.
4195 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4196 This->bigBlockDepotCount++;
4197 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4199 /* Flag the block depot.
4201 StorageImpl_SetNextBlockInChain(This,
4202 depotBlockIndexPos,
4203 BLOCK_SPECIAL);
4205 /* If necessary, flag the extended depot block.
4207 if (extIndex != BLOCK_UNUSED)
4208 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4210 /* Save header information.
4212 StorageImpl_SaveFileHeader(This);
4216 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4218 if (read)
4220 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4221 ( nextBlockIndex != BLOCK_UNUSED))
4223 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4225 if (nextBlockIndex == BLOCK_UNUSED)
4227 freeBlock = (depotIndex * blocksPerDepot) +
4228 (depotBlockOffset/sizeof(ULONG));
4231 depotBlockOffset += sizeof(ULONG);
4235 depotIndex++;
4236 depotBlockOffset = 0;
4240 * make sure that the block physically exists before using it
4242 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4244 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4246 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4247 ILockBytes_SetSize(This->lockBytes, neededSize);
4249 This->prevFreeBlock = freeBlock;
4251 return freeBlock;
4254 /******************************************************************************
4255 * StorageImpl_FreeBigBlock
4257 * This method will flag the specified block as free in the big block depot.
4259 static void StorageImpl_FreeBigBlock(
4260 StorageImpl* This,
4261 ULONG blockIndex)
4263 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4265 if (blockIndex < This->prevFreeBlock)
4266 This->prevFreeBlock = blockIndex;
4270 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4271 DirRef index, const DirEntry *data)
4273 StorageImpl *This = (StorageImpl*)base;
4274 return StorageImpl_WriteDirEntry(This, index, data);
4277 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4278 DirRef index, DirEntry *data)
4280 StorageImpl *This = (StorageImpl*)base;
4281 return StorageImpl_ReadDirEntry(This, index, data);
4284 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4286 int i;
4288 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4290 if (!This->blockChainCache[i])
4292 return &This->blockChainCache[i];
4296 i = This->blockChainToEvict;
4298 BlockChainStream_Destroy(This->blockChainCache[i]);
4299 This->blockChainCache[i] = NULL;
4301 This->blockChainToEvict++;
4302 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4303 This->blockChainToEvict = 0;
4305 return &This->blockChainCache[i];
4308 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4309 DirRef index)
4311 int i, free_index=-1;
4313 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4315 if (!This->blockChainCache[i])
4317 if (free_index == -1) free_index = i;
4319 else if (This->blockChainCache[i]->ownerDirEntry == index)
4321 return &This->blockChainCache[i];
4325 if (free_index == -1)
4327 free_index = This->blockChainToEvict;
4329 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4330 This->blockChainCache[free_index] = NULL;
4332 This->blockChainToEvict++;
4333 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4334 This->blockChainToEvict = 0;
4337 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4338 return &This->blockChainCache[free_index];
4341 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4343 int i;
4345 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4347 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4349 BlockChainStream_Destroy(This->blockChainCache[i]);
4350 This->blockChainCache[i] = NULL;
4351 return;
4356 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4357 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4359 StorageImpl *This = (StorageImpl*)base;
4360 DirEntry data;
4361 HRESULT hr;
4362 ULONG bytesToRead;
4364 hr = StorageImpl_ReadDirEntry(This, index, &data);
4365 if (FAILED(hr)) return hr;
4367 if (data.size.QuadPart == 0)
4369 *bytesRead = 0;
4370 return S_OK;
4373 if (offset.QuadPart + size > data.size.QuadPart)
4375 bytesToRead = data.size.QuadPart - offset.QuadPart;
4377 else
4379 bytesToRead = size;
4382 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4384 SmallBlockChainStream *stream;
4386 stream = SmallBlockChainStream_Construct(This, NULL, index);
4387 if (!stream) return E_OUTOFMEMORY;
4389 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4391 SmallBlockChainStream_Destroy(stream);
4393 return hr;
4395 else
4397 BlockChainStream *stream = NULL;
4399 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4400 if (!stream) return E_OUTOFMEMORY;
4402 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4404 return hr;
4408 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4409 ULARGE_INTEGER newsize)
4411 StorageImpl *This = (StorageImpl*)base;
4412 DirEntry data;
4413 HRESULT hr;
4414 SmallBlockChainStream *smallblock=NULL;
4415 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4417 hr = StorageImpl_ReadDirEntry(This, index, &data);
4418 if (FAILED(hr)) return hr;
4420 /* In simple mode keep the stream size above the small block limit */
4421 if (This->base.openFlags & STGM_SIMPLE)
4422 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4424 if (data.size.QuadPart == newsize.QuadPart)
4425 return S_OK;
4427 /* Create a block chain object of the appropriate type */
4428 if (data.size.QuadPart == 0)
4430 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4432 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4433 if (!smallblock) return E_OUTOFMEMORY;
4435 else
4437 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4438 bigblock = *pbigblock;
4439 if (!bigblock) return E_OUTOFMEMORY;
4442 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4444 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4445 if (!smallblock) return E_OUTOFMEMORY;
4447 else
4449 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4450 bigblock = *pbigblock;
4451 if (!bigblock) return E_OUTOFMEMORY;
4454 /* Change the block chain type if necessary. */
4455 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4457 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4458 if (!bigblock)
4460 SmallBlockChainStream_Destroy(smallblock);
4461 return E_FAIL;
4464 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4465 *pbigblock = bigblock;
4467 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4469 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4470 if (!smallblock)
4471 return E_FAIL;
4474 /* Set the size of the block chain. */
4475 if (smallblock)
4477 SmallBlockChainStream_SetSize(smallblock, newsize);
4478 SmallBlockChainStream_Destroy(smallblock);
4480 else
4482 BlockChainStream_SetSize(bigblock, newsize);
4485 /* Set the size in the directory entry. */
4486 hr = StorageImpl_ReadDirEntry(This, index, &data);
4487 if (SUCCEEDED(hr))
4489 data.size = newsize;
4491 hr = StorageImpl_WriteDirEntry(This, index, &data);
4493 return hr;
4496 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4497 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4499 StorageImpl *This = (StorageImpl*)base;
4500 DirEntry data;
4501 HRESULT hr;
4502 ULARGE_INTEGER newSize;
4504 hr = StorageImpl_ReadDirEntry(This, index, &data);
4505 if (FAILED(hr)) return hr;
4507 /* Grow the stream if necessary */
4508 newSize.QuadPart = offset.QuadPart + size;
4510 if (newSize.QuadPart > data.size.QuadPart)
4512 hr = StorageImpl_StreamSetSize(base, index, newSize);
4513 if (FAILED(hr))
4514 return hr;
4516 hr = StorageImpl_ReadDirEntry(This, index, &data);
4517 if (FAILED(hr)) return hr;
4520 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4522 SmallBlockChainStream *stream;
4524 stream = SmallBlockChainStream_Construct(This, NULL, index);
4525 if (!stream) return E_OUTOFMEMORY;
4527 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4529 SmallBlockChainStream_Destroy(stream);
4531 return hr;
4533 else
4535 BlockChainStream *stream;
4537 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4538 if (!stream) return E_OUTOFMEMORY;
4540 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4544 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4545 DirRef src)
4547 StorageImpl *This = (StorageImpl*)base;
4548 DirEntry dst_data, src_data;
4549 HRESULT hr;
4551 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4553 if (SUCCEEDED(hr))
4554 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4556 if (SUCCEEDED(hr))
4558 StorageImpl_DeleteCachedBlockChainStream(This, src);
4559 dst_data.startingBlock = src_data.startingBlock;
4560 dst_data.size = src_data.size;
4562 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4565 return hr;
4568 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4570 HRESULT hr=S_OK;
4571 DirEntry currentEntry;
4572 DirRef currentEntryRef;
4573 BlockChainStream *blockChainStream;
4575 if (create)
4577 ULARGE_INTEGER size;
4578 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4580 /* Discard any existing data. */
4581 size.QuadPart = 0;
4582 ILockBytes_SetSize(This->lockBytes, size);
4585 * Initialize all header variables:
4586 * - The big block depot consists of one block and it is at block 0
4587 * - The directory table starts at block 1
4588 * - There is no small block depot
4590 memset( This->bigBlockDepotStart,
4591 BLOCK_UNUSED,
4592 sizeof(This->bigBlockDepotStart));
4594 This->bigBlockDepotCount = 1;
4595 This->bigBlockDepotStart[0] = 0;
4596 This->rootStartBlock = 1;
4597 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4598 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4599 if (This->bigBlockSize == 4096)
4600 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4601 else
4602 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4603 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4604 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4605 This->extBigBlockDepotCount = 0;
4607 StorageImpl_SaveFileHeader(This);
4610 * Add one block for the big block depot and one block for the directory table
4612 size.u.HighPart = 0;
4613 size.u.LowPart = This->bigBlockSize * 3;
4614 ILockBytes_SetSize(This->lockBytes, size);
4617 * Initialize the big block depot
4619 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4620 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4621 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4622 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4624 else
4627 * Load the header for the file.
4629 hr = StorageImpl_LoadFileHeader(This);
4631 if (FAILED(hr))
4633 return hr;
4638 * There is no block depot cached yet.
4640 This->indexBlockDepotCached = 0xFFFFFFFF;
4641 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4644 * Start searching for free blocks with block 0.
4646 This->prevFreeBlock = 0;
4648 This->firstFreeSmallBlock = 0;
4650 /* Read the extended big block depot locations. */
4651 if (This->extBigBlockDepotCount != 0)
4653 ULONG current_block = This->extBigBlockDepotStart;
4654 ULONG cache_size = This->extBigBlockDepotCount * 2;
4655 ULONG i;
4657 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4658 if (!This->extBigBlockDepotLocations)
4660 return E_OUTOFMEMORY;
4663 This->extBigBlockDepotLocationsSize = cache_size;
4665 for (i=0; i<This->extBigBlockDepotCount; i++)
4667 if (current_block == BLOCK_END_OF_CHAIN)
4669 WARN("File has too few extended big block depot blocks.\n");
4670 return STG_E_DOCFILECORRUPT;
4672 This->extBigBlockDepotLocations[i] = current_block;
4673 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4676 else
4678 This->extBigBlockDepotLocations = NULL;
4679 This->extBigBlockDepotLocationsSize = 0;
4683 * Create the block chain abstractions.
4685 if(!(blockChainStream =
4686 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4688 return STG_E_READFAULT;
4690 if (!new_object)
4691 BlockChainStream_Destroy(This->rootBlockChain);
4692 This->rootBlockChain = blockChainStream;
4694 if(!(blockChainStream =
4695 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4696 DIRENTRY_NULL)))
4698 return STG_E_READFAULT;
4700 if (!new_object)
4701 BlockChainStream_Destroy(This->smallBlockDepotChain);
4702 This->smallBlockDepotChain = blockChainStream;
4705 * Write the root storage entry (memory only)
4707 if (create)
4709 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4710 DirEntry rootEntry;
4712 * Initialize the directory table
4714 memset(&rootEntry, 0, sizeof(rootEntry));
4715 strcpyW(rootEntry.name, rootentryW);
4716 rootEntry.sizeOfNameString = sizeof(rootentryW);
4717 rootEntry.stgType = STGTY_ROOT;
4718 rootEntry.leftChild = DIRENTRY_NULL;
4719 rootEntry.rightChild = DIRENTRY_NULL;
4720 rootEntry.dirRootEntry = DIRENTRY_NULL;
4721 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4722 rootEntry.size.u.HighPart = 0;
4723 rootEntry.size.u.LowPart = 0;
4725 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4729 * Find the ID of the root storage.
4731 currentEntryRef = 0;
4735 hr = StorageImpl_ReadDirEntry(
4736 This,
4737 currentEntryRef,
4738 &currentEntry);
4740 if (SUCCEEDED(hr))
4742 if ( (currentEntry.sizeOfNameString != 0 ) &&
4743 (currentEntry.stgType == STGTY_ROOT) )
4745 This->base.storageDirEntry = currentEntryRef;
4749 currentEntryRef++;
4751 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4753 if (FAILED(hr))
4755 return STG_E_READFAULT;
4759 * Create the block chain abstraction for the small block root chain.
4761 if(!(blockChainStream =
4762 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4764 return STG_E_READFAULT;
4766 if (!new_object)
4767 BlockChainStream_Destroy(This->smallBlockRootChain);
4768 This->smallBlockRootChain = blockChainStream;
4770 if (!new_object)
4772 int i;
4773 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4775 BlockChainStream_Destroy(This->blockChainCache[i]);
4776 This->blockChainCache[i] = NULL;
4780 return hr;
4783 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4784 ULONG* result, BOOL refresh)
4786 StorageImpl *This = (StorageImpl*)base;
4787 HRESULT hr=S_OK;
4788 DWORD oldTransactionSig = This->transactionSig;
4790 if (refresh)
4792 ULARGE_INTEGER offset;
4793 ULONG bytes_read;
4794 BYTE data[4];
4796 offset.u.HighPart = 0;
4797 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4798 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4800 if (SUCCEEDED(hr))
4802 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4804 if (oldTransactionSig != This->transactionSig)
4806 /* Someone else wrote to this, so toss all cached information. */
4807 TRACE("signature changed\n");
4809 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4812 if (FAILED(hr))
4813 This->transactionSig = oldTransactionSig;
4817 *result = This->transactionSig;
4819 return hr;
4822 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4823 ULONG value)
4825 StorageImpl *This = (StorageImpl*)base;
4827 This->transactionSig = value;
4828 StorageImpl_SaveFileHeader(This);
4830 return S_OK;
4833 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4834 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4836 if ((dwLockType & This->locks_supported) == 0)
4838 if (supported) *supported = FALSE;
4839 return S_OK;
4842 if (supported) *supported = TRUE;
4843 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4846 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4847 ULARGE_INTEGER cb, DWORD dwLockType)
4849 if ((dwLockType & This->locks_supported) == 0)
4850 return S_OK;
4852 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4855 /* Internal function */
4856 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4857 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4859 HRESULT hr;
4860 int delay = 0;
4861 DWORD start_time = GetTickCount();
4862 DWORD last_sanity_check = start_time;
4863 ULARGE_INTEGER sanity_offset, sanity_cb;
4865 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4866 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4870 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4872 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4874 DWORD current_time = GetTickCount();
4875 if (current_time - start_time >= 20000)
4877 /* timeout */
4878 break;
4880 if (current_time - last_sanity_check >= 500)
4882 /* Any storage implementation with the file open in a
4883 * shared mode should not lock these bytes for writing. However,
4884 * some programs (LibreOffice Writer) will keep ALL bytes locked
4885 * when opening in exclusive mode. We can use a read lock to
4886 * detect this case early, and not hang a full 20 seconds.
4888 * This can collide with another attempt to open the file in
4889 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4890 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4891 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4892 break;
4893 if (SUCCEEDED(hr))
4895 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4896 hr = STG_E_ACCESSDENIED;
4899 last_sanity_check = current_time;
4901 Sleep(delay);
4902 if (delay < 150) delay++;
4904 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4906 return hr;
4909 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4911 StorageImpl *This = (StorageImpl*)base;
4912 HRESULT hr;
4913 ULARGE_INTEGER offset, cb;
4915 if (write)
4917 /* Synchronous grab of second priority range, the commit lock, and the
4918 * lock-checking lock. */
4919 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4920 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4922 else
4924 offset.QuadPart = RANGELOCK_COMMIT;
4925 cb.QuadPart = 1;
4928 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4930 return hr;
4933 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4935 StorageImpl *This = (StorageImpl*)base;
4936 HRESULT hr;
4937 ULARGE_INTEGER offset, cb;
4939 if (write)
4941 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4942 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4944 else
4946 offset.QuadPart = RANGELOCK_COMMIT;
4947 cb.QuadPart = 1;
4950 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4952 return hr;
4955 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4957 StorageImpl *This = (StorageImpl*) iface;
4958 STATSTG statstg;
4959 HRESULT hr;
4961 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4963 *result = statstg.pwcsName;
4965 return hr;
4968 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
4969 ULONG end, HRESULT fail_hr)
4971 HRESULT hr;
4972 ULARGE_INTEGER offset, cb;
4974 offset.QuadPart = start;
4975 cb.QuadPart = 1 + end - start;
4977 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4978 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4980 if (FAILED(hr))
4981 return fail_hr;
4982 else
4983 return S_OK;
4986 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
4988 HRESULT hr=S_OK;
4989 int i, j;
4990 ULARGE_INTEGER offset, cb;
4992 cb.QuadPart = 1;
4994 for (i=start; i<=end; i++)
4996 offset.QuadPart = i;
4997 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4998 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
4999 break;
5002 if (SUCCEEDED(hr))
5004 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
5006 if (This->locked_bytes[j] == 0)
5008 This->locked_bytes[j] = i;
5009 break;
5014 return hr;
5017 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5019 HRESULT hr;
5020 ULARGE_INTEGER offset;
5021 ULARGE_INTEGER cb;
5022 DWORD share_mode = STGM_SHARE_MODE(openFlags);
5023 BOOL supported;
5025 if (openFlags & STGM_NOSNAPSHOT)
5027 /* STGM_NOSNAPSHOT implies deny write */
5028 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5029 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5032 /* Wrap all other locking inside a single lock so we can check ranges safely */
5033 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5034 cb.QuadPart = 1;
5035 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5037 /* If the ILockBytes doesn't support locking that's ok. */
5038 if (!supported) return S_OK;
5039 else if (FAILED(hr)) return hr;
5041 hr = S_OK;
5043 /* First check for any conflicting locks. */
5044 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5045 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5047 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5048 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5050 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5051 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5053 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5054 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5056 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5057 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5059 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5061 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5063 if (SUCCEEDED(hr))
5064 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5067 /* Then grab our locks. */
5068 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5070 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5071 if (SUCCEEDED(hr))
5072 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5075 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5076 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5078 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5079 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5081 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5082 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5084 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5085 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5087 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5088 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5090 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5091 cb.QuadPart = 1;
5092 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5094 return hr;
5097 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5099 StorageImpl *This = (StorageImpl*)storage;
5100 int i;
5101 HRESULT hr;
5102 TRACE("(%p)\n", This);
5104 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5106 if (SUCCEEDED(hr))
5107 hr = BlockChainStream_Flush(This->rootBlockChain);
5109 if (SUCCEEDED(hr))
5110 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5112 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5113 if (This->blockChainCache[i])
5114 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5116 if (SUCCEEDED(hr))
5117 hr = ILockBytes_Flush(This->lockBytes);
5119 return hr;
5122 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5124 StorageImpl *This = (StorageImpl*) iface;
5126 StorageBaseImpl_DeleteAll(&This->base);
5128 This->base.reverted = TRUE;
5131 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5133 StorageImpl *This = (StorageImpl*) iface;
5134 int i;
5135 TRACE("(%p)\n", This);
5137 StorageImpl_Flush(iface);
5139 StorageImpl_Invalidate(iface);
5141 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5143 BlockChainStream_Destroy(This->smallBlockRootChain);
5144 BlockChainStream_Destroy(This->rootBlockChain);
5145 BlockChainStream_Destroy(This->smallBlockDepotChain);
5147 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
5148 BlockChainStream_Destroy(This->blockChainCache[i]);
5150 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
5152 ULARGE_INTEGER offset, cb;
5153 cb.QuadPart = 1;
5154 if (This->locked_bytes[i] != 0)
5156 offset.QuadPart = This->locked_bytes[i];
5157 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5161 if (This->lockBytes)
5162 ILockBytes_Release(This->lockBytes);
5163 HeapFree(GetProcessHeap(), 0, This);
5167 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5169 StorageImpl_Destroy,
5170 StorageImpl_Invalidate,
5171 StorageImpl_Flush,
5172 StorageImpl_GetFilename,
5173 StorageImpl_CreateDirEntry,
5174 StorageImpl_BaseWriteDirEntry,
5175 StorageImpl_BaseReadDirEntry,
5176 StorageImpl_DestroyDirEntry,
5177 StorageImpl_StreamReadAt,
5178 StorageImpl_StreamWriteAt,
5179 StorageImpl_StreamSetSize,
5180 StorageImpl_StreamLink,
5181 StorageImpl_GetTransactionSig,
5182 StorageImpl_SetTransactionSig,
5183 StorageImpl_LockTransaction,
5184 StorageImpl_UnlockTransaction
5189 * Virtual function table for the IStorageBaseImpl class.
5191 static const IStorageVtbl StorageImpl_Vtbl =
5193 StorageBaseImpl_QueryInterface,
5194 StorageBaseImpl_AddRef,
5195 StorageBaseImpl_Release,
5196 StorageBaseImpl_CreateStream,
5197 StorageBaseImpl_OpenStream,
5198 StorageBaseImpl_CreateStorage,
5199 StorageBaseImpl_OpenStorage,
5200 StorageBaseImpl_CopyTo,
5201 StorageBaseImpl_MoveElementTo,
5202 StorageBaseImpl_Commit,
5203 StorageBaseImpl_Revert,
5204 StorageBaseImpl_EnumElements,
5205 StorageBaseImpl_DestroyElement,
5206 StorageBaseImpl_RenameElement,
5207 StorageBaseImpl_SetElementTimes,
5208 StorageBaseImpl_SetClass,
5209 StorageBaseImpl_SetStateBits,
5210 StorageBaseImpl_Stat
5213 static HRESULT StorageImpl_Construct(
5214 HANDLE hFile,
5215 LPCOLESTR pwcsName,
5216 ILockBytes* pLkbyt,
5217 DWORD openFlags,
5218 BOOL fileBased,
5219 BOOL create,
5220 ULONG sector_size,
5221 StorageImpl** result)
5223 StorageImpl* This;
5224 HRESULT hr = S_OK;
5225 STATSTG stat;
5227 if ( FAILED( validateSTGM(openFlags) ))
5228 return STG_E_INVALIDFLAG;
5230 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5231 if (!This)
5232 return E_OUTOFMEMORY;
5234 memset(This, 0, sizeof(StorageImpl));
5236 list_init(&This->base.strmHead);
5238 list_init(&This->base.storageHead);
5240 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5241 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5242 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5243 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5244 This->base.openFlags = (openFlags & ~STGM_CREATE);
5245 This->base.ref = 1;
5246 This->base.create = create;
5248 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5249 This->base.lockingrole = SWMR_Writer;
5250 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5251 This->base.lockingrole = SWMR_Reader;
5252 else
5253 This->base.lockingrole = SWMR_None;
5255 This->base.reverted = FALSE;
5258 * Initialize the big block cache.
5260 This->bigBlockSize = sector_size;
5261 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5262 if (hFile)
5263 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5264 else
5266 This->lockBytes = pLkbyt;
5267 ILockBytes_AddRef(pLkbyt);
5270 if (SUCCEEDED(hr))
5271 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5273 if (SUCCEEDED(hr))
5275 This->locks_supported = stat.grfLocksSupported;
5276 if (!hFile)
5277 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5278 This->locks_supported &= ~WINE_LOCK_READ;
5280 hr = StorageImpl_GrabLocks(This, openFlags);
5283 if (SUCCEEDED(hr))
5284 hr = StorageImpl_Refresh(This, TRUE, create);
5286 if (FAILED(hr))
5288 IStorage_Release(&This->base.IStorage_iface);
5289 *result = NULL;
5291 else
5293 StorageImpl_Flush(&This->base);
5294 *result = This;
5297 return hr;
5301 /************************************************************************
5302 * StorageInternalImpl implementation
5303 ***********************************************************************/
5305 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5307 StorageInternalImpl* This = (StorageInternalImpl*) base;
5309 if (!This->base.reverted)
5311 TRACE("Storage invalidated (stg=%p)\n", This);
5313 This->base.reverted = TRUE;
5315 This->parentStorage = NULL;
5317 StorageBaseImpl_DeleteAll(&This->base);
5319 list_remove(&This->ParentListEntry);
5323 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5325 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5327 StorageInternalImpl_Invalidate(&This->base);
5329 HeapFree(GetProcessHeap(), 0, This);
5332 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5334 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5336 return StorageBaseImpl_Flush(This->parentStorage);
5339 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5341 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5343 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5346 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5347 const DirEntry *newData, DirRef *index)
5349 StorageInternalImpl* This = (StorageInternalImpl*) base;
5351 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5352 newData, index);
5355 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5356 DirRef index, const DirEntry *data)
5358 StorageInternalImpl* This = (StorageInternalImpl*) base;
5360 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5361 index, data);
5364 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5365 DirRef index, DirEntry *data)
5367 StorageInternalImpl* This = (StorageInternalImpl*) base;
5369 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5370 index, data);
5373 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5374 DirRef index)
5376 StorageInternalImpl* This = (StorageInternalImpl*) base;
5378 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5379 index);
5382 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5383 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5385 StorageInternalImpl* This = (StorageInternalImpl*) base;
5387 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5388 index, offset, size, buffer, bytesRead);
5391 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5392 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5394 StorageInternalImpl* This = (StorageInternalImpl*) base;
5396 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5397 index, offset, size, buffer, bytesWritten);
5400 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5401 DirRef index, ULARGE_INTEGER newsize)
5403 StorageInternalImpl* This = (StorageInternalImpl*) base;
5405 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5406 index, newsize);
5409 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5410 DirRef dst, DirRef src)
5412 StorageInternalImpl* This = (StorageInternalImpl*) base;
5414 return StorageBaseImpl_StreamLink(This->parentStorage,
5415 dst, src);
5418 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5419 ULONG* result, BOOL refresh)
5421 return E_NOTIMPL;
5424 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5425 ULONG value)
5427 return E_NOTIMPL;
5430 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5432 return E_NOTIMPL;
5435 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5437 return E_NOTIMPL;
5440 /******************************************************************************
5442 ** StorageInternalImpl_Commit
5445 static HRESULT WINAPI StorageInternalImpl_Commit(
5446 IStorage* iface,
5447 DWORD grfCommitFlags) /* [in] */
5449 StorageBaseImpl* This = impl_from_IStorage(iface);
5450 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5451 return StorageBaseImpl_Flush(This);
5454 /******************************************************************************
5456 ** StorageInternalImpl_Revert
5459 static HRESULT WINAPI StorageInternalImpl_Revert(
5460 IStorage* iface)
5462 FIXME("(%p): stub\n", iface);
5463 return S_OK;
5467 * Virtual function table for the StorageInternalImpl class.
5469 static const IStorageVtbl StorageInternalImpl_Vtbl =
5471 StorageBaseImpl_QueryInterface,
5472 StorageBaseImpl_AddRef,
5473 StorageBaseImpl_Release,
5474 StorageBaseImpl_CreateStream,
5475 StorageBaseImpl_OpenStream,
5476 StorageBaseImpl_CreateStorage,
5477 StorageBaseImpl_OpenStorage,
5478 StorageBaseImpl_CopyTo,
5479 StorageBaseImpl_MoveElementTo,
5480 StorageInternalImpl_Commit,
5481 StorageInternalImpl_Revert,
5482 StorageBaseImpl_EnumElements,
5483 StorageBaseImpl_DestroyElement,
5484 StorageBaseImpl_RenameElement,
5485 StorageBaseImpl_SetElementTimes,
5486 StorageBaseImpl_SetClass,
5487 StorageBaseImpl_SetStateBits,
5488 StorageBaseImpl_Stat
5491 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5493 StorageInternalImpl_Destroy,
5494 StorageInternalImpl_Invalidate,
5495 StorageInternalImpl_Flush,
5496 StorageInternalImpl_GetFilename,
5497 StorageInternalImpl_CreateDirEntry,
5498 StorageInternalImpl_WriteDirEntry,
5499 StorageInternalImpl_ReadDirEntry,
5500 StorageInternalImpl_DestroyDirEntry,
5501 StorageInternalImpl_StreamReadAt,
5502 StorageInternalImpl_StreamWriteAt,
5503 StorageInternalImpl_StreamSetSize,
5504 StorageInternalImpl_StreamLink,
5505 StorageInternalImpl_GetTransactionSig,
5506 StorageInternalImpl_SetTransactionSig,
5507 StorageInternalImpl_LockTransaction,
5508 StorageInternalImpl_UnlockTransaction
5511 static StorageInternalImpl* StorageInternalImpl_Construct(
5512 StorageBaseImpl* parentStorage,
5513 DWORD openFlags,
5514 DirRef storageDirEntry)
5516 StorageInternalImpl* newStorage;
5518 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5520 if (newStorage!=0)
5522 list_init(&newStorage->base.strmHead);
5524 list_init(&newStorage->base.storageHead);
5527 * Initialize the virtual function table.
5529 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5530 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5531 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5532 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5534 newStorage->base.reverted = FALSE;
5536 newStorage->base.ref = 1;
5538 newStorage->parentStorage = parentStorage;
5541 * Keep a reference to the directory entry of this storage
5543 newStorage->base.storageDirEntry = storageDirEntry;
5545 newStorage->base.create = FALSE;
5547 return newStorage;
5550 return 0;
5554 /************************************************************************
5555 * TransactedSnapshotImpl implementation
5556 ***********************************************************************/
5558 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5560 DirRef result=This->firstFreeEntry;
5562 while (result < This->entries_size && This->entries[result].inuse)
5563 result++;
5565 if (result == This->entries_size)
5567 ULONG new_size = This->entries_size * 2;
5568 TransactedDirEntry *new_entries;
5570 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5571 if (!new_entries) return DIRENTRY_NULL;
5573 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5574 HeapFree(GetProcessHeap(), 0, This->entries);
5576 This->entries = new_entries;
5577 This->entries_size = new_size;
5580 This->entries[result].inuse = TRUE;
5582 This->firstFreeEntry = result+1;
5584 return result;
5587 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5588 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5590 DirRef stubEntryRef;
5591 TransactedDirEntry *entry;
5593 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5595 if (stubEntryRef != DIRENTRY_NULL)
5597 entry = &This->entries[stubEntryRef];
5599 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5601 entry->read = FALSE;
5604 return stubEntryRef;
5607 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5608 TransactedSnapshotImpl *This, DirRef entry)
5610 HRESULT hr=S_OK;
5611 DirEntry data;
5613 if (!This->entries[entry].read)
5615 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5616 This->entries[entry].transactedParentEntry,
5617 &data);
5619 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5621 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5623 if (data.leftChild == DIRENTRY_NULL)
5624 hr = E_OUTOFMEMORY;
5627 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5629 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5631 if (data.rightChild == DIRENTRY_NULL)
5632 hr = E_OUTOFMEMORY;
5635 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5637 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5639 if (data.dirRootEntry == DIRENTRY_NULL)
5640 hr = E_OUTOFMEMORY;
5643 if (SUCCEEDED(hr))
5645 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5646 This->entries[entry].read = TRUE;
5650 return hr;
5653 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5654 TransactedSnapshotImpl *This, DirRef entry)
5656 HRESULT hr = S_OK;
5658 if (!This->entries[entry].stream_dirty)
5660 DirEntry new_entrydata;
5662 memset(&new_entrydata, 0, sizeof(DirEntry));
5663 new_entrydata.name[0] = 'S';
5664 new_entrydata.sizeOfNameString = 1;
5665 new_entrydata.stgType = STGTY_STREAM;
5666 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5667 new_entrydata.leftChild = DIRENTRY_NULL;
5668 new_entrydata.rightChild = DIRENTRY_NULL;
5669 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5671 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5672 &This->entries[entry].stream_entry);
5674 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5676 hr = StorageBaseImpl_CopyStream(
5677 This->scratch, This->entries[entry].stream_entry,
5678 This->transactedParent, This->entries[entry].transactedParentEntry);
5680 if (FAILED(hr))
5681 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5684 if (SUCCEEDED(hr))
5685 This->entries[entry].stream_dirty = TRUE;
5687 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5689 /* Since this entry is modified, and we aren't using its stream data, we
5690 * no longer care about the original entry. */
5691 DirRef delete_ref;
5692 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5694 if (delete_ref != DIRENTRY_NULL)
5695 This->entries[delete_ref].deleted = TRUE;
5697 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5701 return hr;
5704 /* Find the first entry in a depth-first traversal. */
5705 static DirRef TransactedSnapshotImpl_FindFirstChild(
5706 TransactedSnapshotImpl* This, DirRef parent)
5708 DirRef cursor, prev;
5709 TransactedDirEntry *entry;
5711 cursor = parent;
5712 entry = &This->entries[cursor];
5713 while (entry->read)
5715 if (entry->data.leftChild != DIRENTRY_NULL)
5717 prev = cursor;
5718 cursor = entry->data.leftChild;
5719 entry = &This->entries[cursor];
5720 entry->parent = prev;
5722 else if (entry->data.rightChild != DIRENTRY_NULL)
5724 prev = cursor;
5725 cursor = entry->data.rightChild;
5726 entry = &This->entries[cursor];
5727 entry->parent = prev;
5729 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5731 prev = cursor;
5732 cursor = entry->data.dirRootEntry;
5733 entry = &This->entries[cursor];
5734 entry->parent = prev;
5736 else
5737 break;
5740 return cursor;
5743 /* Find the next entry in a depth-first traversal. */
5744 static DirRef TransactedSnapshotImpl_FindNextChild(
5745 TransactedSnapshotImpl* This, DirRef current)
5747 DirRef parent;
5748 TransactedDirEntry *parent_entry;
5750 parent = This->entries[current].parent;
5751 parent_entry = &This->entries[parent];
5753 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5755 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5757 This->entries[parent_entry->data.rightChild].parent = parent;
5758 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5761 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5763 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5764 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5768 return parent;
5771 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5772 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5773 TransactedSnapshotImpl* This, DirRef entry)
5775 return entry != DIRENTRY_NULL &&
5776 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5779 /* Destroy the entries created by CopyTree. */
5780 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5781 TransactedSnapshotImpl* This, DirRef stop)
5783 DirRef cursor;
5784 TransactedDirEntry *entry;
5785 ULARGE_INTEGER zero;
5787 zero.QuadPart = 0;
5789 if (!This->entries[This->base.storageDirEntry].read)
5790 return;
5792 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5794 if (cursor == DIRENTRY_NULL)
5795 return;
5797 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5799 while (cursor != DIRENTRY_NULL && cursor != stop)
5801 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5803 entry = &This->entries[cursor];
5805 if (entry->stream_dirty)
5806 StorageBaseImpl_StreamSetSize(This->transactedParent,
5807 entry->newTransactedParentEntry, zero);
5809 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5810 entry->newTransactedParentEntry);
5812 entry->newTransactedParentEntry = entry->transactedParentEntry;
5815 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5819 /* Make a copy of our edited tree that we can use in the parent. */
5820 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5822 DirRef cursor;
5823 TransactedDirEntry *entry;
5824 HRESULT hr = S_OK;
5826 cursor = This->base.storageDirEntry;
5827 entry = &This->entries[cursor];
5828 entry->parent = DIRENTRY_NULL;
5829 entry->newTransactedParentEntry = entry->transactedParentEntry;
5831 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5832 return S_OK;
5834 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5836 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5837 entry = &This->entries[cursor];
5839 while (cursor != DIRENTRY_NULL)
5841 /* Make a copy of this entry in the transacted parent. */
5842 if (!entry->read ||
5843 (!entry->dirty && !entry->stream_dirty &&
5844 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5845 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5846 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5847 entry->newTransactedParentEntry = entry->transactedParentEntry;
5848 else
5850 DirEntry newData;
5852 memcpy(&newData, &entry->data, sizeof(DirEntry));
5854 newData.size.QuadPart = 0;
5855 newData.startingBlock = BLOCK_END_OF_CHAIN;
5857 if (newData.leftChild != DIRENTRY_NULL)
5858 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5860 if (newData.rightChild != DIRENTRY_NULL)
5861 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5863 if (newData.dirRootEntry != DIRENTRY_NULL)
5864 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5866 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5867 &entry->newTransactedParentEntry);
5868 if (FAILED(hr))
5870 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5871 return hr;
5874 if (entry->stream_dirty)
5876 hr = StorageBaseImpl_CopyStream(
5877 This->transactedParent, entry->newTransactedParentEntry,
5878 This->scratch, entry->stream_entry);
5880 else if (entry->data.size.QuadPart)
5882 hr = StorageBaseImpl_StreamLink(
5883 This->transactedParent, entry->newTransactedParentEntry,
5884 entry->transactedParentEntry);
5887 if (FAILED(hr))
5889 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5890 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5891 return hr;
5895 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5896 entry = &This->entries[cursor];
5899 return hr;
5902 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5903 IStorage* iface,
5904 DWORD grfCommitFlags) /* [in] */
5906 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5907 TransactedDirEntry *root_entry;
5908 DirRef i, dir_root_ref;
5909 DirEntry data;
5910 ULARGE_INTEGER zero;
5911 HRESULT hr;
5912 ULONG transactionSig;
5914 zero.QuadPart = 0;
5916 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5918 /* Cannot commit a read-only transacted storage */
5919 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5920 return STG_E_ACCESSDENIED;
5922 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5923 if (hr == E_NOTIMPL) hr = S_OK;
5924 if (SUCCEEDED(hr))
5926 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5927 if (SUCCEEDED(hr))
5929 if (transactionSig != This->lastTransactionSig)
5931 ERR("file was externally modified\n");
5932 hr = STG_E_NOTCURRENT;
5935 if (SUCCEEDED(hr))
5937 This->lastTransactionSig = transactionSig+1;
5938 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5941 else if (hr == E_NOTIMPL)
5942 hr = S_OK;
5944 if (FAILED(hr)) goto end;
5946 /* To prevent data loss, we create the new structure in the file before we
5947 * delete the old one, so that in case of errors the old data is intact. We
5948 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5949 * needed in the rare situation where we have just enough free disk space to
5950 * overwrite the existing data. */
5952 root_entry = &This->entries[This->base.storageDirEntry];
5954 if (!root_entry->read)
5955 goto end;
5957 hr = TransactedSnapshotImpl_CopyTree(This);
5958 if (FAILED(hr)) goto end;
5960 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5961 dir_root_ref = DIRENTRY_NULL;
5962 else
5963 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5965 hr = StorageBaseImpl_Flush(This->transactedParent);
5967 /* Update the storage to use the new data in one step. */
5968 if (SUCCEEDED(hr))
5969 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5970 root_entry->transactedParentEntry, &data);
5972 if (SUCCEEDED(hr))
5974 data.dirRootEntry = dir_root_ref;
5975 data.clsid = root_entry->data.clsid;
5976 data.ctime = root_entry->data.ctime;
5977 data.mtime = root_entry->data.mtime;
5979 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5980 root_entry->transactedParentEntry, &data);
5983 /* Try to flush after updating the root storage, but if the flush fails, keep
5984 * going, on the theory that it'll either succeed later or the subsequent
5985 * writes will fail. */
5986 StorageBaseImpl_Flush(This->transactedParent);
5988 if (SUCCEEDED(hr))
5990 /* Destroy the old now-orphaned data. */
5991 for (i=0; i<This->entries_size; i++)
5993 TransactedDirEntry *entry = &This->entries[i];
5994 if (entry->inuse)
5996 if (entry->deleted)
5998 StorageBaseImpl_StreamSetSize(This->transactedParent,
5999 entry->transactedParentEntry, zero);
6000 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6001 entry->transactedParentEntry);
6002 memset(entry, 0, sizeof(TransactedDirEntry));
6003 This->firstFreeEntry = min(i, This->firstFreeEntry);
6005 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6007 if (entry->transactedParentEntry != DIRENTRY_NULL)
6008 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6009 entry->transactedParentEntry);
6010 if (entry->stream_dirty)
6012 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6013 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6014 entry->stream_dirty = FALSE;
6016 entry->dirty = FALSE;
6017 entry->transactedParentEntry = entry->newTransactedParentEntry;
6022 else
6024 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6027 if (SUCCEEDED(hr))
6028 hr = StorageBaseImpl_Flush(This->transactedParent);
6029 end:
6030 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6033 return hr;
6036 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6037 IStorage* iface)
6039 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6040 ULARGE_INTEGER zero;
6041 ULONG i;
6043 TRACE("(%p)\n", iface);
6045 /* Destroy the open objects. */
6046 StorageBaseImpl_DeleteAll(&This->base);
6048 /* Clear out the scratch file. */
6049 zero.QuadPart = 0;
6050 for (i=0; i<This->entries_size; i++)
6052 if (This->entries[i].stream_dirty)
6054 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6055 zero);
6057 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6061 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6063 This->firstFreeEntry = 0;
6064 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6066 return S_OK;
6069 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6071 if (!This->reverted)
6073 TRACE("Storage invalidated (stg=%p)\n", This);
6075 This->reverted = TRUE;
6077 StorageBaseImpl_DeleteAll(This);
6081 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6083 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6085 IStorage_Revert(&This->base.IStorage_iface);
6086 IStorage_Release(&This->transactedParent->IStorage_iface);
6087 IStorage_Release(&This->scratch->IStorage_iface);
6088 HeapFree(GetProcessHeap(), 0, This->entries);
6089 HeapFree(GetProcessHeap(), 0, This);
6092 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6094 /* We only need to flush when committing. */
6095 return S_OK;
6098 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6100 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6102 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6105 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6106 const DirEntry *newData, DirRef *index)
6108 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6109 DirRef new_ref;
6110 TransactedDirEntry *new_entry;
6112 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6113 if (new_ref == DIRENTRY_NULL)
6114 return E_OUTOFMEMORY;
6116 new_entry = &This->entries[new_ref];
6118 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6119 new_entry->read = TRUE;
6120 new_entry->dirty = TRUE;
6121 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6123 *index = new_ref;
6125 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6127 return S_OK;
6130 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6131 DirRef index, const DirEntry *data)
6133 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6134 HRESULT hr;
6136 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6138 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6139 if (FAILED(hr)) return hr;
6141 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6143 if (index != This->base.storageDirEntry)
6145 This->entries[index].dirty = TRUE;
6147 if (data->size.QuadPart == 0 &&
6148 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6150 /* Since this entry is modified, and we aren't using its stream data, we
6151 * no longer care about the original entry. */
6152 DirRef delete_ref;
6153 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6155 if (delete_ref != DIRENTRY_NULL)
6156 This->entries[delete_ref].deleted = TRUE;
6158 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6162 return S_OK;
6165 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6166 DirRef index, DirEntry *data)
6168 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6169 HRESULT hr;
6171 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6172 if (FAILED(hr)) return hr;
6174 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6176 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6178 return S_OK;
6181 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6182 DirRef index)
6184 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6186 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6187 This->entries[index].data.size.QuadPart != 0)
6189 /* If we deleted this entry while it has stream data. We must have left the
6190 * data because some other entry is using it, and we need to leave the
6191 * original entry alone. */
6192 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6193 This->firstFreeEntry = min(index, This->firstFreeEntry);
6195 else
6197 This->entries[index].deleted = TRUE;
6200 return S_OK;
6203 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6204 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6206 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6208 if (This->entries[index].stream_dirty)
6210 return StorageBaseImpl_StreamReadAt(This->scratch,
6211 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6213 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6215 /* This stream doesn't live in the parent, and we haven't allocated storage
6216 * for it yet */
6217 *bytesRead = 0;
6218 return S_OK;
6220 else
6222 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6223 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6227 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6228 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6230 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6231 HRESULT hr;
6233 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6234 if (FAILED(hr)) return hr;
6236 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6237 if (FAILED(hr)) return hr;
6239 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6240 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6242 if (SUCCEEDED(hr) && size != 0)
6243 This->entries[index].data.size.QuadPart = max(
6244 This->entries[index].data.size.QuadPart,
6245 offset.QuadPart + size);
6247 return hr;
6250 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6251 DirRef index, ULARGE_INTEGER newsize)
6253 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6254 HRESULT hr;
6256 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6257 if (FAILED(hr)) return hr;
6259 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6260 return S_OK;
6262 if (newsize.QuadPart == 0)
6264 /* Destroy any parent references or entries in the scratch file. */
6265 if (This->entries[index].stream_dirty)
6267 ULARGE_INTEGER zero;
6268 zero.QuadPart = 0;
6269 StorageBaseImpl_StreamSetSize(This->scratch,
6270 This->entries[index].stream_entry, zero);
6271 StorageBaseImpl_DestroyDirEntry(This->scratch,
6272 This->entries[index].stream_entry);
6273 This->entries[index].stream_dirty = FALSE;
6275 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6277 DirRef delete_ref;
6278 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6280 if (delete_ref != DIRENTRY_NULL)
6281 This->entries[delete_ref].deleted = TRUE;
6283 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6286 else
6288 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6289 if (FAILED(hr)) return hr;
6291 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6292 This->entries[index].stream_entry, newsize);
6295 if (SUCCEEDED(hr))
6296 This->entries[index].data.size = newsize;
6298 return hr;
6301 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6302 DirRef dst, DirRef src)
6304 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6305 HRESULT hr;
6306 TransactedDirEntry *dst_entry, *src_entry;
6308 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6309 if (FAILED(hr)) return hr;
6311 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6312 if (FAILED(hr)) return hr;
6314 dst_entry = &This->entries[dst];
6315 src_entry = &This->entries[src];
6317 dst_entry->stream_dirty = src_entry->stream_dirty;
6318 dst_entry->stream_entry = src_entry->stream_entry;
6319 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6320 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6321 dst_entry->data.size = src_entry->data.size;
6323 return S_OK;
6326 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6327 ULONG* result, BOOL refresh)
6329 return E_NOTIMPL;
6332 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6333 ULONG value)
6335 return E_NOTIMPL;
6338 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6340 return E_NOTIMPL;
6343 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6345 return E_NOTIMPL;
6348 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6350 StorageBaseImpl_QueryInterface,
6351 StorageBaseImpl_AddRef,
6352 StorageBaseImpl_Release,
6353 StorageBaseImpl_CreateStream,
6354 StorageBaseImpl_OpenStream,
6355 StorageBaseImpl_CreateStorage,
6356 StorageBaseImpl_OpenStorage,
6357 StorageBaseImpl_CopyTo,
6358 StorageBaseImpl_MoveElementTo,
6359 TransactedSnapshotImpl_Commit,
6360 TransactedSnapshotImpl_Revert,
6361 StorageBaseImpl_EnumElements,
6362 StorageBaseImpl_DestroyElement,
6363 StorageBaseImpl_RenameElement,
6364 StorageBaseImpl_SetElementTimes,
6365 StorageBaseImpl_SetClass,
6366 StorageBaseImpl_SetStateBits,
6367 StorageBaseImpl_Stat
6370 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6372 TransactedSnapshotImpl_Destroy,
6373 TransactedSnapshotImpl_Invalidate,
6374 TransactedSnapshotImpl_Flush,
6375 TransactedSnapshotImpl_GetFilename,
6376 TransactedSnapshotImpl_CreateDirEntry,
6377 TransactedSnapshotImpl_WriteDirEntry,
6378 TransactedSnapshotImpl_ReadDirEntry,
6379 TransactedSnapshotImpl_DestroyDirEntry,
6380 TransactedSnapshotImpl_StreamReadAt,
6381 TransactedSnapshotImpl_StreamWriteAt,
6382 TransactedSnapshotImpl_StreamSetSize,
6383 TransactedSnapshotImpl_StreamLink,
6384 TransactedSnapshotImpl_GetTransactionSig,
6385 TransactedSnapshotImpl_SetTransactionSig,
6386 TransactedSnapshotImpl_LockTransaction,
6387 TransactedSnapshotImpl_UnlockTransaction
6390 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6391 TransactedSnapshotImpl** result)
6393 HRESULT hr;
6395 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6396 if (*result)
6398 IStorage *scratch;
6400 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6402 /* This is OK because the property set storage functions use the IStorage functions. */
6403 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6404 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6406 list_init(&(*result)->base.strmHead);
6408 list_init(&(*result)->base.storageHead);
6410 (*result)->base.ref = 1;
6412 (*result)->base.openFlags = parentStorage->openFlags;
6414 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6415 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6417 /* Create a new temporary storage to act as the scratch file. */
6418 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6419 0, &scratch);
6420 (*result)->scratch = impl_from_IStorage(scratch);
6422 if (SUCCEEDED(hr))
6424 ULONG num_entries = 20;
6426 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6427 (*result)->entries_size = num_entries;
6428 (*result)->firstFreeEntry = 0;
6430 if ((*result)->entries)
6432 /* parentStorage already has 1 reference, which we take over here. */
6433 (*result)->transactedParent = parentStorage;
6435 parentStorage->transactedChild = &(*result)->base;
6437 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6439 else
6441 IStorage_Release(scratch);
6443 hr = E_OUTOFMEMORY;
6447 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6449 return hr;
6451 else
6452 return E_OUTOFMEMORY;
6456 /************************************************************************
6457 * TransactedSharedImpl implementation
6458 ***********************************************************************/
6460 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6462 if (!This->reverted)
6464 TRACE("Storage invalidated (stg=%p)\n", This);
6466 This->reverted = TRUE;
6468 StorageBaseImpl_DeleteAll(This);
6472 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6474 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6476 TransactedSharedImpl_Invalidate(&This->base);
6477 IStorage_Release(&This->transactedParent->IStorage_iface);
6478 IStorage_Release(&This->scratch->base.IStorage_iface);
6479 HeapFree(GetProcessHeap(), 0, This);
6482 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6484 /* We only need to flush when committing. */
6485 return S_OK;
6488 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6490 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6492 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6495 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6496 const DirEntry *newData, DirRef *index)
6498 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6500 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6501 newData, index);
6504 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6505 DirRef index, const DirEntry *data)
6507 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6509 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6510 index, data);
6513 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6514 DirRef index, DirEntry *data)
6516 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6518 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6519 index, data);
6522 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6523 DirRef index)
6525 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6527 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6528 index);
6531 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6532 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6534 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6536 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6537 index, offset, size, buffer, bytesRead);
6540 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6541 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6543 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6545 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6546 index, offset, size, buffer, bytesWritten);
6549 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6550 DirRef index, ULARGE_INTEGER newsize)
6552 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6554 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6555 index, newsize);
6558 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6559 DirRef dst, DirRef src)
6561 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6563 return StorageBaseImpl_StreamLink(&This->scratch->base,
6564 dst, src);
6567 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6568 ULONG* result, BOOL refresh)
6570 return E_NOTIMPL;
6573 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6574 ULONG value)
6576 return E_NOTIMPL;
6579 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6581 return E_NOTIMPL;
6584 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6586 return E_NOTIMPL;
6589 static HRESULT WINAPI TransactedSharedImpl_Commit(
6590 IStorage* iface,
6591 DWORD grfCommitFlags) /* [in] */
6593 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6594 DirRef new_storage_ref, prev_storage_ref;
6595 DirEntry src_data, dst_data;
6596 HRESULT hr;
6597 ULONG transactionSig;
6599 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6601 /* Cannot commit a read-only transacted storage */
6602 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6603 return STG_E_ACCESSDENIED;
6605 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6606 if (hr == E_NOTIMPL) hr = S_OK;
6607 if (SUCCEEDED(hr))
6609 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6610 if (SUCCEEDED(hr))
6612 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6613 hr = STG_E_NOTCURRENT;
6615 if (SUCCEEDED(hr))
6616 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6618 else if (hr == E_NOTIMPL)
6619 hr = S_OK;
6621 if (SUCCEEDED(hr))
6622 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6624 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6625 if (SUCCEEDED(hr))
6626 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6628 if (SUCCEEDED(hr))
6629 hr = StorageBaseImpl_Flush(This->transactedParent);
6631 if (SUCCEEDED(hr))
6632 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6634 if (SUCCEEDED(hr))
6636 prev_storage_ref = dst_data.dirRootEntry;
6637 dst_data.dirRootEntry = new_storage_ref;
6638 dst_data.clsid = src_data.clsid;
6639 dst_data.ctime = src_data.ctime;
6640 dst_data.mtime = src_data.mtime;
6641 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6644 if (SUCCEEDED(hr))
6646 /* Try to flush after updating the root storage, but if the flush fails, keep
6647 * going, on the theory that it'll either succeed later or the subsequent
6648 * writes will fail. */
6649 StorageBaseImpl_Flush(This->transactedParent);
6651 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6654 if (SUCCEEDED(hr))
6655 hr = StorageBaseImpl_Flush(This->transactedParent);
6657 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6659 if (SUCCEEDED(hr))
6660 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6662 if (SUCCEEDED(hr))
6664 This->lastTransactionSig = transactionSig+1;
6668 return hr;
6671 static HRESULT WINAPI TransactedSharedImpl_Revert(
6672 IStorage* iface)
6674 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6676 TRACE("(%p)\n", iface);
6678 /* Destroy the open objects. */
6679 StorageBaseImpl_DeleteAll(&This->base);
6681 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6684 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6686 StorageBaseImpl_QueryInterface,
6687 StorageBaseImpl_AddRef,
6688 StorageBaseImpl_Release,
6689 StorageBaseImpl_CreateStream,
6690 StorageBaseImpl_OpenStream,
6691 StorageBaseImpl_CreateStorage,
6692 StorageBaseImpl_OpenStorage,
6693 StorageBaseImpl_CopyTo,
6694 StorageBaseImpl_MoveElementTo,
6695 TransactedSharedImpl_Commit,
6696 TransactedSharedImpl_Revert,
6697 StorageBaseImpl_EnumElements,
6698 StorageBaseImpl_DestroyElement,
6699 StorageBaseImpl_RenameElement,
6700 StorageBaseImpl_SetElementTimes,
6701 StorageBaseImpl_SetClass,
6702 StorageBaseImpl_SetStateBits,
6703 StorageBaseImpl_Stat
6706 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6708 TransactedSharedImpl_Destroy,
6709 TransactedSharedImpl_Invalidate,
6710 TransactedSharedImpl_Flush,
6711 TransactedSharedImpl_GetFilename,
6712 TransactedSharedImpl_CreateDirEntry,
6713 TransactedSharedImpl_WriteDirEntry,
6714 TransactedSharedImpl_ReadDirEntry,
6715 TransactedSharedImpl_DestroyDirEntry,
6716 TransactedSharedImpl_StreamReadAt,
6717 TransactedSharedImpl_StreamWriteAt,
6718 TransactedSharedImpl_StreamSetSize,
6719 TransactedSharedImpl_StreamLink,
6720 TransactedSharedImpl_GetTransactionSig,
6721 TransactedSharedImpl_SetTransactionSig,
6722 TransactedSharedImpl_LockTransaction,
6723 TransactedSharedImpl_UnlockTransaction
6726 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6727 TransactedSharedImpl** result)
6729 HRESULT hr;
6731 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6732 if (*result)
6734 IStorage *scratch;
6736 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6738 /* This is OK because the property set storage functions use the IStorage functions. */
6739 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6740 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6742 list_init(&(*result)->base.strmHead);
6744 list_init(&(*result)->base.storageHead);
6746 (*result)->base.ref = 1;
6748 (*result)->base.openFlags = parentStorage->openFlags;
6750 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6752 if (SUCCEEDED(hr))
6754 STGOPTIONS stgo;
6756 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6757 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6759 stgo.usVersion = 1;
6760 stgo.reserved = 0;
6761 stgo.ulSectorSize = 4096;
6762 stgo.pwcsTemplateFile = NULL;
6764 /* Create a new temporary storage to act as the scratch file. */
6765 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6766 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6767 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6769 if (SUCCEEDED(hr))
6771 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6772 parentStorage, parentStorage->storageDirEntry);
6774 if (SUCCEEDED(hr))
6776 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6778 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6779 (*result)->transactedParent = parentStorage;
6782 if (FAILED(hr))
6783 IStorage_Release(scratch);
6786 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6789 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6791 return hr;
6793 else
6794 return E_OUTOFMEMORY;
6797 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6798 BOOL toplevel, StorageBaseImpl** result)
6800 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6802 if (parentStorage->openFlags & fixme_flags)
6804 fixme_flags &= ~parentStorage->openFlags;
6805 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6808 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6809 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6810 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6812 /* Need to create a temp file for the snapshot */
6813 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6816 return TransactedSnapshotImpl_Construct(parentStorage,
6817 (TransactedSnapshotImpl**)result);
6820 static HRESULT Storage_Construct(
6821 HANDLE hFile,
6822 LPCOLESTR pwcsName,
6823 ILockBytes* pLkbyt,
6824 DWORD openFlags,
6825 BOOL fileBased,
6826 BOOL create,
6827 ULONG sector_size,
6828 StorageBaseImpl** result)
6830 StorageImpl *newStorage;
6831 StorageBaseImpl *newTransactedStorage;
6832 HRESULT hr;
6834 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6835 if (FAILED(hr)) goto end;
6837 if (openFlags & STGM_TRANSACTED)
6839 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6840 if (FAILED(hr))
6841 IStorage_Release(&newStorage->base.IStorage_iface);
6842 else
6843 *result = newTransactedStorage;
6845 else
6846 *result = &newStorage->base;
6848 end:
6849 return hr;
6853 /************************************************************************
6854 * StorageUtl helper functions
6855 ***********************************************************************/
6857 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6859 WORD tmp;
6861 memcpy(&tmp, buffer+offset, sizeof(WORD));
6862 *value = lendian16toh(tmp);
6865 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6867 value = htole16(value);
6868 memcpy(buffer+offset, &value, sizeof(WORD));
6871 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6873 DWORD tmp;
6875 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6876 *value = lendian32toh(tmp);
6879 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6881 value = htole32(value);
6882 memcpy(buffer+offset, &value, sizeof(DWORD));
6885 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6886 ULARGE_INTEGER* value)
6888 #ifdef WORDS_BIGENDIAN
6889 ULARGE_INTEGER tmp;
6891 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6892 value->u.LowPart = htole32(tmp.u.HighPart);
6893 value->u.HighPart = htole32(tmp.u.LowPart);
6894 #else
6895 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6896 #endif
6899 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6900 const ULARGE_INTEGER *value)
6902 #ifdef WORDS_BIGENDIAN
6903 ULARGE_INTEGER tmp;
6905 tmp.u.LowPart = htole32(value->u.HighPart);
6906 tmp.u.HighPart = htole32(value->u.LowPart);
6907 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6908 #else
6909 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6910 #endif
6913 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6915 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6916 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6917 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6919 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6922 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6924 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6925 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6926 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6928 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6931 void StorageUtl_CopyDirEntryToSTATSTG(
6932 StorageBaseImpl* storage,
6933 STATSTG* destination,
6934 const DirEntry* source,
6935 int statFlags)
6938 * The copy of the string occurs only when the flag is not set
6940 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6942 /* Use the filename for the root storage. */
6943 destination->pwcsName = 0;
6944 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6946 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6947 (source->name[0] == 0) )
6949 destination->pwcsName = 0;
6951 else
6953 destination->pwcsName =
6954 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6956 strcpyW(destination->pwcsName, source->name);
6959 switch (source->stgType)
6961 case STGTY_STORAGE:
6962 case STGTY_ROOT:
6963 destination->type = STGTY_STORAGE;
6964 break;
6965 case STGTY_STREAM:
6966 destination->type = STGTY_STREAM;
6967 break;
6968 default:
6969 destination->type = STGTY_STREAM;
6970 break;
6973 destination->cbSize = source->size;
6975 currentReturnStruct->mtime = {0}; TODO
6976 currentReturnStruct->ctime = {0};
6977 currentReturnStruct->atime = {0};
6979 destination->grfMode = 0;
6980 destination->grfLocksSupported = 0;
6981 destination->clsid = source->clsid;
6982 destination->grfStateBits = 0;
6983 destination->reserved = 0;
6987 /************************************************************************
6988 * BlockChainStream implementation
6989 ***********************************************************************/
6991 /******************************************************************************
6992 * BlockChainStream_GetHeadOfChain
6994 * Returns the head of this stream chain.
6995 * Some special chains don't have directory entries, their heads are kept in
6996 * This->headOfStreamPlaceHolder.
6999 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7001 DirEntry chainEntry;
7002 HRESULT hr;
7004 if (This->headOfStreamPlaceHolder != 0)
7005 return *(This->headOfStreamPlaceHolder);
7007 if (This->ownerDirEntry != DIRENTRY_NULL)
7009 hr = StorageImpl_ReadDirEntry(
7010 This->parentStorage,
7011 This->ownerDirEntry,
7012 &chainEntry);
7014 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7015 return chainEntry.startingBlock;
7018 return BLOCK_END_OF_CHAIN;
7021 /* Read and save the index of all blocks in this stream. */
7022 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7024 ULONG next_sector, next_offset;
7025 HRESULT hr;
7026 struct BlockChainRun *last_run;
7028 if (This->indexCacheLen == 0)
7030 last_run = NULL;
7031 next_offset = 0;
7032 next_sector = BlockChainStream_GetHeadOfChain(This);
7034 else
7036 last_run = &This->indexCache[This->indexCacheLen-1];
7037 next_offset = last_run->lastOffset+1;
7038 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7039 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7040 &next_sector);
7041 if (FAILED(hr)) return hr;
7044 while (next_sector != BLOCK_END_OF_CHAIN)
7046 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7048 /* Add the current block to the cache. */
7049 if (This->indexCacheSize == 0)
7051 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7052 if (!This->indexCache) return E_OUTOFMEMORY;
7053 This->indexCacheSize = 16;
7055 else if (This->indexCacheSize == This->indexCacheLen)
7057 struct BlockChainRun *new_cache;
7058 ULONG new_size;
7060 new_size = This->indexCacheSize * 2;
7061 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7062 if (!new_cache) return E_OUTOFMEMORY;
7063 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7065 HeapFree(GetProcessHeap(), 0, This->indexCache);
7066 This->indexCache = new_cache;
7067 This->indexCacheSize = new_size;
7070 This->indexCacheLen++;
7071 last_run = &This->indexCache[This->indexCacheLen-1];
7072 last_run->firstSector = next_sector;
7073 last_run->firstOffset = next_offset;
7076 last_run->lastOffset = next_offset;
7078 /* Find the next block. */
7079 next_offset++;
7080 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7081 if (FAILED(hr)) return hr;
7084 if (This->indexCacheLen)
7086 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7087 This->numBlocks = last_run->lastOffset+1;
7089 else
7091 This->tailIndex = BLOCK_END_OF_CHAIN;
7092 This->numBlocks = 0;
7095 return S_OK;
7098 /* Locate the nth block in this stream. */
7099 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7101 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7102 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7104 if (offset >= This->numBlocks)
7105 return BLOCK_END_OF_CHAIN;
7107 while (min_run < max_run)
7109 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7110 if (offset < This->indexCache[run_to_check].firstOffset)
7112 max_offset = This->indexCache[run_to_check].firstOffset-1;
7113 max_run = run_to_check-1;
7115 else if (offset > This->indexCache[run_to_check].lastOffset)
7117 min_offset = This->indexCache[run_to_check].lastOffset+1;
7118 min_run = run_to_check+1;
7120 else
7121 /* Block is in this run. */
7122 min_run = max_run = run_to_check;
7125 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7128 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7129 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7131 BlockChainBlock *result=NULL;
7132 int i;
7134 for (i=0; i<2; i++)
7135 if (This->cachedBlocks[i].index == index)
7137 *sector = This->cachedBlocks[i].sector;
7138 *block = &This->cachedBlocks[i];
7139 return S_OK;
7142 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7143 if (*sector == BLOCK_END_OF_CHAIN)
7144 return STG_E_DOCFILECORRUPT;
7146 if (create)
7148 if (This->cachedBlocks[0].index == 0xffffffff)
7149 result = &This->cachedBlocks[0];
7150 else if (This->cachedBlocks[1].index == 0xffffffff)
7151 result = &This->cachedBlocks[1];
7152 else
7154 result = &This->cachedBlocks[This->blockToEvict++];
7155 if (This->blockToEvict == 2)
7156 This->blockToEvict = 0;
7159 if (result->dirty)
7161 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7162 return STG_E_WRITEFAULT;
7163 result->dirty = FALSE;
7166 result->read = FALSE;
7167 result->index = index;
7168 result->sector = *sector;
7171 *block = result;
7172 return S_OK;
7175 BlockChainStream* BlockChainStream_Construct(
7176 StorageImpl* parentStorage,
7177 ULONG* headOfStreamPlaceHolder,
7178 DirRef dirEntry)
7180 BlockChainStream* newStream;
7182 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7184 newStream->parentStorage = parentStorage;
7185 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7186 newStream->ownerDirEntry = dirEntry;
7187 newStream->indexCache = NULL;
7188 newStream->indexCacheLen = 0;
7189 newStream->indexCacheSize = 0;
7190 newStream->cachedBlocks[0].index = 0xffffffff;
7191 newStream->cachedBlocks[0].dirty = FALSE;
7192 newStream->cachedBlocks[1].index = 0xffffffff;
7193 newStream->cachedBlocks[1].dirty = FALSE;
7194 newStream->blockToEvict = 0;
7196 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7198 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7199 HeapFree(GetProcessHeap(), 0, newStream);
7200 return NULL;
7203 return newStream;
7206 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7208 int i;
7209 if (!This) return S_OK;
7210 for (i=0; i<2; i++)
7212 if (This->cachedBlocks[i].dirty)
7214 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7215 This->cachedBlocks[i].dirty = FALSE;
7216 else
7217 return STG_E_WRITEFAULT;
7220 return S_OK;
7223 void BlockChainStream_Destroy(BlockChainStream* This)
7225 if (This)
7227 BlockChainStream_Flush(This);
7228 HeapFree(GetProcessHeap(), 0, This->indexCache);
7230 HeapFree(GetProcessHeap(), 0, This);
7233 /******************************************************************************
7234 * BlockChainStream_Shrink
7236 * Shrinks this chain in the big block depot.
7238 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7239 ULARGE_INTEGER newSize)
7241 ULONG blockIndex;
7242 ULONG numBlocks;
7243 int i;
7246 * Figure out how many blocks are needed to contain the new size
7248 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7250 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7251 numBlocks++;
7253 if (numBlocks)
7256 * Go to the new end of chain
7258 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7260 /* Mark the new end of chain */
7261 StorageImpl_SetNextBlockInChain(
7262 This->parentStorage,
7263 blockIndex,
7264 BLOCK_END_OF_CHAIN);
7266 This->tailIndex = blockIndex;
7268 else
7270 if (This->headOfStreamPlaceHolder != 0)
7272 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7274 else
7276 DirEntry chainEntry;
7277 assert(This->ownerDirEntry != DIRENTRY_NULL);
7279 StorageImpl_ReadDirEntry(
7280 This->parentStorage,
7281 This->ownerDirEntry,
7282 &chainEntry);
7284 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7286 StorageImpl_WriteDirEntry(
7287 This->parentStorage,
7288 This->ownerDirEntry,
7289 &chainEntry);
7292 This->tailIndex = BLOCK_END_OF_CHAIN;
7295 This->numBlocks = numBlocks;
7298 * Mark the extra blocks as free
7300 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7302 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7303 StorageImpl_FreeBigBlock(This->parentStorage,
7304 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7305 if (last_run->lastOffset == last_run->firstOffset)
7306 This->indexCacheLen--;
7307 else
7308 last_run->lastOffset--;
7312 * Reset the last accessed block cache.
7314 for (i=0; i<2; i++)
7316 if (This->cachedBlocks[i].index >= numBlocks)
7318 This->cachedBlocks[i].index = 0xffffffff;
7319 This->cachedBlocks[i].dirty = FALSE;
7323 return TRUE;
7326 /******************************************************************************
7327 * BlockChainStream_Enlarge
7329 * Grows this chain in the big block depot.
7331 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7332 ULARGE_INTEGER newSize)
7334 ULONG blockIndex, currentBlock;
7335 ULONG newNumBlocks;
7336 ULONG oldNumBlocks = 0;
7338 blockIndex = BlockChainStream_GetHeadOfChain(This);
7341 * Empty chain. Create the head.
7343 if (blockIndex == BLOCK_END_OF_CHAIN)
7345 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7346 StorageImpl_SetNextBlockInChain(This->parentStorage,
7347 blockIndex,
7348 BLOCK_END_OF_CHAIN);
7350 if (This->headOfStreamPlaceHolder != 0)
7352 *(This->headOfStreamPlaceHolder) = blockIndex;
7354 else
7356 DirEntry chainEntry;
7357 assert(This->ownerDirEntry != DIRENTRY_NULL);
7359 StorageImpl_ReadDirEntry(
7360 This->parentStorage,
7361 This->ownerDirEntry,
7362 &chainEntry);
7364 chainEntry.startingBlock = blockIndex;
7366 StorageImpl_WriteDirEntry(
7367 This->parentStorage,
7368 This->ownerDirEntry,
7369 &chainEntry);
7372 This->tailIndex = blockIndex;
7373 This->numBlocks = 1;
7377 * Figure out how many blocks are needed to contain this stream
7379 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7381 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7382 newNumBlocks++;
7385 * Go to the current end of chain
7387 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7389 currentBlock = blockIndex;
7391 while (blockIndex != BLOCK_END_OF_CHAIN)
7393 This->numBlocks++;
7394 currentBlock = blockIndex;
7396 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7397 &blockIndex)))
7398 return FALSE;
7401 This->tailIndex = currentBlock;
7404 currentBlock = This->tailIndex;
7405 oldNumBlocks = This->numBlocks;
7408 * Add new blocks to the chain
7410 if (oldNumBlocks < newNumBlocks)
7412 while (oldNumBlocks < newNumBlocks)
7414 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7416 StorageImpl_SetNextBlockInChain(
7417 This->parentStorage,
7418 currentBlock,
7419 blockIndex);
7421 StorageImpl_SetNextBlockInChain(
7422 This->parentStorage,
7423 blockIndex,
7424 BLOCK_END_OF_CHAIN);
7426 currentBlock = blockIndex;
7427 oldNumBlocks++;
7430 This->tailIndex = blockIndex;
7431 This->numBlocks = newNumBlocks;
7434 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7435 return FALSE;
7437 return TRUE;
7441 /******************************************************************************
7442 * BlockChainStream_GetSize
7444 * Returns the size of this chain.
7445 * Will return the block count if this chain doesn't have a directory entry.
7447 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7449 DirEntry chainEntry;
7451 if(This->headOfStreamPlaceHolder == NULL)
7454 * This chain has a directory entry so use the size value from there.
7456 StorageImpl_ReadDirEntry(
7457 This->parentStorage,
7458 This->ownerDirEntry,
7459 &chainEntry);
7461 return chainEntry.size;
7463 else
7466 * this chain is a chain that does not have a directory entry, figure out the
7467 * size by making the product number of used blocks times the
7468 * size of them
7470 ULARGE_INTEGER result;
7471 result.QuadPart =
7472 (ULONGLONG)BlockChainStream_GetCount(This) *
7473 This->parentStorage->bigBlockSize;
7475 return result;
7479 /******************************************************************************
7480 * BlockChainStream_SetSize
7482 * Sets the size of this stream. The big block depot will be updated.
7483 * The file will grow if we grow the chain.
7485 * TODO: Free the actual blocks in the file when we shrink the chain.
7486 * Currently, the blocks are still in the file. So the file size
7487 * doesn't shrink even if we shrink streams.
7489 BOOL BlockChainStream_SetSize(
7490 BlockChainStream* This,
7491 ULARGE_INTEGER newSize)
7493 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7495 if (newSize.QuadPart == size.QuadPart)
7496 return TRUE;
7498 if (newSize.QuadPart < size.QuadPart)
7500 BlockChainStream_Shrink(This, newSize);
7502 else
7504 BlockChainStream_Enlarge(This, newSize);
7507 return TRUE;
7510 /******************************************************************************
7511 * BlockChainStream_ReadAt
7513 * Reads a specified number of bytes from this chain at the specified offset.
7514 * bytesRead may be NULL.
7515 * Failure will be returned if the specified number of bytes has not been read.
7517 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7518 ULARGE_INTEGER offset,
7519 ULONG size,
7520 void* buffer,
7521 ULONG* bytesRead)
7523 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7524 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7525 ULONG bytesToReadInBuffer;
7526 ULONG blockIndex;
7527 BYTE* bufferWalker;
7528 ULARGE_INTEGER stream_size;
7529 HRESULT hr;
7530 BlockChainBlock *cachedBlock;
7532 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7535 * Find the first block in the stream that contains part of the buffer.
7537 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7539 *bytesRead = 0;
7541 stream_size = BlockChainStream_GetSize(This);
7542 if (stream_size.QuadPart > offset.QuadPart)
7543 size = min(stream_size.QuadPart - offset.QuadPart, size);
7544 else
7545 return S_OK;
7548 * Start reading the buffer.
7550 bufferWalker = buffer;
7552 while (size > 0)
7554 ULARGE_INTEGER ulOffset;
7555 DWORD bytesReadAt;
7558 * Calculate how many bytes we can copy from this big block.
7560 bytesToReadInBuffer =
7561 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7563 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7565 if (FAILED(hr))
7566 return hr;
7568 if (!cachedBlock)
7570 /* Not in cache, and we're going to read past the end of the block. */
7571 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7572 offsetInBlock;
7574 StorageImpl_ReadAt(This->parentStorage,
7575 ulOffset,
7576 bufferWalker,
7577 bytesToReadInBuffer,
7578 &bytesReadAt);
7580 else
7582 if (!cachedBlock->read)
7584 ULONG read;
7585 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7586 return STG_E_READFAULT;
7588 cachedBlock->read = TRUE;
7591 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7592 bytesReadAt = bytesToReadInBuffer;
7595 blockNoInSequence++;
7596 bufferWalker += bytesReadAt;
7597 size -= bytesReadAt;
7598 *bytesRead += bytesReadAt;
7599 offsetInBlock = 0; /* There is no offset on the next block */
7601 if (bytesToReadInBuffer != bytesReadAt)
7602 break;
7605 return S_OK;
7608 /******************************************************************************
7609 * BlockChainStream_WriteAt
7611 * Writes the specified number of bytes to this chain at the specified offset.
7612 * Will fail if not all specified number of bytes have been written.
7614 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7615 ULARGE_INTEGER offset,
7616 ULONG size,
7617 const void* buffer,
7618 ULONG* bytesWritten)
7620 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7621 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7622 ULONG bytesToWrite;
7623 ULONG blockIndex;
7624 const BYTE* bufferWalker;
7625 HRESULT hr;
7626 BlockChainBlock *cachedBlock;
7628 *bytesWritten = 0;
7629 bufferWalker = buffer;
7631 while (size > 0)
7633 ULARGE_INTEGER ulOffset;
7634 DWORD bytesWrittenAt;
7637 * Calculate how many bytes we can copy to this big block.
7639 bytesToWrite =
7640 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7642 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7644 /* BlockChainStream_SetSize should have already been called to ensure we have
7645 * enough blocks in the chain to write into */
7646 if (FAILED(hr))
7648 ERR("not enough blocks in chain to write data\n");
7649 return hr;
7652 if (!cachedBlock)
7654 /* Not in cache, and we're going to write past the end of the block. */
7655 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7656 offsetInBlock;
7658 StorageImpl_WriteAt(This->parentStorage,
7659 ulOffset,
7660 bufferWalker,
7661 bytesToWrite,
7662 &bytesWrittenAt);
7664 else
7666 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7668 ULONG read;
7669 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7670 return STG_E_READFAULT;
7673 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7674 bytesWrittenAt = bytesToWrite;
7675 cachedBlock->read = TRUE;
7676 cachedBlock->dirty = TRUE;
7679 blockNoInSequence++;
7680 bufferWalker += bytesWrittenAt;
7681 size -= bytesWrittenAt;
7682 *bytesWritten += bytesWrittenAt;
7683 offsetInBlock = 0; /* There is no offset on the next block */
7685 if (bytesWrittenAt != bytesToWrite)
7686 break;
7689 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7693 /************************************************************************
7694 * SmallBlockChainStream implementation
7695 ***********************************************************************/
7697 SmallBlockChainStream* SmallBlockChainStream_Construct(
7698 StorageImpl* parentStorage,
7699 ULONG* headOfStreamPlaceHolder,
7700 DirRef dirEntry)
7702 SmallBlockChainStream* newStream;
7704 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7706 newStream->parentStorage = parentStorage;
7707 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7708 newStream->ownerDirEntry = dirEntry;
7710 return newStream;
7713 void SmallBlockChainStream_Destroy(
7714 SmallBlockChainStream* This)
7716 HeapFree(GetProcessHeap(), 0, This);
7719 /******************************************************************************
7720 * SmallBlockChainStream_GetHeadOfChain
7722 * Returns the head of this chain of small blocks.
7724 static ULONG SmallBlockChainStream_GetHeadOfChain(
7725 SmallBlockChainStream* This)
7727 DirEntry chainEntry;
7728 HRESULT hr;
7730 if (This->headOfStreamPlaceHolder != NULL)
7731 return *(This->headOfStreamPlaceHolder);
7733 if (This->ownerDirEntry)
7735 hr = StorageImpl_ReadDirEntry(
7736 This->parentStorage,
7737 This->ownerDirEntry,
7738 &chainEntry);
7740 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7741 return chainEntry.startingBlock;
7744 return BLOCK_END_OF_CHAIN;
7747 /******************************************************************************
7748 * SmallBlockChainStream_GetNextBlockInChain
7750 * Returns the index of the next small block in this chain.
7752 * Return Values:
7753 * - BLOCK_END_OF_CHAIN: end of this chain
7754 * - BLOCK_UNUSED: small block 'blockIndex' is free
7756 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7757 SmallBlockChainStream* This,
7758 ULONG blockIndex,
7759 ULONG* nextBlockInChain)
7761 ULARGE_INTEGER offsetOfBlockInDepot;
7762 DWORD buffer;
7763 ULONG bytesRead;
7764 HRESULT res;
7766 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7768 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7771 * Read those bytes in the buffer from the small block file.
7773 res = BlockChainStream_ReadAt(
7774 This->parentStorage->smallBlockDepotChain,
7775 offsetOfBlockInDepot,
7776 sizeof(DWORD),
7777 &buffer,
7778 &bytesRead);
7780 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7781 res = STG_E_READFAULT;
7783 if (SUCCEEDED(res))
7785 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7786 return S_OK;
7789 return res;
7792 /******************************************************************************
7793 * SmallBlockChainStream_SetNextBlockInChain
7795 * Writes the index of the next block of the specified block in the small
7796 * block depot.
7797 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7798 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7800 static void SmallBlockChainStream_SetNextBlockInChain(
7801 SmallBlockChainStream* This,
7802 ULONG blockIndex,
7803 ULONG nextBlock)
7805 ULARGE_INTEGER offsetOfBlockInDepot;
7806 DWORD buffer;
7807 ULONG bytesWritten;
7809 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7811 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7814 * Read those bytes in the buffer from the small block file.
7816 BlockChainStream_WriteAt(
7817 This->parentStorage->smallBlockDepotChain,
7818 offsetOfBlockInDepot,
7819 sizeof(DWORD),
7820 &buffer,
7821 &bytesWritten);
7824 /******************************************************************************
7825 * SmallBlockChainStream_FreeBlock
7827 * Flag small block 'blockIndex' as free in the small block depot.
7829 static void SmallBlockChainStream_FreeBlock(
7830 SmallBlockChainStream* This,
7831 ULONG blockIndex)
7833 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7836 /******************************************************************************
7837 * SmallBlockChainStream_GetNextFreeBlock
7839 * Returns the index of a free small block. The small block depot will be
7840 * enlarged if necessary. The small block chain will also be enlarged if
7841 * necessary.
7843 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7844 SmallBlockChainStream* This)
7846 ULARGE_INTEGER offsetOfBlockInDepot;
7847 DWORD buffer;
7848 ULONG bytesRead;
7849 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7850 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7851 HRESULT res = S_OK;
7852 ULONG smallBlocksPerBigBlock;
7853 DirEntry rootEntry;
7854 ULONG blocksRequired;
7855 ULARGE_INTEGER old_size, size_required;
7857 offsetOfBlockInDepot.u.HighPart = 0;
7860 * Scan the small block depot for a free block
7862 while (nextBlockIndex != BLOCK_UNUSED)
7864 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7866 res = BlockChainStream_ReadAt(
7867 This->parentStorage->smallBlockDepotChain,
7868 offsetOfBlockInDepot,
7869 sizeof(DWORD),
7870 &buffer,
7871 &bytesRead);
7874 * If we run out of space for the small block depot, enlarge it
7876 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7878 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7880 if (nextBlockIndex != BLOCK_UNUSED)
7881 blockIndex++;
7883 else
7885 ULONG count =
7886 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7888 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7889 ULARGE_INTEGER newSize, offset;
7890 ULONG bytesWritten;
7892 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7893 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7896 * Initialize all the small blocks to free
7898 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7899 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7900 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7901 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7903 StorageImpl_SaveFileHeader(This->parentStorage);
7907 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7909 smallBlocksPerBigBlock =
7910 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7913 * Verify if we have to allocate big blocks to contain small blocks
7915 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7917 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7919 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7921 if (size_required.QuadPart > old_size.QuadPart)
7923 BlockChainStream_SetSize(
7924 This->parentStorage->smallBlockRootChain,
7925 size_required);
7927 StorageImpl_ReadDirEntry(
7928 This->parentStorage,
7929 This->parentStorage->base.storageDirEntry,
7930 &rootEntry);
7932 rootEntry.size = size_required;
7934 StorageImpl_WriteDirEntry(
7935 This->parentStorage,
7936 This->parentStorage->base.storageDirEntry,
7937 &rootEntry);
7940 return blockIndex;
7943 /******************************************************************************
7944 * SmallBlockChainStream_ReadAt
7946 * Reads a specified number of bytes from this chain at the specified offset.
7947 * bytesRead may be NULL.
7948 * Failure will be returned if the specified number of bytes has not been read.
7950 HRESULT SmallBlockChainStream_ReadAt(
7951 SmallBlockChainStream* This,
7952 ULARGE_INTEGER offset,
7953 ULONG size,
7954 void* buffer,
7955 ULONG* bytesRead)
7957 HRESULT rc = S_OK;
7958 ULARGE_INTEGER offsetInBigBlockFile;
7959 ULONG blockNoInSequence =
7960 offset.u.LowPart / This->parentStorage->smallBlockSize;
7962 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7963 ULONG bytesToReadInBuffer;
7964 ULONG blockIndex;
7965 ULONG bytesReadFromBigBlockFile;
7966 BYTE* bufferWalker;
7967 ULARGE_INTEGER stream_size;
7970 * This should never happen on a small block file.
7972 assert(offset.u.HighPart==0);
7974 *bytesRead = 0;
7976 stream_size = SmallBlockChainStream_GetSize(This);
7977 if (stream_size.QuadPart > offset.QuadPart)
7978 size = min(stream_size.QuadPart - offset.QuadPart, size);
7979 else
7980 return S_OK;
7983 * Find the first block in the stream that contains part of the buffer.
7985 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7987 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7989 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7990 if(FAILED(rc))
7991 return rc;
7992 blockNoInSequence--;
7996 * Start reading the buffer.
7998 bufferWalker = buffer;
8000 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8003 * Calculate how many bytes we can copy from this small block.
8005 bytesToReadInBuffer =
8006 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8009 * Calculate the offset of the small block in the small block file.
8011 offsetInBigBlockFile.QuadPart =
8012 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8014 offsetInBigBlockFile.QuadPart += offsetInBlock;
8017 * Read those bytes in the buffer from the small block file.
8018 * The small block has already been identified so it shouldn't fail
8019 * unless the file is corrupt.
8021 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8022 offsetInBigBlockFile,
8023 bytesToReadInBuffer,
8024 bufferWalker,
8025 &bytesReadFromBigBlockFile);
8027 if (FAILED(rc))
8028 return rc;
8030 if (!bytesReadFromBigBlockFile)
8031 return STG_E_DOCFILECORRUPT;
8034 * Step to the next big block.
8036 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8037 if(FAILED(rc))
8038 return STG_E_DOCFILECORRUPT;
8040 bufferWalker += bytesReadFromBigBlockFile;
8041 size -= bytesReadFromBigBlockFile;
8042 *bytesRead += bytesReadFromBigBlockFile;
8043 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8046 return S_OK;
8049 /******************************************************************************
8050 * SmallBlockChainStream_WriteAt
8052 * Writes the specified number of bytes to this chain at the specified offset.
8053 * Will fail if not all specified number of bytes have been written.
8055 HRESULT SmallBlockChainStream_WriteAt(
8056 SmallBlockChainStream* This,
8057 ULARGE_INTEGER offset,
8058 ULONG size,
8059 const void* buffer,
8060 ULONG* bytesWritten)
8062 ULARGE_INTEGER offsetInBigBlockFile;
8063 ULONG blockNoInSequence =
8064 offset.u.LowPart / This->parentStorage->smallBlockSize;
8066 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8067 ULONG bytesToWriteInBuffer;
8068 ULONG blockIndex;
8069 ULONG bytesWrittenToBigBlockFile;
8070 const BYTE* bufferWalker;
8071 HRESULT res;
8074 * This should never happen on a small block file.
8076 assert(offset.u.HighPart==0);
8079 * Find the first block in the stream that contains part of the buffer.
8081 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8083 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8085 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8086 return STG_E_DOCFILECORRUPT;
8087 blockNoInSequence--;
8091 * Start writing the buffer.
8093 *bytesWritten = 0;
8094 bufferWalker = buffer;
8095 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8098 * Calculate how many bytes we can copy to this small block.
8100 bytesToWriteInBuffer =
8101 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8104 * Calculate the offset of the small block in the small block file.
8106 offsetInBigBlockFile.QuadPart =
8107 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8109 offsetInBigBlockFile.QuadPart += offsetInBlock;
8112 * Write those bytes in the buffer to the small block file.
8114 res = BlockChainStream_WriteAt(
8115 This->parentStorage->smallBlockRootChain,
8116 offsetInBigBlockFile,
8117 bytesToWriteInBuffer,
8118 bufferWalker,
8119 &bytesWrittenToBigBlockFile);
8120 if (FAILED(res))
8121 return res;
8124 * Step to the next big block.
8126 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8127 if (FAILED(res))
8128 return res;
8129 bufferWalker += bytesWrittenToBigBlockFile;
8130 size -= bytesWrittenToBigBlockFile;
8131 *bytesWritten += bytesWrittenToBigBlockFile;
8132 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8135 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8138 /******************************************************************************
8139 * SmallBlockChainStream_Shrink
8141 * Shrinks this chain in the small block depot.
8143 static BOOL SmallBlockChainStream_Shrink(
8144 SmallBlockChainStream* This,
8145 ULARGE_INTEGER newSize)
8147 ULONG blockIndex, extraBlock;
8148 ULONG numBlocks;
8149 ULONG count = 0;
8151 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8153 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8154 numBlocks++;
8156 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8159 * Go to the new end of chain
8161 while (count < numBlocks)
8163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8164 &blockIndex)))
8165 return FALSE;
8166 count++;
8170 * If the count is 0, we have a special case, the head of the chain was
8171 * just freed.
8173 if (count == 0)
8175 DirEntry chainEntry;
8177 StorageImpl_ReadDirEntry(This->parentStorage,
8178 This->ownerDirEntry,
8179 &chainEntry);
8181 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8183 StorageImpl_WriteDirEntry(This->parentStorage,
8184 This->ownerDirEntry,
8185 &chainEntry);
8188 * We start freeing the chain at the head block.
8190 extraBlock = blockIndex;
8192 else
8194 /* Get the next block before marking the new end */
8195 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8196 &extraBlock)))
8197 return FALSE;
8199 /* Mark the new end of chain */
8200 SmallBlockChainStream_SetNextBlockInChain(
8201 This,
8202 blockIndex,
8203 BLOCK_END_OF_CHAIN);
8207 * Mark the extra blocks as free
8209 while (extraBlock != BLOCK_END_OF_CHAIN)
8211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8212 &blockIndex)))
8213 return FALSE;
8214 SmallBlockChainStream_FreeBlock(This, extraBlock);
8215 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8216 extraBlock = blockIndex;
8219 return TRUE;
8222 /******************************************************************************
8223 * SmallBlockChainStream_Enlarge
8225 * Grows this chain in the small block depot.
8227 static BOOL SmallBlockChainStream_Enlarge(
8228 SmallBlockChainStream* This,
8229 ULARGE_INTEGER newSize)
8231 ULONG blockIndex, currentBlock;
8232 ULONG newNumBlocks;
8233 ULONG oldNumBlocks = 0;
8235 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8238 * Empty chain. Create the head.
8240 if (blockIndex == BLOCK_END_OF_CHAIN)
8242 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8243 SmallBlockChainStream_SetNextBlockInChain(
8244 This,
8245 blockIndex,
8246 BLOCK_END_OF_CHAIN);
8248 if (This->headOfStreamPlaceHolder != NULL)
8250 *(This->headOfStreamPlaceHolder) = blockIndex;
8252 else
8254 DirEntry chainEntry;
8256 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8257 &chainEntry);
8259 chainEntry.startingBlock = blockIndex;
8261 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8262 &chainEntry);
8266 currentBlock = blockIndex;
8269 * Figure out how many blocks are needed to contain this stream
8271 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8273 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8274 newNumBlocks++;
8277 * Go to the current end of chain
8279 while (blockIndex != BLOCK_END_OF_CHAIN)
8281 oldNumBlocks++;
8282 currentBlock = blockIndex;
8283 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8284 return FALSE;
8288 * Add new blocks to the chain
8290 while (oldNumBlocks < newNumBlocks)
8292 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8293 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8295 SmallBlockChainStream_SetNextBlockInChain(
8296 This,
8297 blockIndex,
8298 BLOCK_END_OF_CHAIN);
8300 currentBlock = blockIndex;
8301 oldNumBlocks++;
8304 return TRUE;
8307 /******************************************************************************
8308 * SmallBlockChainStream_SetSize
8310 * Sets the size of this stream.
8311 * The file will grow if we grow the chain.
8313 * TODO: Free the actual blocks in the file when we shrink the chain.
8314 * Currently, the blocks are still in the file. So the file size
8315 * doesn't shrink even if we shrink streams.
8317 BOOL SmallBlockChainStream_SetSize(
8318 SmallBlockChainStream* This,
8319 ULARGE_INTEGER newSize)
8321 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8323 if (newSize.u.LowPart == size.u.LowPart)
8324 return TRUE;
8326 if (newSize.u.LowPart < size.u.LowPart)
8328 SmallBlockChainStream_Shrink(This, newSize);
8330 else
8332 SmallBlockChainStream_Enlarge(This, newSize);
8335 return TRUE;
8338 /******************************************************************************
8339 * SmallBlockChainStream_GetCount
8341 * Returns the number of small blocks that comprises this chain.
8342 * This is not the size of the stream as the last block may not be full!
8345 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8347 ULONG blockIndex;
8348 ULONG count = 0;
8350 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8352 while(blockIndex != BLOCK_END_OF_CHAIN)
8354 count++;
8356 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8357 blockIndex, &blockIndex)))
8358 return 0;
8361 return count;
8364 /******************************************************************************
8365 * SmallBlockChainStream_GetSize
8367 * Returns the size of this chain.
8369 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8371 DirEntry chainEntry;
8373 if(This->headOfStreamPlaceHolder != NULL)
8375 ULARGE_INTEGER result;
8376 result.u.HighPart = 0;
8378 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8379 This->parentStorage->smallBlockSize;
8381 return result;
8384 StorageImpl_ReadDirEntry(
8385 This->parentStorage,
8386 This->ownerDirEntry,
8387 &chainEntry);
8389 return chainEntry.size;
8393 /************************************************************************
8394 * Miscellaneous storage functions
8395 ***********************************************************************/
8397 static HRESULT create_storagefile(
8398 LPCOLESTR pwcsName,
8399 DWORD grfMode,
8400 DWORD grfAttrs,
8401 STGOPTIONS* pStgOptions,
8402 REFIID riid,
8403 void** ppstgOpen)
8405 StorageBaseImpl* newStorage = 0;
8406 HANDLE hFile = INVALID_HANDLE_VALUE;
8407 HRESULT hr = STG_E_INVALIDFLAG;
8408 DWORD shareMode;
8409 DWORD accessMode;
8410 DWORD creationMode;
8411 DWORD fileAttributes;
8412 WCHAR tempFileName[MAX_PATH];
8414 if (ppstgOpen == 0)
8415 return STG_E_INVALIDPOINTER;
8417 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8418 return STG_E_INVALIDPARAMETER;
8420 /* if no share mode given then DENY_NONE is the default */
8421 if (STGM_SHARE_MODE(grfMode) == 0)
8422 grfMode |= STGM_SHARE_DENY_NONE;
8424 if ( FAILED( validateSTGM(grfMode) ))
8425 goto end;
8427 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8428 switch(STGM_ACCESS_MODE(grfMode))
8430 case STGM_WRITE:
8431 case STGM_READWRITE:
8432 break;
8433 default:
8434 goto end;
8437 /* in direct mode, can only use SHARE_EXCLUSIVE */
8438 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8439 goto end;
8441 /* but in transacted mode, any share mode is valid */
8444 * Generate a unique name.
8446 if (pwcsName == 0)
8448 WCHAR tempPath[MAX_PATH];
8449 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8451 memset(tempPath, 0, sizeof(tempPath));
8452 memset(tempFileName, 0, sizeof(tempFileName));
8454 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8455 tempPath[0] = '.';
8457 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8458 pwcsName = tempFileName;
8459 else
8461 hr = STG_E_INSUFFICIENTMEMORY;
8462 goto end;
8465 creationMode = TRUNCATE_EXISTING;
8467 else
8469 creationMode = GetCreationModeFromSTGM(grfMode);
8473 * Interpret the STGM value grfMode
8475 shareMode = GetShareModeFromSTGM(grfMode);
8476 accessMode = GetAccessModeFromSTGM(grfMode);
8478 if (grfMode & STGM_DELETEONRELEASE)
8479 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8480 else
8481 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8483 *ppstgOpen = 0;
8485 hFile = CreateFileW(pwcsName,
8486 accessMode,
8487 shareMode,
8488 NULL,
8489 creationMode,
8490 fileAttributes,
8493 if (hFile == INVALID_HANDLE_VALUE)
8495 if(GetLastError() == ERROR_FILE_EXISTS)
8496 hr = STG_E_FILEALREADYEXISTS;
8497 else
8498 hr = E_FAIL;
8499 goto end;
8503 * Allocate and initialize the new IStorage object.
8505 hr = Storage_Construct(
8506 hFile,
8507 pwcsName,
8508 NULL,
8509 grfMode,
8510 TRUE,
8511 TRUE,
8512 pStgOptions->ulSectorSize,
8513 &newStorage);
8515 if (FAILED(hr))
8517 goto end;
8520 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8521 IStorage_Release(&newStorage->IStorage_iface);
8523 end:
8524 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8526 return hr;
8529 /******************************************************************************
8530 * StgCreateDocfile [OLE32.@]
8531 * Creates a new compound file storage object
8533 * PARAMS
8534 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8535 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8536 * reserved [ ?] unused?, usually 0
8537 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8539 * RETURNS
8540 * S_OK if the file was successfully created
8541 * some STG_E_ value if error
8542 * NOTES
8543 * if pwcsName is NULL, create file with new unique name
8544 * the function can returns
8545 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8546 * (unrealized now)
8548 HRESULT WINAPI StgCreateDocfile(
8549 LPCOLESTR pwcsName,
8550 DWORD grfMode,
8551 DWORD reserved,
8552 IStorage **ppstgOpen)
8554 STGOPTIONS stgoptions = {1, 0, 512};
8556 TRACE("(%s, %x, %d, %p)\n",
8557 debugstr_w(pwcsName), grfMode,
8558 reserved, ppstgOpen);
8560 if (ppstgOpen == 0)
8561 return STG_E_INVALIDPOINTER;
8562 if (reserved != 0)
8563 return STG_E_INVALIDPARAMETER;
8565 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8568 /******************************************************************************
8569 * StgCreateStorageEx [OLE32.@]
8571 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8573 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8574 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8576 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8578 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8579 return STG_E_INVALIDPARAMETER;
8582 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8584 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8585 return STG_E_INVALIDPARAMETER;
8588 if (stgfmt == STGFMT_FILE)
8590 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8591 return STG_E_INVALIDPARAMETER;
8594 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8596 STGOPTIONS defaultOptions = {1, 0, 512};
8598 if (!pStgOptions) pStgOptions = &defaultOptions;
8599 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8603 ERR("Invalid stgfmt argument\n");
8604 return STG_E_INVALIDPARAMETER;
8607 /******************************************************************************
8608 * StgCreatePropSetStg [OLE32.@]
8610 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8611 IPropertySetStorage **propset)
8613 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8614 if (reserved)
8615 return STG_E_INVALIDPARAMETER;
8617 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8620 /******************************************************************************
8621 * StgOpenStorageEx [OLE32.@]
8623 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8625 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8626 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8628 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8630 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8631 return STG_E_INVALIDPARAMETER;
8634 switch (stgfmt)
8636 case STGFMT_FILE:
8637 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8638 return STG_E_INVALIDPARAMETER;
8640 case STGFMT_STORAGE:
8641 break;
8643 case STGFMT_DOCFILE:
8644 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8646 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8647 return STG_E_INVALIDPARAMETER;
8649 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8650 break;
8652 case STGFMT_ANY:
8653 WARN("STGFMT_ANY assuming storage\n");
8654 break;
8656 default:
8657 return STG_E_INVALIDPARAMETER;
8660 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8664 /******************************************************************************
8665 * StgOpenStorage [OLE32.@]
8667 HRESULT WINAPI StgOpenStorage(
8668 const OLECHAR *pwcsName,
8669 IStorage *pstgPriority,
8670 DWORD grfMode,
8671 SNB snbExclude,
8672 DWORD reserved,
8673 IStorage **ppstgOpen)
8675 StorageBaseImpl* newStorage = 0;
8676 HRESULT hr = S_OK;
8677 HANDLE hFile = 0;
8678 DWORD shareMode;
8679 DWORD accessMode;
8680 LPWSTR temp_name = NULL;
8682 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8683 debugstr_w(pwcsName), pstgPriority, grfMode,
8684 snbExclude, reserved, ppstgOpen);
8686 if (pstgPriority)
8688 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8689 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8690 if (FAILED(hr)) goto end;
8691 pwcsName = temp_name;
8692 TRACE("using filename %s\n", debugstr_w(temp_name));
8695 if (pwcsName == 0)
8697 hr = STG_E_INVALIDNAME;
8698 goto end;
8701 if (ppstgOpen == 0)
8703 hr = STG_E_INVALIDPOINTER;
8704 goto end;
8707 if (reserved)
8709 hr = STG_E_INVALIDPARAMETER;
8710 goto end;
8713 if (grfMode & STGM_PRIORITY)
8715 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8716 return STG_E_INVALIDFLAG;
8717 if (grfMode & STGM_DELETEONRELEASE)
8718 return STG_E_INVALIDFUNCTION;
8719 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8720 return STG_E_INVALIDFLAG;
8721 grfMode &= ~0xf0; /* remove the existing sharing mode */
8722 grfMode |= STGM_SHARE_DENY_NONE;
8726 * Validate the sharing mode
8728 if (grfMode & STGM_DIRECT_SWMR)
8730 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8731 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8733 hr = STG_E_INVALIDFLAG;
8734 goto end;
8737 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8738 switch(STGM_SHARE_MODE(grfMode))
8740 case STGM_SHARE_EXCLUSIVE:
8741 case STGM_SHARE_DENY_WRITE:
8742 break;
8743 default:
8744 hr = STG_E_INVALIDFLAG;
8745 goto end;
8748 if ( FAILED( validateSTGM(grfMode) ) ||
8749 (grfMode&STGM_CREATE))
8751 hr = STG_E_INVALIDFLAG;
8752 goto end;
8755 /* shared reading requires transacted or single writer mode */
8756 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8757 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8758 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8760 hr = STG_E_INVALIDFLAG;
8761 goto end;
8765 * Interpret the STGM value grfMode
8767 shareMode = GetShareModeFromSTGM(grfMode);
8768 accessMode = GetAccessModeFromSTGM(grfMode);
8770 *ppstgOpen = 0;
8772 hFile = CreateFileW( pwcsName,
8773 accessMode,
8774 shareMode,
8775 NULL,
8776 OPEN_EXISTING,
8777 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8780 if (hFile==INVALID_HANDLE_VALUE)
8782 DWORD last_error = GetLastError();
8784 hr = E_FAIL;
8786 switch (last_error)
8788 case ERROR_FILE_NOT_FOUND:
8789 hr = STG_E_FILENOTFOUND;
8790 break;
8792 case ERROR_PATH_NOT_FOUND:
8793 hr = STG_E_PATHNOTFOUND;
8794 break;
8796 case ERROR_ACCESS_DENIED:
8797 case ERROR_WRITE_PROTECT:
8798 hr = STG_E_ACCESSDENIED;
8799 break;
8801 case ERROR_SHARING_VIOLATION:
8802 hr = STG_E_SHAREVIOLATION;
8803 break;
8805 default:
8806 hr = E_FAIL;
8809 goto end;
8813 * Refuse to open the file if it's too small to be a structured storage file
8814 * FIXME: verify the file when reading instead of here
8816 if (GetFileSize(hFile, NULL) < 0x100)
8818 CloseHandle(hFile);
8819 hr = STG_E_FILEALREADYEXISTS;
8820 goto end;
8824 * Allocate and initialize the new IStorage object.
8826 hr = Storage_Construct(
8827 hFile,
8828 pwcsName,
8829 NULL,
8830 grfMode,
8831 TRUE,
8832 FALSE,
8833 512,
8834 &newStorage);
8836 if (FAILED(hr))
8839 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8841 if(hr == STG_E_INVALIDHEADER)
8842 hr = STG_E_FILEALREADYEXISTS;
8843 goto end;
8846 *ppstgOpen = &newStorage->IStorage_iface;
8848 end:
8849 CoTaskMemFree(temp_name);
8850 if (pstgPriority) IStorage_Release(pstgPriority);
8851 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8852 return hr;
8855 /******************************************************************************
8856 * StgCreateDocfileOnILockBytes [OLE32.@]
8858 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8859 ILockBytes *plkbyt,
8860 DWORD grfMode,
8861 DWORD reserved,
8862 IStorage** ppstgOpen)
8864 StorageBaseImpl* newStorage = 0;
8865 HRESULT hr = S_OK;
8867 if ((ppstgOpen == 0) || (plkbyt == 0))
8868 return STG_E_INVALIDPOINTER;
8871 * Allocate and initialize the new IStorage object.
8873 hr = Storage_Construct(
8876 plkbyt,
8877 grfMode,
8878 FALSE,
8879 TRUE,
8880 512,
8881 &newStorage);
8883 if (FAILED(hr))
8885 return hr;
8888 *ppstgOpen = &newStorage->IStorage_iface;
8890 return hr;
8893 /******************************************************************************
8894 * StgOpenStorageOnILockBytes [OLE32.@]
8896 HRESULT WINAPI StgOpenStorageOnILockBytes(
8897 ILockBytes *plkbyt,
8898 IStorage *pstgPriority,
8899 DWORD grfMode,
8900 SNB snbExclude,
8901 DWORD reserved,
8902 IStorage **ppstgOpen)
8904 StorageBaseImpl* newStorage = 0;
8905 HRESULT hr = S_OK;
8907 if ((plkbyt == 0) || (ppstgOpen == 0))
8908 return STG_E_INVALIDPOINTER;
8910 if ( FAILED( validateSTGM(grfMode) ))
8911 return STG_E_INVALIDFLAG;
8913 *ppstgOpen = 0;
8916 * Allocate and initialize the new IStorage object.
8918 hr = Storage_Construct(
8921 plkbyt,
8922 grfMode,
8923 FALSE,
8924 FALSE,
8925 512,
8926 &newStorage);
8928 if (FAILED(hr))
8930 return hr;
8933 *ppstgOpen = &newStorage->IStorage_iface;
8935 return hr;
8938 /******************************************************************************
8939 * StgSetTimes [ole32.@]
8940 * StgSetTimes [OLE32.@]
8944 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8945 FILETIME const *patime, FILETIME const *pmtime)
8947 IStorage *stg = NULL;
8948 HRESULT r;
8950 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8952 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8953 0, 0, &stg);
8954 if( SUCCEEDED(r) )
8956 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8957 IStorage_Release(stg);
8960 return r;
8963 /******************************************************************************
8964 * StgIsStorageILockBytes [OLE32.@]
8966 * Determines if the ILockBytes contains a storage object.
8968 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8970 BYTE sig[sizeof(STORAGE_magic)];
8971 ULARGE_INTEGER offset;
8972 ULONG read = 0;
8974 offset.u.HighPart = 0;
8975 offset.u.LowPart = 0;
8977 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8979 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8980 return S_OK;
8982 return S_FALSE;
8985 /******************************************************************************
8986 * WriteClassStg [OLE32.@]
8988 * This method will store the specified CLSID in the specified storage object
8990 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8992 if(!pStg)
8993 return E_INVALIDARG;
8995 if(!rclsid)
8996 return STG_E_INVALIDPOINTER;
8998 return IStorage_SetClass(pStg, rclsid);
9001 /***********************************************************************
9002 * ReadClassStg (OLE32.@)
9004 * This method reads the CLSID previously written to a storage object with
9005 * the WriteClassStg.
9007 * PARAMS
9008 * pstg [I] IStorage pointer
9009 * pclsid [O] Pointer to where the CLSID is written
9011 * RETURNS
9012 * Success: S_OK.
9013 * Failure: HRESULT code.
9015 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9017 STATSTG pstatstg;
9018 HRESULT hRes;
9020 TRACE("(%p, %p)\n", pstg, pclsid);
9022 if(!pstg || !pclsid)
9023 return E_INVALIDARG;
9026 * read a STATSTG structure (contains the clsid) from the storage
9028 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9030 if(SUCCEEDED(hRes))
9031 *pclsid=pstatstg.clsid;
9033 return hRes;
9036 /***********************************************************************
9037 * OleLoadFromStream (OLE32.@)
9039 * This function loads an object from stream
9041 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9043 CLSID clsid;
9044 HRESULT res;
9045 LPPERSISTSTREAM xstm;
9047 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9049 res=ReadClassStm(pStm,&clsid);
9050 if (FAILED(res))
9051 return res;
9052 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9053 if (FAILED(res))
9054 return res;
9055 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9056 if (FAILED(res)) {
9057 IUnknown_Release((IUnknown*)*ppvObj);
9058 return res;
9060 res=IPersistStream_Load(xstm,pStm);
9061 IPersistStream_Release(xstm);
9062 /* FIXME: all refcounts ok at this point? I think they should be:
9063 * pStm : unchanged
9064 * ppvObj : 1
9065 * xstm : 0 (released)
9067 return res;
9070 /***********************************************************************
9071 * OleSaveToStream (OLE32.@)
9073 * This function saves an object with the IPersistStream interface on it
9074 * to the specified stream.
9076 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9079 CLSID clsid;
9080 HRESULT res;
9082 TRACE("(%p,%p)\n",pPStm,pStm);
9084 res=IPersistStream_GetClassID(pPStm,&clsid);
9086 if (SUCCEEDED(res)){
9088 res=WriteClassStm(pStm,&clsid);
9090 if (SUCCEEDED(res))
9092 res=IPersistStream_Save(pPStm,pStm,TRUE);
9095 TRACE("Finished Save\n");
9096 return res;
9099 /*************************************************************************
9100 * STORAGE_CreateOleStream [Internal]
9102 * Creates the "\001OLE" stream in the IStorage if necessary.
9104 * PARAMS
9105 * storage [I] Dest storage to create the stream in
9106 * flags [I] flags to be set for newly created stream
9108 * RETURNS
9109 * HRESULT return value
9111 * NOTES
9113 * This stream is still unknown, MS Word seems to have extra data
9114 * but since the data is stored in the OLESTREAM there should be
9115 * no need to recreate the stream. If the stream is manually
9116 * deleted it will create it with this default data.
9119 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9121 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9122 static const DWORD version_magic = 0x02000001;
9123 IStream *stream;
9124 HRESULT hr;
9126 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9127 if (hr == S_OK)
9129 struct empty_1ole_stream {
9130 DWORD version_magic;
9131 DWORD flags;
9132 DWORD update_options;
9133 DWORD reserved;
9134 DWORD mon_stream_size;
9136 struct empty_1ole_stream stream_data;
9138 stream_data.version_magic = version_magic;
9139 stream_data.flags = flags;
9140 stream_data.update_options = 0;
9141 stream_data.reserved = 0;
9142 stream_data.mon_stream_size = 0;
9144 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9145 IStream_Release(stream);
9148 return hr;
9151 /* write a string to a stream, preceded by its length */
9152 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9154 HRESULT r;
9155 LPSTR str;
9156 DWORD len = 0;
9158 if( string )
9159 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9160 r = IStream_Write( stm, &len, sizeof(len), NULL);
9161 if( FAILED( r ) )
9162 return r;
9163 if(len == 0)
9164 return r;
9165 str = CoTaskMemAlloc( len );
9166 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9167 r = IStream_Write( stm, str, len, NULL);
9168 CoTaskMemFree( str );
9169 return r;
9172 /* read a string preceded by its length from a stream */
9173 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9175 HRESULT r;
9176 DWORD len, count = 0;
9177 LPSTR str;
9178 LPWSTR wstr;
9180 r = IStream_Read( stm, &len, sizeof(len), &count );
9181 if( FAILED( r ) )
9182 return r;
9183 if( count != sizeof(len) )
9184 return E_OUTOFMEMORY;
9186 TRACE("%d bytes\n",len);
9188 str = CoTaskMemAlloc( len );
9189 if( !str )
9190 return E_OUTOFMEMORY;
9191 count = 0;
9192 r = IStream_Read( stm, str, len, &count );
9193 if( FAILED( r ) )
9195 CoTaskMemFree( str );
9196 return r;
9198 if( count != len )
9200 CoTaskMemFree( str );
9201 return E_OUTOFMEMORY;
9204 TRACE("Read string %s\n",debugstr_an(str,len));
9206 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9207 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9208 if( wstr )
9210 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9211 wstr[len] = 0;
9213 CoTaskMemFree( str );
9215 *string = wstr;
9217 return r;
9221 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9222 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9224 IStream *pstm;
9225 HRESULT r = S_OK;
9226 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9228 static const BYTE unknown1[12] =
9229 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9230 0xFF, 0xFF, 0xFF, 0xFF};
9231 static const BYTE unknown2[16] =
9232 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9235 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9236 debugstr_w(lpszUserType), debugstr_w(szClipName),
9237 debugstr_w(szProgIDName));
9239 /* Create a CompObj stream */
9240 r = IStorage_CreateStream(pstg, szwStreamName,
9241 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9242 if( FAILED (r) )
9243 return r;
9245 /* Write CompObj Structure to stream */
9246 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9248 if( SUCCEEDED( r ) )
9249 r = WriteClassStm( pstm, clsid );
9251 if( SUCCEEDED( r ) )
9252 r = STREAM_WriteString( pstm, lpszUserType );
9253 if( SUCCEEDED( r ) )
9254 r = STREAM_WriteString( pstm, szClipName );
9255 if( SUCCEEDED( r ) )
9256 r = STREAM_WriteString( pstm, szProgIDName );
9257 if( SUCCEEDED( r ) )
9258 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9260 IStream_Release( pstm );
9262 return r;
9265 /***********************************************************************
9266 * WriteFmtUserTypeStg (OLE32.@)
9268 HRESULT WINAPI WriteFmtUserTypeStg(
9269 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9271 STATSTG stat;
9272 HRESULT r;
9273 WCHAR szwClipName[0x40];
9274 CLSID clsid;
9275 LPWSTR wstrProgID = NULL;
9276 DWORD n;
9278 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9280 /* get the clipboard format name */
9281 if( cf )
9283 n = GetClipboardFormatNameW( cf, szwClipName,
9284 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9285 szwClipName[n]=0;
9288 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9290 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9291 if(SUCCEEDED(r))
9292 clsid = stat.clsid;
9293 else
9294 clsid = CLSID_NULL;
9296 ProgIDFromCLSID(&clsid, &wstrProgID);
9298 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9300 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9301 cf ? szwClipName : NULL, wstrProgID );
9303 CoTaskMemFree(wstrProgID);
9305 return r;
9309 /******************************************************************************
9310 * ReadFmtUserTypeStg [OLE32.@]
9312 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9314 HRESULT r;
9315 IStream *stm = 0;
9316 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9317 unsigned char unknown1[12];
9318 unsigned char unknown2[16];
9319 DWORD count;
9320 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9321 CLSID clsid;
9323 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9325 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9326 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9327 if( FAILED ( r ) )
9329 WARN("Failed to open stream r = %08x\n", r);
9330 return r;
9333 /* read the various parts of the structure */
9334 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9335 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9336 goto end;
9337 r = ReadClassStm( stm, &clsid );
9338 if( FAILED( r ) )
9339 goto end;
9341 r = STREAM_ReadString( stm, &szCLSIDName );
9342 if( FAILED( r ) )
9343 goto end;
9345 r = STREAM_ReadString( stm, &szOleTypeName );
9346 if( FAILED( r ) )
9347 goto end;
9349 r = STREAM_ReadString( stm, &szProgIDName );
9350 if( FAILED( r ) )
9351 goto end;
9353 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9354 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9355 goto end;
9357 /* ok, success... now we just need to store what we found */
9358 if( pcf )
9359 *pcf = RegisterClipboardFormatW( szOleTypeName );
9361 if( lplpszUserType )
9363 *lplpszUserType = szCLSIDName;
9364 szCLSIDName = NULL;
9367 end:
9368 CoTaskMemFree( szCLSIDName );
9369 CoTaskMemFree( szOleTypeName );
9370 CoTaskMemFree( szProgIDName );
9371 IStream_Release( stm );
9373 return r;
9376 /******************************************************************************
9377 * StgIsStorageFile [OLE32.@]
9378 * Verify if the file contains a storage object
9380 * PARAMS
9381 * fn [ I] Filename
9383 * RETURNS
9384 * S_OK if file has magic bytes as a storage object
9385 * S_FALSE if file is not storage
9387 HRESULT WINAPI
9388 StgIsStorageFile(LPCOLESTR fn)
9390 HANDLE hf;
9391 BYTE magic[8];
9392 DWORD bytes_read;
9394 TRACE("%s\n", debugstr_w(fn));
9395 hf = CreateFileW(fn, GENERIC_READ,
9396 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9397 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9399 if (hf == INVALID_HANDLE_VALUE)
9400 return STG_E_FILENOTFOUND;
9402 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9404 WARN(" unable to read file\n");
9405 CloseHandle(hf);
9406 return S_FALSE;
9409 CloseHandle(hf);
9411 if (bytes_read != 8) {
9412 TRACE(" too short\n");
9413 return S_FALSE;
9416 if (!memcmp(magic,STORAGE_magic,8)) {
9417 TRACE(" -> YES\n");
9418 return S_OK;
9421 TRACE(" -> Invalid header.\n");
9422 return S_FALSE;
9425 /***********************************************************************
9426 * WriteClassStm (OLE32.@)
9428 * Writes a CLSID to a stream.
9430 * PARAMS
9431 * pStm [I] Stream to write to.
9432 * rclsid [I] CLSID to write.
9434 * RETURNS
9435 * Success: S_OK.
9436 * Failure: HRESULT code.
9438 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9440 TRACE("(%p,%p)\n",pStm,rclsid);
9442 if (!pStm || !rclsid)
9443 return E_INVALIDARG;
9445 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9448 /***********************************************************************
9449 * ReadClassStm (OLE32.@)
9451 * Reads a CLSID from a stream.
9453 * PARAMS
9454 * pStm [I] Stream to read from.
9455 * rclsid [O] CLSID to read.
9457 * RETURNS
9458 * Success: S_OK.
9459 * Failure: HRESULT code.
9461 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9463 ULONG nbByte;
9464 HRESULT res;
9466 TRACE("(%p,%p)\n",pStm,pclsid);
9468 if (!pStm || !pclsid)
9469 return E_INVALIDARG;
9471 /* clear the output args */
9472 *pclsid = CLSID_NULL;
9474 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9476 if (FAILED(res))
9477 return res;
9479 if (nbByte != sizeof(CLSID))
9480 return STG_E_READFAULT;
9481 else
9482 return S_OK;
9486 /************************************************************************
9487 * OleConvert Functions
9488 ***********************************************************************/
9490 #define OLESTREAM_ID 0x501
9491 #define OLESTREAM_MAX_STR_LEN 255
9493 /* OLESTREAM memory structure to use for Get and Put Routines */
9494 typedef struct
9496 DWORD dwOleID;
9497 DWORD dwTypeID;
9498 DWORD dwOleTypeNameLength;
9499 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9500 CHAR *pstrOleObjFileName;
9501 DWORD dwOleObjFileNameLength;
9502 DWORD dwMetaFileWidth;
9503 DWORD dwMetaFileHeight;
9504 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9505 DWORD dwDataLength;
9506 BYTE *pData;
9507 } OLECONVERT_OLESTREAM_DATA;
9509 /* CompObj Stream structure */
9510 typedef struct
9512 BYTE byUnknown1[12];
9513 CLSID clsid;
9514 DWORD dwCLSIDNameLength;
9515 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9516 DWORD dwOleTypeNameLength;
9517 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9518 DWORD dwProgIDNameLength;
9519 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9520 BYTE byUnknown2[16];
9521 } OLECONVERT_ISTORAGE_COMPOBJ;
9523 /* Ole Presentation Stream structure */
9524 typedef struct
9526 BYTE byUnknown1[28];
9527 DWORD dwExtentX;
9528 DWORD dwExtentY;
9529 DWORD dwSize;
9530 BYTE *pData;
9531 } OLECONVERT_ISTORAGE_OLEPRES;
9534 /*************************************************************************
9535 * OLECONVERT_LoadOLE10 [Internal]
9537 * Loads the OLE10 STREAM to memory
9539 * PARAMS
9540 * pOleStream [I] The OLESTREAM
9541 * pData [I] Data Structure for the OLESTREAM Data
9543 * RETURNS
9544 * Success: S_OK
9545 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9546 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9548 * NOTES
9549 * This function is used by OleConvertOLESTREAMToIStorage only.
9551 * Memory allocated for pData must be freed by the caller
9553 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9555 DWORD dwSize;
9556 HRESULT hRes = S_OK;
9557 int nTryCnt=0;
9558 int max_try = 6;
9560 pData->pData = NULL;
9561 pData->pstrOleObjFileName = NULL;
9563 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9565 /* Get the OleID */
9566 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9567 if(dwSize != sizeof(pData->dwOleID))
9569 hRes = CONVERT10_E_OLESTREAM_GET;
9571 else if(pData->dwOleID != OLESTREAM_ID)
9573 hRes = CONVERT10_E_OLESTREAM_FMT;
9575 else
9577 hRes = S_OK;
9578 break;
9582 if(hRes == S_OK)
9584 /* Get the TypeID... more info needed for this field */
9585 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9586 if(dwSize != sizeof(pData->dwTypeID))
9588 hRes = CONVERT10_E_OLESTREAM_GET;
9591 if(hRes == S_OK)
9593 if(pData->dwTypeID != 0)
9595 /* Get the length of the OleTypeName */
9596 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9597 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9599 hRes = CONVERT10_E_OLESTREAM_GET;
9602 if(hRes == S_OK)
9604 if(pData->dwOleTypeNameLength > 0)
9606 /* Get the OleTypeName */
9607 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9608 if(dwSize != pData->dwOleTypeNameLength)
9610 hRes = CONVERT10_E_OLESTREAM_GET;
9614 if(bStrem1)
9616 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9617 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9619 hRes = CONVERT10_E_OLESTREAM_GET;
9621 if(hRes == S_OK)
9623 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9624 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9625 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9626 if(pData->pstrOleObjFileName)
9628 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9629 if(dwSize != pData->dwOleObjFileNameLength)
9631 hRes = CONVERT10_E_OLESTREAM_GET;
9634 else
9635 hRes = CONVERT10_E_OLESTREAM_GET;
9638 else
9640 /* Get the Width of the Metafile */
9641 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9642 if(dwSize != sizeof(pData->dwMetaFileWidth))
9644 hRes = CONVERT10_E_OLESTREAM_GET;
9646 if(hRes == S_OK)
9648 /* Get the Height of the Metafile */
9649 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9650 if(dwSize != sizeof(pData->dwMetaFileHeight))
9652 hRes = CONVERT10_E_OLESTREAM_GET;
9656 if(hRes == S_OK)
9658 /* Get the Length of the Data */
9659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9660 if(dwSize != sizeof(pData->dwDataLength))
9662 hRes = CONVERT10_E_OLESTREAM_GET;
9666 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9668 if(!bStrem1) /* if it is a second OLE stream data */
9670 pData->dwDataLength -= 8;
9671 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9672 if(dwSize != sizeof(pData->strUnknown))
9674 hRes = CONVERT10_E_OLESTREAM_GET;
9678 if(hRes == S_OK)
9680 if(pData->dwDataLength > 0)
9682 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9684 /* Get Data (ex. IStorage, Metafile, or BMP) */
9685 if(pData->pData)
9687 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9688 if(dwSize != pData->dwDataLength)
9690 hRes = CONVERT10_E_OLESTREAM_GET;
9693 else
9695 hRes = CONVERT10_E_OLESTREAM_GET;
9701 return hRes;
9704 /*************************************************************************
9705 * OLECONVERT_SaveOLE10 [Internal]
9707 * Saves the OLE10 STREAM From memory
9709 * PARAMS
9710 * pData [I] Data Structure for the OLESTREAM Data
9711 * pOleStream [I] The OLESTREAM to save
9713 * RETURNS
9714 * Success: S_OK
9715 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9717 * NOTES
9718 * This function is used by OleConvertIStorageToOLESTREAM only.
9721 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9723 DWORD dwSize;
9724 HRESULT hRes = S_OK;
9727 /* Set the OleID */
9728 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9729 if(dwSize != sizeof(pData->dwOleID))
9731 hRes = CONVERT10_E_OLESTREAM_PUT;
9734 if(hRes == S_OK)
9736 /* Set the TypeID */
9737 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9738 if(dwSize != sizeof(pData->dwTypeID))
9740 hRes = CONVERT10_E_OLESTREAM_PUT;
9744 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9746 /* Set the Length of the OleTypeName */
9747 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9748 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9750 hRes = CONVERT10_E_OLESTREAM_PUT;
9753 if(hRes == S_OK)
9755 if(pData->dwOleTypeNameLength > 0)
9757 /* Set the OleTypeName */
9758 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9759 if(dwSize != pData->dwOleTypeNameLength)
9761 hRes = CONVERT10_E_OLESTREAM_PUT;
9766 if(hRes == S_OK)
9768 /* Set the width of the Metafile */
9769 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9770 if(dwSize != sizeof(pData->dwMetaFileWidth))
9772 hRes = CONVERT10_E_OLESTREAM_PUT;
9776 if(hRes == S_OK)
9778 /* Set the height of the Metafile */
9779 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9780 if(dwSize != sizeof(pData->dwMetaFileHeight))
9782 hRes = CONVERT10_E_OLESTREAM_PUT;
9786 if(hRes == S_OK)
9788 /* Set the length of the Data */
9789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9790 if(dwSize != sizeof(pData->dwDataLength))
9792 hRes = CONVERT10_E_OLESTREAM_PUT;
9796 if(hRes == S_OK)
9798 if(pData->dwDataLength > 0)
9800 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9801 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9802 if(dwSize != pData->dwDataLength)
9804 hRes = CONVERT10_E_OLESTREAM_PUT;
9809 return hRes;
9812 /*************************************************************************
9813 * OLECONVERT_GetOLE20FromOLE10[Internal]
9815 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9816 * opens it, and copies the content to the dest IStorage for
9817 * OleConvertOLESTREAMToIStorage
9820 * PARAMS
9821 * pDestStorage [I] The IStorage to copy the data to
9822 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9823 * nBufferLength [I] The size of the buffer
9825 * RETURNS
9826 * Nothing
9828 * NOTES
9832 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9834 HRESULT hRes;
9835 HANDLE hFile;
9836 IStorage *pTempStorage;
9837 DWORD dwNumOfBytesWritten;
9838 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9839 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9841 /* Create a temp File */
9842 GetTempPathW(MAX_PATH, wstrTempDir);
9843 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9844 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9846 if(hFile != INVALID_HANDLE_VALUE)
9848 /* Write IStorage Data to File */
9849 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9850 CloseHandle(hFile);
9852 /* Open and copy temp storage to the Dest Storage */
9853 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9854 if(hRes == S_OK)
9856 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9857 IStorage_Release(pTempStorage);
9859 DeleteFileW(wstrTempFile);
9864 /*************************************************************************
9865 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9867 * Saves the OLE10 STREAM From memory
9869 * PARAMS
9870 * pStorage [I] The Src IStorage to copy
9871 * pData [I] The Dest Memory to write to.
9873 * RETURNS
9874 * The size in bytes allocated for pData
9876 * NOTES
9877 * Memory allocated for pData must be freed by the caller
9879 * Used by OleConvertIStorageToOLESTREAM only.
9882 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9884 HANDLE hFile;
9885 HRESULT hRes;
9886 DWORD nDataLength = 0;
9887 IStorage *pTempStorage;
9888 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9889 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9891 *pData = NULL;
9893 /* Create temp Storage */
9894 GetTempPathW(MAX_PATH, wstrTempDir);
9895 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9896 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9898 if(hRes == S_OK)
9900 /* Copy Src Storage to the Temp Storage */
9901 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9902 IStorage_Release(pTempStorage);
9904 /* Open Temp Storage as a file and copy to memory */
9905 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9906 if(hFile != INVALID_HANDLE_VALUE)
9908 nDataLength = GetFileSize(hFile, NULL);
9909 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9910 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9911 CloseHandle(hFile);
9913 DeleteFileW(wstrTempFile);
9915 return nDataLength;
9918 /*************************************************************************
9919 * OLECONVERT_CreateCompObjStream [Internal]
9921 * Creates a "\001CompObj" is the destination IStorage if necessary.
9923 * PARAMS
9924 * pStorage [I] The dest IStorage to create the CompObj Stream
9925 * if necessary.
9926 * strOleTypeName [I] The ProgID
9928 * RETURNS
9929 * Success: S_OK
9930 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9932 * NOTES
9933 * This function is used by OleConvertOLESTREAMToIStorage only.
9935 * The stream data is stored in the OLESTREAM and there should be
9936 * no need to recreate the stream. If the stream is manually
9937 * deleted it will attempt to create it by querying the registry.
9941 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9943 IStream *pStream;
9944 HRESULT hStorageRes, hRes = S_OK;
9945 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9946 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9947 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9949 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9950 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9952 /* Initialize the CompObj structure */
9953 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9954 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9955 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9958 /* Create a CompObj stream if it doesn't exist */
9959 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9960 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9961 if(hStorageRes == S_OK)
9963 /* copy the OleTypeName to the compobj struct */
9964 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9965 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9967 /* copy the OleTypeName to the compobj struct */
9968 /* Note: in the test made, these were Identical */
9969 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9970 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9972 /* Get the CLSID */
9973 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9974 bufferW, OLESTREAM_MAX_STR_LEN );
9975 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9977 if(hRes == S_OK)
9979 HKEY hKey;
9980 LONG hErr;
9981 /* Get the CLSID Default Name from the Registry */
9982 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9983 if(hErr == ERROR_SUCCESS)
9985 char strTemp[OLESTREAM_MAX_STR_LEN];
9986 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9987 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9988 if(hErr == ERROR_SUCCESS)
9990 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9992 RegCloseKey(hKey);
9996 /* Write CompObj Structure to stream */
9997 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9999 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10001 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10002 if(IStorageCompObj.dwCLSIDNameLength > 0)
10004 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10006 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10007 if(IStorageCompObj.dwOleTypeNameLength > 0)
10009 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10011 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10012 if(IStorageCompObj.dwProgIDNameLength > 0)
10014 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10016 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10017 IStream_Release(pStream);
10019 return hRes;
10023 /*************************************************************************
10024 * OLECONVERT_CreateOlePresStream[Internal]
10026 * Creates the "\002OlePres000" Stream with the Metafile data
10028 * PARAMS
10029 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10030 * dwExtentX [I] Width of the Metafile
10031 * dwExtentY [I] Height of the Metafile
10032 * pData [I] Metafile data
10033 * dwDataLength [I] Size of the Metafile data
10035 * RETURNS
10036 * Success: S_OK
10037 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10039 * NOTES
10040 * This function is used by OleConvertOLESTREAMToIStorage only.
10043 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10045 HRESULT hRes;
10046 IStream *pStream;
10047 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10048 BYTE pOlePresStreamHeader [] =
10050 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10051 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10052 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10053 0x00, 0x00, 0x00, 0x00
10056 BYTE pOlePresStreamHeaderEmpty [] =
10058 0x00, 0x00, 0x00, 0x00,
10059 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10060 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10061 0x00, 0x00, 0x00, 0x00
10064 /* Create the OlePres000 Stream */
10065 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10066 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10068 if(hRes == S_OK)
10070 DWORD nHeaderSize;
10071 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10073 memset(&OlePres, 0, sizeof(OlePres));
10074 /* Do we have any metafile data to save */
10075 if(dwDataLength > 0)
10077 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10078 nHeaderSize = sizeof(pOlePresStreamHeader);
10080 else
10082 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10083 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10085 /* Set width and height of the metafile */
10086 OlePres.dwExtentX = dwExtentX;
10087 OlePres.dwExtentY = -dwExtentY;
10089 /* Set Data and Length */
10090 if(dwDataLength > sizeof(METAFILEPICT16))
10092 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10093 OlePres.pData = &(pData[8]);
10095 /* Save OlePres000 Data to Stream */
10096 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10097 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10098 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10099 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10100 if(OlePres.dwSize > 0)
10102 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10104 IStream_Release(pStream);
10108 /*************************************************************************
10109 * OLECONVERT_CreateOle10NativeStream [Internal]
10111 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10113 * PARAMS
10114 * pStorage [I] Dest storage to create the stream in
10115 * pData [I] Ole10 Native Data (ex. bmp)
10116 * dwDataLength [I] Size of the Ole10 Native Data
10118 * RETURNS
10119 * Nothing
10121 * NOTES
10122 * This function is used by OleConvertOLESTREAMToIStorage only.
10124 * Might need to verify the data and return appropriate error message
10127 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10129 HRESULT hRes;
10130 IStream *pStream;
10131 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10133 /* Create the Ole10Native Stream */
10134 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10135 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10137 if(hRes == S_OK)
10139 /* Write info to stream */
10140 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10141 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10142 IStream_Release(pStream);
10147 /*************************************************************************
10148 * OLECONVERT_GetOLE10ProgID [Internal]
10150 * Finds the ProgID (or OleTypeID) from the IStorage
10152 * PARAMS
10153 * pStorage [I] The Src IStorage to get the ProgID
10154 * strProgID [I] the ProgID string to get
10155 * dwSize [I] the size of the string
10157 * RETURNS
10158 * Success: S_OK
10159 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10161 * NOTES
10162 * This function is used by OleConvertIStorageToOLESTREAM only.
10166 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10168 HRESULT hRes;
10169 IStream *pStream;
10170 LARGE_INTEGER iSeekPos;
10171 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10172 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10174 /* Open the CompObj Stream */
10175 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10176 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10177 if(hRes == S_OK)
10180 /*Get the OleType from the CompObj Stream */
10181 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10182 iSeekPos.u.HighPart = 0;
10184 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10185 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10186 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10187 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10188 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10189 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10190 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10192 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10193 if(*dwSize > 0)
10195 IStream_Read(pStream, strProgID, *dwSize, NULL);
10197 IStream_Release(pStream);
10199 else
10201 STATSTG stat;
10202 LPOLESTR wstrProgID;
10204 /* Get the OleType from the registry */
10205 REFCLSID clsid = &(stat.clsid);
10206 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10207 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10208 if(hRes == S_OK)
10210 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10211 CoTaskMemFree(wstrProgID);
10215 return hRes;
10218 /*************************************************************************
10219 * OLECONVERT_GetOle10PresData [Internal]
10221 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10223 * PARAMS
10224 * pStorage [I] Src IStroage
10225 * pOleStream [I] Dest OleStream Mem Struct
10227 * RETURNS
10228 * Nothing
10230 * NOTES
10231 * This function is used by OleConvertIStorageToOLESTREAM only.
10233 * Memory allocated for pData must be freed by the caller
10237 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10240 HRESULT hRes;
10241 IStream *pStream;
10242 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10244 /* Initialize Default data for OLESTREAM */
10245 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10246 pOleStreamData[0].dwTypeID = 2;
10247 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10248 pOleStreamData[1].dwTypeID = 0;
10249 pOleStreamData[0].dwMetaFileWidth = 0;
10250 pOleStreamData[0].dwMetaFileHeight = 0;
10251 pOleStreamData[0].pData = NULL;
10252 pOleStreamData[1].pData = NULL;
10254 /* Open Ole10Native Stream */
10255 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10256 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10257 if(hRes == S_OK)
10260 /* Read Size and Data */
10261 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10262 if(pOleStreamData->dwDataLength > 0)
10264 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10265 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10267 IStream_Release(pStream);
10273 /*************************************************************************
10274 * OLECONVERT_GetOle20PresData[Internal]
10276 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10278 * PARAMS
10279 * pStorage [I] Src IStroage
10280 * pOleStreamData [I] Dest OleStream Mem Struct
10282 * RETURNS
10283 * Nothing
10285 * NOTES
10286 * This function is used by OleConvertIStorageToOLESTREAM only.
10288 * Memory allocated for pData must be freed by the caller
10290 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10292 HRESULT hRes;
10293 IStream *pStream;
10294 OLECONVERT_ISTORAGE_OLEPRES olePress;
10295 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10297 /* Initialize Default data for OLESTREAM */
10298 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10299 pOleStreamData[0].dwTypeID = 2;
10300 pOleStreamData[0].dwMetaFileWidth = 0;
10301 pOleStreamData[0].dwMetaFileHeight = 0;
10302 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10303 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10304 pOleStreamData[1].dwTypeID = 0;
10305 pOleStreamData[1].dwOleTypeNameLength = 0;
10306 pOleStreamData[1].strOleTypeName[0] = 0;
10307 pOleStreamData[1].dwMetaFileWidth = 0;
10308 pOleStreamData[1].dwMetaFileHeight = 0;
10309 pOleStreamData[1].pData = NULL;
10310 pOleStreamData[1].dwDataLength = 0;
10313 /* Open OlePress000 stream */
10314 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10315 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10316 if(hRes == S_OK)
10318 LARGE_INTEGER iSeekPos;
10319 METAFILEPICT16 MetaFilePict;
10320 static const char strMetafilePictName[] = "METAFILEPICT";
10322 /* Set the TypeID for a Metafile */
10323 pOleStreamData[1].dwTypeID = 5;
10325 /* Set the OleTypeName to Metafile */
10326 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10327 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10329 iSeekPos.u.HighPart = 0;
10330 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10332 /* Get Presentation Data */
10333 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10334 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10335 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10336 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10338 /*Set width and Height */
10339 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10340 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10341 if(olePress.dwSize > 0)
10343 /* Set Length */
10344 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10346 /* Set MetaFilePict struct */
10347 MetaFilePict.mm = 8;
10348 MetaFilePict.xExt = olePress.dwExtentX;
10349 MetaFilePict.yExt = olePress.dwExtentY;
10350 MetaFilePict.hMF = 0;
10352 /* Get Metafile Data */
10353 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10354 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10355 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10357 IStream_Release(pStream);
10361 /*************************************************************************
10362 * OleConvertOLESTREAMToIStorage [OLE32.@]
10364 * Read info on MSDN
10366 * TODO
10367 * DVTARGETDEVICE parameter is not handled
10368 * Still unsure of some mem fields for OLE 10 Stream
10369 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10370 * and "\001OLE" streams
10373 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10374 LPOLESTREAM pOleStream,
10375 LPSTORAGE pstg,
10376 const DVTARGETDEVICE* ptd)
10378 int i;
10379 HRESULT hRes=S_OK;
10380 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10382 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10384 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10386 if(ptd != NULL)
10388 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10391 if(pstg == NULL || pOleStream == NULL)
10393 hRes = E_INVALIDARG;
10396 if(hRes == S_OK)
10398 /* Load the OLESTREAM to Memory */
10399 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10402 if(hRes == S_OK)
10404 /* Load the OLESTREAM to Memory (part 2)*/
10405 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10408 if(hRes == S_OK)
10411 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10413 /* Do we have the IStorage Data in the OLESTREAM */
10414 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10416 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10417 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10419 else
10421 /* It must be an original OLE 1.0 source */
10422 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10425 else
10427 /* It must be an original OLE 1.0 source */
10428 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10431 /* Create CompObj Stream if necessary */
10432 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10433 if(hRes == S_OK)
10435 /*Create the Ole Stream if necessary */
10436 STORAGE_CreateOleStream(pstg, 0);
10441 /* Free allocated memory */
10442 for(i=0; i < 2; i++)
10444 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10445 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10446 pOleStreamData[i].pstrOleObjFileName = NULL;
10448 return hRes;
10451 /*************************************************************************
10452 * OleConvertIStorageToOLESTREAM [OLE32.@]
10454 * Read info on MSDN
10456 * Read info on MSDN
10458 * TODO
10459 * Still unsure of some mem fields for OLE 10 Stream
10460 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10461 * and "\001OLE" streams.
10464 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10465 LPSTORAGE pstg,
10466 LPOLESTREAM pOleStream)
10468 int i;
10469 HRESULT hRes = S_OK;
10470 IStream *pStream;
10471 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10472 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10474 TRACE("%p %p\n", pstg, pOleStream);
10476 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10478 if(pstg == NULL || pOleStream == NULL)
10480 hRes = E_INVALIDARG;
10482 if(hRes == S_OK)
10484 /* Get the ProgID */
10485 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10486 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10488 if(hRes == S_OK)
10490 /* Was it originally Ole10 */
10491 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10492 if(hRes == S_OK)
10494 IStream_Release(pStream);
10495 /* Get Presentation Data for Ole10Native */
10496 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10498 else
10500 /* Get Presentation Data (OLE20) */
10501 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10504 /* Save OLESTREAM */
10505 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10506 if(hRes == S_OK)
10508 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10513 /* Free allocated memory */
10514 for(i=0; i < 2; i++)
10516 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10519 return hRes;
10522 enum stream_1ole_flags {
10523 OleStream_LinkedObject = 0x00000001,
10524 OleStream_Convert = 0x00000004
10527 /***********************************************************************
10528 * GetConvertStg (OLE32.@)
10530 HRESULT WINAPI GetConvertStg(IStorage *stg)
10532 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10533 static const DWORD version_magic = 0x02000001;
10534 DWORD header[2];
10535 IStream *stream;
10536 HRESULT hr;
10538 TRACE("%p\n", stg);
10540 if (!stg) return E_INVALIDARG;
10542 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10543 if (FAILED(hr)) return hr;
10545 hr = IStream_Read(stream, header, sizeof(header), NULL);
10546 IStream_Release(stream);
10547 if (FAILED(hr)) return hr;
10549 if (header[0] != version_magic)
10551 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10552 return E_FAIL;
10555 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10558 /***********************************************************************
10559 * SetConvertStg (OLE32.@)
10561 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10563 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10564 DWORD flags = convert ? OleStream_Convert : 0;
10565 IStream *stream;
10566 DWORD header[2];
10567 HRESULT hr;
10569 TRACE("(%p, %d)\n", storage, convert);
10571 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10572 if (FAILED(hr))
10574 if (hr != STG_E_FILENOTFOUND)
10575 return hr;
10577 return STORAGE_CreateOleStream(storage, flags);
10580 hr = IStream_Read(stream, header, sizeof(header), NULL);
10581 if (FAILED(hr))
10583 IStream_Release(stream);
10584 return hr;
10587 /* update flag if differs */
10588 if ((header[1] ^ flags) & OleStream_Convert)
10590 LARGE_INTEGER pos = {{0}};
10592 if (header[1] & OleStream_Convert)
10593 flags = header[1] & ~OleStream_Convert;
10594 else
10595 flags = header[1] | OleStream_Convert;
10597 pos.QuadPart = sizeof(DWORD);
10598 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10599 if (FAILED(hr))
10601 IStream_Release(stream);
10602 return hr;
10605 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10608 IStream_Release(stream);
10609 return hr;