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