winhttp: Avoid returning while holding a lock (Coverity).
[wine.git] / dlls / ole32 / storage32.c
blob2bc9d3d568a68ad9623c5c2b4d448f80403f2694
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winnls.h"
45 #include "winuser.h"
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
49 #include "storage32.h"
50 #include "ole2.h" /* For Write/ReadClassStm */
52 #include "winreg.h"
53 #include "wine/wingdi16.h"
54 #include "compobj_private.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
60 * These are signatures to detect the type of Document file.
62 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
63 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
65 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
68 /****************************************************************************
69 * StorageInternalImpl definitions.
71 * Definition of the implementation structure for the IStorage interface.
72 * This one implements the IStorage interface for storage that are
73 * inside another storage.
75 typedef struct StorageInternalImpl
77 struct StorageBaseImpl base;
80 * Entry in the parent's stream tracking list
82 struct list ParentListEntry;
84 StorageBaseImpl *parentStorage;
85 } StorageInternalImpl;
87 static const IStorageVtbl StorageInternalImpl_Vtbl;
88 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
90 typedef struct TransactedDirEntry
92 /* If applicable, a reference to the original DirEntry in the transacted
93 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
94 DirRef transactedParentEntry;
96 /* True if this entry is being used. */
97 BOOL inuse;
99 /* True if data is up to date. */
100 BOOL read;
102 /* True if this entry has been modified. */
103 BOOL dirty;
105 /* True if this entry's stream has been modified. */
106 BOOL stream_dirty;
108 /* True if this entry has been deleted in the transacted storage, but the
109 * delete has not yet been committed. */
110 BOOL deleted;
112 /* If this entry's stream has been modified, a reference to where the stream
113 * is stored in the snapshot file. */
114 DirRef stream_entry;
116 /* This directory entry's data, including any changes that have been made. */
117 DirEntry data;
119 /* A reference to the parent of this node. This is only valid while we are
120 * committing changes. */
121 DirRef parent;
123 /* A reference to a newly-created entry in the transacted parent. This is
124 * always equal to transactedParentEntry except when committing changes. */
125 DirRef newTransactedParentEntry;
126 } TransactedDirEntry;
129 /****************************************************************************
130 * Transacted storage object.
132 typedef struct TransactedSnapshotImpl
134 struct StorageBaseImpl base;
137 * Modified streams are temporarily saved to the scratch file.
139 StorageBaseImpl *scratch;
141 /* The directory structure is kept here, so that we can track how these
142 * entries relate to those in the parent storage. */
143 TransactedDirEntry *entries;
144 ULONG entries_size;
145 ULONG firstFreeEntry;
148 * Changes are committed to the transacted parent.
150 StorageBaseImpl *transactedParent;
152 /* The transaction signature from when we last committed */
153 ULONG lastTransactionSig;
154 } TransactedSnapshotImpl;
156 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
157 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
159 typedef struct TransactedSharedImpl
161 struct StorageBaseImpl base;
164 * Snapshot and uncommitted changes go here.
166 TransactedSnapshotImpl *scratch;
169 * Changes are committed to the transacted parent.
171 StorageBaseImpl *transactedParent;
173 /* The transaction signature from when we last committed */
174 ULONG lastTransactionSig;
175 } TransactedSharedImpl;
178 /****************************************************************************
179 * BlockChainStream definitions.
181 * The BlockChainStream class is a utility class that is used to create an
182 * abstraction of the big block chains in the storage file.
185 struct BlockChainRun
187 /* This represents a range of blocks that happen reside in consecutive sectors. */
188 ULONG firstSector;
189 ULONG firstOffset;
190 ULONG lastOffset;
193 typedef struct BlockChainBlock
195 ULONG index;
196 ULONG sector;
197 BOOL read;
198 BOOL dirty;
199 BYTE data[MAX_BIG_BLOCK_SIZE];
200 } BlockChainBlock;
202 struct BlockChainStream
204 StorageImpl* parentStorage;
205 ULONG* headOfStreamPlaceHolder;
206 DirRef ownerDirEntry;
207 struct BlockChainRun* indexCache;
208 ULONG indexCacheLen;
209 ULONG indexCacheSize;
210 BlockChainBlock cachedBlocks[2];
211 ULONG blockToEvict;
212 ULONG tailIndex;
213 ULONG numBlocks;
216 /* Returns the number of blocks that comprises this chain.
217 * This is not the size of the stream as the last block may not be full!
219 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
221 return This->numBlocks;
224 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
225 static void BlockChainStream_Destroy(BlockChainStream*);
226 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
227 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
228 static HRESULT BlockChainStream_Flush(BlockChainStream*);
229 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
230 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
233 /****************************************************************************
234 * SmallBlockChainStream definitions.
236 * The SmallBlockChainStream class is a utility class that is used to create an
237 * abstraction of the small block chains in the storage file.
240 struct SmallBlockChainStream
242 StorageImpl* parentStorage;
243 DirRef ownerDirEntry;
244 ULONG* headOfStreamPlaceHolder;
247 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
248 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
249 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
250 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
251 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
252 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
255 /************************************************************************
256 * STGM Functions
257 ***********************************************************************/
259 /************************************************************************
260 * This method validates an STGM parameter that can contain the values below
262 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
263 * The stgm values contained in 0xffff0000 are bitmasks.
265 * STGM_DIRECT 0x00000000
266 * STGM_TRANSACTED 0x00010000
267 * STGM_SIMPLE 0x08000000
269 * STGM_READ 0x00000000
270 * STGM_WRITE 0x00000001
271 * STGM_READWRITE 0x00000002
273 * STGM_SHARE_DENY_NONE 0x00000040
274 * STGM_SHARE_DENY_READ 0x00000030
275 * STGM_SHARE_DENY_WRITE 0x00000020
276 * STGM_SHARE_EXCLUSIVE 0x00000010
278 * STGM_PRIORITY 0x00040000
279 * STGM_DELETEONRELEASE 0x04000000
281 * STGM_CREATE 0x00001000
282 * STGM_CONVERT 0x00020000
283 * STGM_FAILIFTHERE 0x00000000
285 * STGM_NOSCRATCH 0x00100000
286 * STGM_NOSNAPSHOT 0x00200000
288 static HRESULT validateSTGM(DWORD stgm)
290 DWORD access = STGM_ACCESS_MODE(stgm);
291 DWORD share = STGM_SHARE_MODE(stgm);
292 DWORD create = STGM_CREATE_MODE(stgm);
294 if (stgm&~STGM_KNOWN_FLAGS)
296 ERR("unknown flags %08x\n", stgm);
297 return E_FAIL;
300 switch (access)
302 case STGM_READ:
303 case STGM_WRITE:
304 case STGM_READWRITE:
305 break;
306 default:
307 return E_FAIL;
310 switch (share)
312 case STGM_SHARE_DENY_NONE:
313 case STGM_SHARE_DENY_READ:
314 case STGM_SHARE_DENY_WRITE:
315 case STGM_SHARE_EXCLUSIVE:
316 break;
317 case 0:
318 if (!(stgm & STGM_TRANSACTED))
319 return E_FAIL;
320 break;
321 default:
322 return E_FAIL;
325 switch (create)
327 case STGM_CREATE:
328 case STGM_FAILIFTHERE:
329 break;
330 default:
331 return E_FAIL;
335 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
337 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
338 return E_FAIL;
341 * STGM_CREATE | STGM_CONVERT
342 * if both are false, STGM_FAILIFTHERE is set to TRUE
344 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
345 return E_FAIL;
348 * STGM_NOSCRATCH requires STGM_TRANSACTED
350 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
351 return E_FAIL;
354 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
355 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
357 if ( (stgm & STGM_NOSNAPSHOT) &&
358 (!(stgm & STGM_TRANSACTED) ||
359 share == STGM_SHARE_EXCLUSIVE ||
360 share == STGM_SHARE_DENY_WRITE) )
361 return E_FAIL;
363 return S_OK;
366 /************************************************************************
367 * GetShareModeFromSTGM
369 * This method will return a share mode flag from a STGM value.
370 * The STGM value is assumed valid.
372 static DWORD GetShareModeFromSTGM(DWORD stgm)
374 switch (STGM_SHARE_MODE(stgm))
376 case 0:
377 assert(stgm & STGM_TRANSACTED);
378 /* fall-through */
379 case STGM_SHARE_DENY_NONE:
380 return FILE_SHARE_READ | FILE_SHARE_WRITE;
381 case STGM_SHARE_DENY_READ:
382 return FILE_SHARE_WRITE;
383 case STGM_SHARE_DENY_WRITE:
384 case STGM_SHARE_EXCLUSIVE:
385 return FILE_SHARE_READ;
387 ERR("Invalid share mode!\n");
388 assert(0);
389 return 0;
392 /************************************************************************
393 * GetAccessModeFromSTGM
395 * This method will return an access mode flag from a STGM value.
396 * The STGM value is assumed valid.
398 static DWORD GetAccessModeFromSTGM(DWORD stgm)
400 switch (STGM_ACCESS_MODE(stgm))
402 case STGM_READ:
403 return GENERIC_READ;
404 case STGM_WRITE:
405 case STGM_READWRITE:
406 return GENERIC_READ | GENERIC_WRITE;
408 ERR("Invalid access mode!\n");
409 assert(0);
410 return 0;
413 /************************************************************************
414 * GetCreationModeFromSTGM
416 * This method will return a creation mode flag from a STGM value.
417 * The STGM value is assumed valid.
419 static DWORD GetCreationModeFromSTGM(DWORD stgm)
421 switch(STGM_CREATE_MODE(stgm))
423 case STGM_CREATE:
424 return CREATE_ALWAYS;
425 case STGM_CONVERT:
426 FIXME("STGM_CONVERT not implemented!\n");
427 return CREATE_NEW;
428 case STGM_FAILIFTHERE:
429 return CREATE_NEW;
431 ERR("Invalid create mode!\n");
432 assert(0);
433 return 0;
437 /************************************************************************
438 * IDirectWriterLock implementation
439 ***********************************************************************/
441 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
443 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
446 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
448 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
449 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
452 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
454 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
455 return IStorage_AddRef(&This->IStorage_iface);
458 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
460 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
461 return IStorage_Release(&This->IStorage_iface);
464 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
466 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
467 FIXME("(%p)->(%d): stub\n", This, timeout);
468 return E_NOTIMPL;
471 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
473 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
474 FIXME("(%p): stub\n", This);
475 return E_NOTIMPL;
478 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
480 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
481 FIXME("(%p): stub\n", This);
482 return E_NOTIMPL;
485 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
487 directwriterlock_QueryInterface,
488 directwriterlock_AddRef,
489 directwriterlock_Release,
490 directwriterlock_WaitForWriteAccess,
491 directwriterlock_ReleaseWriteAccess,
492 directwriterlock_HaveWriteAccess
496 /************************************************************************
497 * StorageBaseImpl implementation : Tree helper functions
498 ***********************************************************************/
500 /****************************************************************************
502 * Internal Method
504 * Case insensitive comparison of DirEntry.name by first considering
505 * their size.
507 * Returns <0 when name1 < name2
508 * >0 when name1 > name2
509 * 0 when name1 == name2
511 static LONG entryNameCmp(
512 const OLECHAR *name1,
513 const OLECHAR *name2)
515 LONG diff = lstrlenW(name1) - lstrlenW(name2);
517 while (diff == 0 && *name1 != 0)
520 * We compare the string themselves only when they are of the same length
522 diff = toupperW(*name1++) - toupperW(*name2++);
525 return diff;
528 /****************************************************************************
530 * Internal Method
532 * Find and read the element of a storage with the given name.
534 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
535 const OLECHAR *name, DirEntry *data)
537 DirRef currentEntry;
539 /* Read the storage entry to find the root of the tree. */
540 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
542 currentEntry = data->dirRootEntry;
544 while (currentEntry != DIRENTRY_NULL)
546 LONG cmp;
548 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
550 cmp = entryNameCmp(name, data->name);
552 if (cmp == 0)
553 /* found it */
554 break;
556 else if (cmp < 0)
557 currentEntry = data->leftChild;
559 else if (cmp > 0)
560 currentEntry = data->rightChild;
563 return currentEntry;
566 /****************************************************************************
568 * Internal Method
570 * Find and read the binary tree parent of the element with the given name.
572 * If there is no such element, find a place where it could be inserted and
573 * return STG_E_FILENOTFOUND.
575 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
576 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
577 ULONG *relation)
579 DirRef childEntry;
580 DirEntry childData;
582 /* Read the storage entry to find the root of the tree. */
583 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
585 *parentEntry = storageEntry;
586 *relation = DIRENTRY_RELATION_DIR;
588 childEntry = parentData->dirRootEntry;
590 while (childEntry != DIRENTRY_NULL)
592 LONG cmp;
594 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
596 cmp = entryNameCmp(childName, childData.name);
598 if (cmp == 0)
599 /* found it */
600 break;
602 else if (cmp < 0)
604 *parentData = childData;
605 *parentEntry = childEntry;
606 *relation = DIRENTRY_RELATION_PREVIOUS;
608 childEntry = parentData->leftChild;
611 else if (cmp > 0)
613 *parentData = childData;
614 *parentEntry = childEntry;
615 *relation = DIRENTRY_RELATION_NEXT;
617 childEntry = parentData->rightChild;
621 if (childEntry == DIRENTRY_NULL)
622 return STG_E_FILENOTFOUND;
623 else
624 return S_OK;
627 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
629 switch (relation)
631 case DIRENTRY_RELATION_PREVIOUS:
632 entry->leftChild = new_target;
633 break;
634 case DIRENTRY_RELATION_NEXT:
635 entry->rightChild = new_target;
636 break;
637 case DIRENTRY_RELATION_DIR:
638 entry->dirRootEntry = new_target;
639 break;
640 default:
641 assert(0);
645 /****************************************************************************
647 * Internal Method
649 * Add a directory entry to a storage
651 static HRESULT insertIntoTree(
652 StorageBaseImpl *This,
653 DirRef parentStorageIndex,
654 DirRef newEntryIndex)
656 DirEntry currentEntry;
657 DirEntry newEntry;
660 * Read the inserted entry
662 StorageBaseImpl_ReadDirEntry(This,
663 newEntryIndex,
664 &newEntry);
667 * Read the storage entry
669 StorageBaseImpl_ReadDirEntry(This,
670 parentStorageIndex,
671 &currentEntry);
673 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
676 * The root storage contains some element, therefore, start the research
677 * for the appropriate location.
679 BOOL found = FALSE;
680 DirRef current, next, previous, currentEntryId;
683 * Keep a reference to the root of the storage's element tree
685 currentEntryId = currentEntry.dirRootEntry;
688 * Read
690 StorageBaseImpl_ReadDirEntry(This,
691 currentEntry.dirRootEntry,
692 &currentEntry);
694 previous = currentEntry.leftChild;
695 next = currentEntry.rightChild;
696 current = currentEntryId;
698 while (!found)
700 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
702 if (diff < 0)
704 if (previous != DIRENTRY_NULL)
706 StorageBaseImpl_ReadDirEntry(This,
707 previous,
708 &currentEntry);
709 current = previous;
711 else
713 currentEntry.leftChild = newEntryIndex;
714 StorageBaseImpl_WriteDirEntry(This,
715 current,
716 &currentEntry);
717 found = TRUE;
720 else if (diff > 0)
722 if (next != DIRENTRY_NULL)
724 StorageBaseImpl_ReadDirEntry(This,
725 next,
726 &currentEntry);
727 current = next;
729 else
731 currentEntry.rightChild = newEntryIndex;
732 StorageBaseImpl_WriteDirEntry(This,
733 current,
734 &currentEntry);
735 found = TRUE;
738 else
741 * Trying to insert an item with the same name in the
742 * subtree structure.
744 return STG_E_FILEALREADYEXISTS;
747 previous = currentEntry.leftChild;
748 next = currentEntry.rightChild;
751 else
754 * The storage is empty, make the new entry the root of its element tree
756 currentEntry.dirRootEntry = newEntryIndex;
757 StorageBaseImpl_WriteDirEntry(This,
758 parentStorageIndex,
759 &currentEntry);
762 return S_OK;
765 /*************************************************************************
767 * Internal Method
769 * This method removes a directory entry from its parent storage tree without
770 * freeing any resources attached to it.
772 static HRESULT removeFromTree(
773 StorageBaseImpl *This,
774 DirRef parentStorageIndex,
775 DirRef deletedIndex)
777 DirEntry entryToDelete;
778 DirEntry parentEntry;
779 DirRef parentEntryRef;
780 ULONG typeOfRelation;
781 HRESULT hr;
783 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
785 if (hr != S_OK)
786 return hr;
789 * Find the element that links to the one we want to delete.
791 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
792 &parentEntry, &parentEntryRef, &typeOfRelation);
794 if (hr != S_OK)
795 return hr;
797 if (entryToDelete.leftChild != DIRENTRY_NULL)
800 * Replace the deleted entry with its left child
802 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
804 hr = StorageBaseImpl_WriteDirEntry(
805 This,
806 parentEntryRef,
807 &parentEntry);
808 if(FAILED(hr))
810 return hr;
813 if (entryToDelete.rightChild != DIRENTRY_NULL)
816 * We need to reinsert the right child somewhere. We already know it and
817 * its children are greater than everything in the left tree, so we
818 * insert it at the rightmost point in the left tree.
820 DirRef newRightChildParent = entryToDelete.leftChild;
821 DirEntry newRightChildParentEntry;
825 hr = StorageBaseImpl_ReadDirEntry(
826 This,
827 newRightChildParent,
828 &newRightChildParentEntry);
829 if (FAILED(hr))
831 return hr;
834 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
835 newRightChildParent = newRightChildParentEntry.rightChild;
836 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
838 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
840 hr = StorageBaseImpl_WriteDirEntry(
841 This,
842 newRightChildParent,
843 &newRightChildParentEntry);
844 if (FAILED(hr))
846 return hr;
850 else
853 * Replace the deleted entry with its right child
855 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
857 hr = StorageBaseImpl_WriteDirEntry(
858 This,
859 parentEntryRef,
860 &parentEntry);
861 if(FAILED(hr))
863 return hr;
867 return hr;
871 /************************************************************************
872 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
873 ***********************************************************************/
876 * IEnumSTATSTGImpl definitions.
878 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
879 * This class allows iterating through the content of a storage and finding
880 * specific items inside it.
882 struct IEnumSTATSTGImpl
884 IEnumSTATSTG IEnumSTATSTG_iface;
886 LONG ref; /* Reference count */
887 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
888 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
890 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
893 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
895 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
898 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
900 IStorage_Release(&This->parentStorage->IStorage_iface);
901 HeapFree(GetProcessHeap(), 0, This);
904 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
905 IEnumSTATSTG* iface,
906 REFIID riid,
907 void** ppvObject)
909 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
911 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
913 if (ppvObject==0)
914 return E_INVALIDARG;
916 *ppvObject = 0;
918 if (IsEqualGUID(&IID_IUnknown, riid) ||
919 IsEqualGUID(&IID_IEnumSTATSTG, riid))
921 *ppvObject = &This->IEnumSTATSTG_iface;
922 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
923 TRACE("<-- %p\n", *ppvObject);
924 return S_OK;
927 TRACE("<-- E_NOINTERFACE\n");
928 return E_NOINTERFACE;
931 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
932 IEnumSTATSTG* iface)
934 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
935 return InterlockedIncrement(&This->ref);
938 static ULONG WINAPI IEnumSTATSTGImpl_Release(
939 IEnumSTATSTG* iface)
941 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
943 ULONG newRef;
945 newRef = InterlockedDecrement(&This->ref);
947 if (newRef==0)
949 IEnumSTATSTGImpl_Destroy(This);
952 return newRef;
955 static HRESULT IEnumSTATSTGImpl_GetNextRef(
956 IEnumSTATSTGImpl* This,
957 DirRef *ref)
959 DirRef result = DIRENTRY_NULL;
960 DirRef searchNode;
961 DirEntry entry;
962 HRESULT hr;
963 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
965 TRACE("%p,%p\n", This, ref);
967 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
968 This->parentStorage->storageDirEntry, &entry);
969 searchNode = entry.dirRootEntry;
971 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
973 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
975 if (SUCCEEDED(hr))
977 LONG diff = entryNameCmp( entry.name, This->name);
979 if (diff <= 0)
981 searchNode = entry.rightChild;
983 else
985 result = searchNode;
986 memcpy(result_name, entry.name, sizeof(result_name));
987 searchNode = entry.leftChild;
992 if (SUCCEEDED(hr))
994 *ref = result;
995 if (result != DIRENTRY_NULL)
996 memcpy(This->name, result_name, sizeof(result_name));
999 TRACE("<-- %08x\n", hr);
1000 return hr;
1003 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
1004 IEnumSTATSTG* iface,
1005 ULONG celt,
1006 STATSTG* rgelt,
1007 ULONG* pceltFetched)
1009 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1011 DirEntry currentEntry;
1012 STATSTG* currentReturnStruct = rgelt;
1013 ULONG objectFetched = 0;
1014 DirRef currentSearchNode;
1015 HRESULT hr=S_OK;
1017 TRACE("%p,%u,%p,%p\n", iface, celt, rgelt, pceltFetched);
1019 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
1020 return E_INVALIDARG;
1022 if (This->parentStorage->reverted)
1024 TRACE("<-- STG_E_REVERTED\n");
1025 return STG_E_REVERTED;
1029 * To avoid the special case, get another pointer to a ULONG value if
1030 * the caller didn't supply one.
1032 if (pceltFetched==0)
1033 pceltFetched = &objectFetched;
1036 * Start the iteration, we will iterate until we hit the end of the
1037 * linked list or until we hit the number of items to iterate through
1039 *pceltFetched = 0;
1041 while ( *pceltFetched < celt )
1043 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1045 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1047 memset(currentReturnStruct, 0, sizeof(*currentReturnStruct));
1048 break;
1052 * Read the entry from the storage.
1054 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
1055 currentSearchNode,
1056 &currentEntry);
1057 if (FAILED(hr)) break;
1060 * Copy the information to the return buffer.
1062 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1063 currentReturnStruct,
1064 &currentEntry,
1065 STATFLAG_DEFAULT);
1068 * Step to the next item in the iteration
1070 (*pceltFetched)++;
1071 currentReturnStruct++;
1074 if (SUCCEEDED(hr) && *pceltFetched != celt)
1075 hr = S_FALSE;
1077 TRACE("<-- %08x (asked %u, got %u)\n", hr, celt, *pceltFetched);
1078 return hr;
1082 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1083 IEnumSTATSTG* iface,
1084 ULONG celt)
1086 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1088 ULONG objectFetched = 0;
1089 DirRef currentSearchNode;
1090 HRESULT hr=S_OK;
1092 TRACE("%p,%u\n", iface, celt);
1094 if (This->parentStorage->reverted)
1096 TRACE("<-- STG_E_REVERTED\n");
1097 return STG_E_REVERTED;
1100 while ( (objectFetched < celt) )
1102 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1104 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1105 break;
1107 objectFetched++;
1110 if (SUCCEEDED(hr) && objectFetched != celt)
1111 return S_FALSE;
1113 TRACE("<-- %08x\n", hr);
1114 return hr;
1117 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
1118 IEnumSTATSTG* iface)
1120 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1122 TRACE("%p\n", iface);
1124 if (This->parentStorage->reverted)
1126 TRACE("<-- STG_E_REVERTED\n");
1127 return STG_E_REVERTED;
1130 This->name[0] = 0;
1132 return S_OK;
1135 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1137 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
1138 IEnumSTATSTG* iface,
1139 IEnumSTATSTG** ppenum)
1141 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1142 IEnumSTATSTGImpl* newClone;
1144 TRACE("%p,%p\n", iface, ppenum);
1146 if (This->parentStorage->reverted)
1148 TRACE("<-- STG_E_REVERTED\n");
1149 return STG_E_REVERTED;
1152 if (ppenum==0)
1153 return E_INVALIDARG;
1155 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1156 This->storageDirEntry);
1157 if (!newClone)
1159 *ppenum = NULL;
1160 return E_OUTOFMEMORY;
1164 * The new clone enumeration must point to the same current node as
1165 * the old one.
1167 memcpy(newClone->name, This->name, sizeof(newClone->name));
1169 *ppenum = &newClone->IEnumSTATSTG_iface;
1171 return S_OK;
1175 * Virtual function table for the IEnumSTATSTGImpl class.
1177 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1179 IEnumSTATSTGImpl_QueryInterface,
1180 IEnumSTATSTGImpl_AddRef,
1181 IEnumSTATSTGImpl_Release,
1182 IEnumSTATSTGImpl_Next,
1183 IEnumSTATSTGImpl_Skip,
1184 IEnumSTATSTGImpl_Reset,
1185 IEnumSTATSTGImpl_Clone
1188 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
1189 StorageBaseImpl* parentStorage,
1190 DirRef storageDirEntry)
1192 IEnumSTATSTGImpl* newEnumeration;
1194 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1196 if (newEnumeration)
1198 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1199 newEnumeration->ref = 1;
1200 newEnumeration->name[0] = 0;
1203 * We want to nail-down the reference to the storage in case the
1204 * enumeration out-lives the storage in the client application.
1206 newEnumeration->parentStorage = parentStorage;
1207 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1209 newEnumeration->storageDirEntry = storageDirEntry;
1212 return newEnumeration;
1216 /************************************************************************
1217 * StorageBaseImpl implementation
1218 ***********************************************************************/
1220 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
1222 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1225 /************************************************************************
1226 * StorageBaseImpl_QueryInterface (IUnknown)
1228 * This method implements the common QueryInterface for all IStorage
1229 * implementations contained in this file.
1231 * See Windows documentation for more details on IUnknown methods.
1233 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
1234 IStorage* iface,
1235 REFIID riid,
1236 void** ppvObject)
1238 StorageBaseImpl *This = impl_from_IStorage(iface);
1240 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
1242 if (!ppvObject)
1243 return E_INVALIDARG;
1245 *ppvObject = 0;
1247 if (IsEqualGUID(&IID_IUnknown, riid) ||
1248 IsEqualGUID(&IID_IStorage, riid))
1250 *ppvObject = &This->IStorage_iface;
1252 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1254 *ppvObject = &This->IPropertySetStorage_iface;
1256 /* locking interface is reported for writer only */
1257 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1259 *ppvObject = &This->IDirectWriterLock_iface;
1261 else
1263 TRACE("<-- E_NOINTERFACE\n");
1264 return E_NOINTERFACE;
1267 IStorage_AddRef(iface);
1268 TRACE("<-- %p\n", *ppvObject);
1269 return S_OK;
1272 /************************************************************************
1273 * StorageBaseImpl_AddRef (IUnknown)
1275 * This method implements the common AddRef for all IStorage
1276 * implementations contained in this file.
1278 * See Windows documentation for more details on IUnknown methods.
1280 static ULONG WINAPI StorageBaseImpl_AddRef(
1281 IStorage* iface)
1283 StorageBaseImpl *This = impl_from_IStorage(iface);
1284 ULONG ref = InterlockedIncrement(&This->ref);
1286 TRACE("(%p) AddRef to %d\n", This, ref);
1288 return ref;
1291 /************************************************************************
1292 * StorageBaseImpl_Release (IUnknown)
1294 * This method implements the common Release for all IStorage
1295 * implementations contained in this file.
1297 * See Windows documentation for more details on IUnknown methods.
1299 static ULONG WINAPI StorageBaseImpl_Release(
1300 IStorage* iface)
1302 StorageBaseImpl *This = impl_from_IStorage(iface);
1304 ULONG ref = InterlockedDecrement(&This->ref);
1306 TRACE("(%p) ReleaseRef to %d\n", This, ref);
1308 if (ref == 0)
1311 * Since we are using a system of base-classes, we want to call the
1312 * destructor of the appropriate derived class. To do this, we are
1313 * using virtual functions to implement the destructor.
1315 StorageBaseImpl_Destroy(This);
1318 return ref;
1321 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1322 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1323 SNB snbExclude, IStorage *pstgDest);
1325 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1326 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1327 SNB snbExclude, IStorage *pstgDest)
1329 DirEntry data;
1330 HRESULT hr;
1331 BOOL skip = FALSE;
1332 IStorage *pstgTmp;
1333 IStream *pstrChild, *pstrTmp;
1334 STATSTG strStat;
1336 if (srcEntry == DIRENTRY_NULL)
1337 return S_OK;
1339 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1341 if (FAILED(hr))
1342 return hr;
1344 if ( snbExclude )
1346 WCHAR **snb = snbExclude;
1348 while ( *snb != NULL && !skip )
1350 if ( lstrcmpW(data.name, *snb) == 0 )
1351 skip = TRUE;
1352 ++snb;
1356 if (!skip)
1358 if (data.stgType == STGTY_STORAGE && !skip_storage)
1361 * create a new storage in destination storage
1363 hr = IStorage_CreateStorage( pstgDest, data.name,
1364 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1365 0, 0,
1366 &pstgTmp );
1369 * if it already exist, don't create a new one use this one
1371 if (hr == STG_E_FILEALREADYEXISTS)
1373 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1374 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1375 NULL, 0, &pstgTmp );
1378 if (SUCCEEDED(hr))
1380 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1381 skip_stream, NULL, pstgTmp );
1383 IStorage_Release(pstgTmp);
1386 else if (data.stgType == STGTY_STREAM && !skip_stream)
1389 * create a new stream in destination storage. If the stream already
1390 * exist, it will be deleted and a new one will be created.
1392 hr = IStorage_CreateStream( pstgDest, data.name,
1393 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1394 0, 0, &pstrTmp );
1397 * open child stream storage. This operation must succeed even if the
1398 * stream is already open, so we use internal functions to do it.
1400 if (hr == S_OK)
1402 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1404 if (streamimpl)
1406 pstrChild = &streamimpl->IStream_iface;
1407 if (pstrChild)
1408 IStream_AddRef(pstrChild);
1410 else
1412 pstrChild = NULL;
1413 hr = E_OUTOFMEMORY;
1417 if (hr == S_OK)
1420 * Get the size of the source stream
1422 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1425 * Set the size of the destination stream.
1427 IStream_SetSize(pstrTmp, strStat.cbSize);
1430 * do the copy
1432 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1433 NULL, NULL );
1435 IStream_Release( pstrChild );
1438 IStream_Release( pstrTmp );
1442 /* copy siblings */
1443 if (SUCCEEDED(hr))
1444 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1445 skip_stream, snbExclude, pstgDest );
1447 if (SUCCEEDED(hr))
1448 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1449 skip_stream, snbExclude, pstgDest );
1451 TRACE("<-- %08x\n", hr);
1452 return hr;
1455 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1457 StgStreamImpl *strm;
1459 TRACE("%p,%d\n", stg, streamEntry);
1461 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1463 if (strm->dirEntry == streamEntry)
1465 return TRUE;
1469 return FALSE;
1472 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1474 StorageInternalImpl *childstg;
1476 TRACE("%p,%d\n", stg, storageEntry);
1478 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1480 if (childstg->base.storageDirEntry == storageEntry)
1482 return TRUE;
1486 return FALSE;
1489 /************************************************************************
1490 * StorageBaseImpl_OpenStream (IStorage)
1492 * This method will open the specified stream object from the current storage.
1494 * See Windows documentation for more details on IStorage methods.
1496 static HRESULT WINAPI StorageBaseImpl_OpenStream(
1497 IStorage* iface,
1498 const OLECHAR* pwcsName, /* [string][in] */
1499 void* reserved1, /* [unique][in] */
1500 DWORD grfMode, /* [in] */
1501 DWORD reserved2, /* [in] */
1502 IStream** ppstm) /* [out] */
1504 StorageBaseImpl *This = impl_from_IStorage(iface);
1505 StgStreamImpl* newStream;
1506 DirEntry currentEntry;
1507 DirRef streamEntryRef;
1508 HRESULT res = STG_E_UNKNOWN;
1510 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1511 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1513 if ( (pwcsName==NULL) || (ppstm==0) )
1515 res = E_INVALIDARG;
1516 goto end;
1519 *ppstm = NULL;
1521 if ( FAILED( validateSTGM(grfMode) ) ||
1522 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1524 res = STG_E_INVALIDFLAG;
1525 goto end;
1529 * As documented.
1531 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1533 res = STG_E_INVALIDFUNCTION;
1534 goto end;
1537 if (This->reverted)
1539 res = STG_E_REVERTED;
1540 goto end;
1544 * Check that we're compatible with the parent's storage mode, but
1545 * only if we are not in transacted mode
1547 if(!(This->openFlags & STGM_TRANSACTED)) {
1548 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1550 res = STG_E_INVALIDFLAG;
1551 goto end;
1556 * Search for the element with the given name
1558 streamEntryRef = findElement(
1559 This,
1560 This->storageDirEntry,
1561 pwcsName,
1562 &currentEntry);
1565 * If it was found, construct the stream object and return a pointer to it.
1567 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1568 (currentEntry.stgType==STGTY_STREAM) )
1570 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1572 /* A single stream cannot be opened a second time. */
1573 res = STG_E_ACCESSDENIED;
1574 goto end;
1577 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1579 if (newStream)
1581 newStream->grfMode = grfMode;
1582 *ppstm = &newStream->IStream_iface;
1584 IStream_AddRef(*ppstm);
1586 res = S_OK;
1587 goto end;
1590 res = E_OUTOFMEMORY;
1591 goto end;
1594 res = STG_E_FILENOTFOUND;
1596 end:
1597 if (res == S_OK)
1598 TRACE("<-- IStream %p\n", *ppstm);
1599 TRACE("<-- %08x\n", res);
1600 return res;
1603 /************************************************************************
1604 * StorageBaseImpl_OpenStorage (IStorage)
1606 * This method will open a new storage object from the current storage.
1608 * See Windows documentation for more details on IStorage methods.
1610 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
1611 IStorage* iface,
1612 const OLECHAR* pwcsName, /* [string][unique][in] */
1613 IStorage* pstgPriority, /* [unique][in] */
1614 DWORD grfMode, /* [in] */
1615 SNB snbExclude, /* [unique][in] */
1616 DWORD reserved, /* [in] */
1617 IStorage** ppstg) /* [out] */
1619 StorageBaseImpl *This = impl_from_IStorage(iface);
1620 StorageInternalImpl* newStorage;
1621 StorageBaseImpl* newTransactedStorage;
1622 DirEntry currentEntry;
1623 DirRef storageEntryRef;
1624 HRESULT res = STG_E_UNKNOWN;
1626 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1627 iface, debugstr_w(pwcsName), pstgPriority,
1628 grfMode, snbExclude, reserved, ppstg);
1630 if ((pwcsName==NULL) || (ppstg==0) )
1632 res = E_INVALIDARG;
1633 goto end;
1636 if (This->openFlags & STGM_SIMPLE)
1638 res = STG_E_INVALIDFUNCTION;
1639 goto end;
1642 /* as documented */
1643 if (snbExclude != NULL)
1645 res = STG_E_INVALIDPARAMETER;
1646 goto end;
1649 if ( FAILED( validateSTGM(grfMode) ))
1651 res = STG_E_INVALIDFLAG;
1652 goto end;
1656 * As documented.
1658 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1659 (grfMode & STGM_DELETEONRELEASE) ||
1660 (grfMode & STGM_PRIORITY) )
1662 res = STG_E_INVALIDFUNCTION;
1663 goto end;
1666 if (This->reverted)
1667 return STG_E_REVERTED;
1670 * Check that we're compatible with the parent's storage mode,
1671 * but only if we are not transacted
1673 if(!(This->openFlags & STGM_TRANSACTED)) {
1674 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1676 res = STG_E_ACCESSDENIED;
1677 goto end;
1681 *ppstg = NULL;
1683 storageEntryRef = findElement(
1684 This,
1685 This->storageDirEntry,
1686 pwcsName,
1687 &currentEntry);
1689 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1690 (currentEntry.stgType==STGTY_STORAGE) )
1692 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1694 /* A single storage cannot be opened a second time. */
1695 res = STG_E_ACCESSDENIED;
1696 goto end;
1699 newStorage = StorageInternalImpl_Construct(
1700 This,
1701 grfMode,
1702 storageEntryRef);
1704 if (newStorage != 0)
1706 if (grfMode & STGM_TRANSACTED)
1708 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1710 if (FAILED(res))
1712 HeapFree(GetProcessHeap(), 0, newStorage);
1713 goto end;
1716 *ppstg = &newTransactedStorage->IStorage_iface;
1718 else
1720 *ppstg = &newStorage->base.IStorage_iface;
1723 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1725 res = S_OK;
1726 goto end;
1729 res = STG_E_INSUFFICIENTMEMORY;
1730 goto end;
1733 res = STG_E_FILENOTFOUND;
1735 end:
1736 TRACE("<-- %08x\n", res);
1737 return res;
1740 /************************************************************************
1741 * StorageBaseImpl_EnumElements (IStorage)
1743 * This method will create an enumerator object that can be used to
1744 * retrieve information about all the elements in the storage object.
1746 * See Windows documentation for more details on IStorage methods.
1748 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1749 IStorage* iface,
1750 DWORD reserved1, /* [in] */
1751 void* reserved2, /* [size_is][unique][in] */
1752 DWORD reserved3, /* [in] */
1753 IEnumSTATSTG** ppenum) /* [out] */
1755 StorageBaseImpl *This = impl_from_IStorage(iface);
1756 IEnumSTATSTGImpl* newEnum;
1758 TRACE("(%p, %d, %p, %d, %p)\n",
1759 iface, reserved1, reserved2, reserved3, ppenum);
1761 if (!ppenum)
1762 return E_INVALIDARG;
1764 if (This->reverted)
1765 return STG_E_REVERTED;
1767 newEnum = IEnumSTATSTGImpl_Construct(
1768 This,
1769 This->storageDirEntry);
1771 if (newEnum)
1773 *ppenum = &newEnum->IEnumSTATSTG_iface;
1774 return S_OK;
1777 return E_OUTOFMEMORY;
1780 /************************************************************************
1781 * StorageBaseImpl_Stat (IStorage)
1783 * This method will retrieve information about this storage object.
1785 * See Windows documentation for more details on IStorage methods.
1787 static HRESULT WINAPI StorageBaseImpl_Stat(
1788 IStorage* iface,
1789 STATSTG* pstatstg, /* [out] */
1790 DWORD grfStatFlag) /* [in] */
1792 StorageBaseImpl *This = impl_from_IStorage(iface);
1793 DirEntry currentEntry;
1794 HRESULT res = STG_E_UNKNOWN;
1796 TRACE("(%p, %p, %x)\n",
1797 iface, pstatstg, grfStatFlag);
1799 if (!pstatstg)
1801 res = E_INVALIDARG;
1802 goto end;
1805 if (This->reverted)
1807 res = STG_E_REVERTED;
1808 goto end;
1811 res = StorageBaseImpl_ReadDirEntry(
1812 This,
1813 This->storageDirEntry,
1814 &currentEntry);
1816 if (SUCCEEDED(res))
1818 StorageUtl_CopyDirEntryToSTATSTG(
1819 This,
1820 pstatstg,
1821 &currentEntry,
1822 grfStatFlag);
1824 pstatstg->grfMode = This->openFlags;
1825 pstatstg->grfStateBits = This->stateBits;
1828 end:
1829 if (res == S_OK)
1831 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);
1833 TRACE("<-- %08x\n", res);
1834 return res;
1837 /************************************************************************
1838 * StorageBaseImpl_RenameElement (IStorage)
1840 * This method will rename the specified element.
1842 * See Windows documentation for more details on IStorage methods.
1844 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1845 IStorage* iface,
1846 const OLECHAR* pwcsOldName, /* [in] */
1847 const OLECHAR* pwcsNewName) /* [in] */
1849 StorageBaseImpl *This = impl_from_IStorage(iface);
1850 DirEntry currentEntry;
1851 DirRef currentEntryRef;
1853 TRACE("(%p, %s, %s)\n",
1854 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1856 if (This->reverted)
1857 return STG_E_REVERTED;
1859 currentEntryRef = findElement(This,
1860 This->storageDirEntry,
1861 pwcsNewName,
1862 &currentEntry);
1864 if (currentEntryRef != DIRENTRY_NULL)
1867 * There is already an element with the new name
1869 return STG_E_FILEALREADYEXISTS;
1873 * Search for the old element name
1875 currentEntryRef = findElement(This,
1876 This->storageDirEntry,
1877 pwcsOldName,
1878 &currentEntry);
1880 if (currentEntryRef != DIRENTRY_NULL)
1882 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1883 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1885 WARN("Element is already open; cannot rename.\n");
1886 return STG_E_ACCESSDENIED;
1889 /* Remove the element from its current position in the tree */
1890 removeFromTree(This, This->storageDirEntry,
1891 currentEntryRef);
1893 /* Change the name of the element */
1894 strcpyW(currentEntry.name, pwcsNewName);
1896 /* Delete any sibling links */
1897 currentEntry.leftChild = DIRENTRY_NULL;
1898 currentEntry.rightChild = DIRENTRY_NULL;
1900 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1901 &currentEntry);
1903 /* Insert the element in a new position in the tree */
1904 insertIntoTree(This, This->storageDirEntry,
1905 currentEntryRef);
1907 else
1910 * There is no element with the old name
1912 return STG_E_FILENOTFOUND;
1915 return StorageBaseImpl_Flush(This);
1918 /************************************************************************
1919 * StorageBaseImpl_CreateStream (IStorage)
1921 * This method will create a stream object within this storage
1923 * See Windows documentation for more details on IStorage methods.
1925 static HRESULT WINAPI StorageBaseImpl_CreateStream(
1926 IStorage* iface,
1927 const OLECHAR* pwcsName, /* [string][in] */
1928 DWORD grfMode, /* [in] */
1929 DWORD reserved1, /* [in] */
1930 DWORD reserved2, /* [in] */
1931 IStream** ppstm) /* [out] */
1933 StorageBaseImpl *This = impl_from_IStorage(iface);
1934 StgStreamImpl* newStream;
1935 DirEntry currentEntry, newStreamEntry;
1936 DirRef currentEntryRef, newStreamEntryRef;
1937 HRESULT hr;
1939 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1940 iface, debugstr_w(pwcsName), grfMode,
1941 reserved1, reserved2, ppstm);
1943 if (ppstm == 0)
1944 return STG_E_INVALIDPOINTER;
1946 if (pwcsName == 0)
1947 return STG_E_INVALIDNAME;
1949 if (reserved1 || reserved2)
1950 return STG_E_INVALIDPARAMETER;
1952 if ( FAILED( validateSTGM(grfMode) ))
1953 return STG_E_INVALIDFLAG;
1955 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1956 return STG_E_INVALIDFLAG;
1958 if (This->reverted)
1959 return STG_E_REVERTED;
1962 * As documented.
1964 if ((grfMode & STGM_DELETEONRELEASE) ||
1965 (grfMode & STGM_TRANSACTED))
1966 return STG_E_INVALIDFUNCTION;
1969 * Don't worry about permissions in transacted mode, as we can always write
1970 * changes; we just can't always commit them.
1972 if(!(This->openFlags & STGM_TRANSACTED)) {
1973 /* Can't create a stream on read-only storage */
1974 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1975 return STG_E_ACCESSDENIED;
1977 /* Can't create a stream with greater access than the parent. */
1978 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1979 return STG_E_ACCESSDENIED;
1982 if(This->openFlags & STGM_SIMPLE)
1983 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1985 *ppstm = 0;
1987 currentEntryRef = findElement(This,
1988 This->storageDirEntry,
1989 pwcsName,
1990 &currentEntry);
1992 if (currentEntryRef != DIRENTRY_NULL)
1995 * An element with this name already exists
1997 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1999 IStorage_DestroyElement(iface, pwcsName);
2001 else
2002 return STG_E_FILEALREADYEXISTS;
2006 * memset the empty entry
2008 memset(&newStreamEntry, 0, sizeof(DirEntry));
2010 newStreamEntry.sizeOfNameString =
2011 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2013 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2014 return STG_E_INVALIDNAME;
2016 strcpyW(newStreamEntry.name, pwcsName);
2018 newStreamEntry.stgType = STGTY_STREAM;
2019 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2020 newStreamEntry.size.u.LowPart = 0;
2021 newStreamEntry.size.u.HighPart = 0;
2023 newStreamEntry.leftChild = DIRENTRY_NULL;
2024 newStreamEntry.rightChild = DIRENTRY_NULL;
2025 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2027 /* call CoFileTime to get the current time
2028 newStreamEntry.ctime
2029 newStreamEntry.mtime
2032 /* newStreamEntry.clsid */
2035 * Create an entry with the new data
2037 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2038 if (FAILED(hr))
2039 return hr;
2042 * Insert the new entry in the parent storage's tree.
2044 hr = insertIntoTree(
2045 This,
2046 This->storageDirEntry,
2047 newStreamEntryRef);
2048 if (FAILED(hr))
2050 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2051 return hr;
2055 * Open the stream to return it.
2057 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2059 if (newStream)
2061 *ppstm = &newStream->IStream_iface;
2062 IStream_AddRef(*ppstm);
2064 else
2066 return STG_E_INSUFFICIENTMEMORY;
2069 return StorageBaseImpl_Flush(This);
2072 /************************************************************************
2073 * StorageBaseImpl_SetClass (IStorage)
2075 * This method will write the specified CLSID in the directory entry of this
2076 * storage.
2078 * See Windows documentation for more details on IStorage methods.
2080 static HRESULT WINAPI StorageBaseImpl_SetClass(
2081 IStorage* iface,
2082 REFCLSID clsid) /* [in] */
2084 StorageBaseImpl *This = impl_from_IStorage(iface);
2085 HRESULT hRes;
2086 DirEntry currentEntry;
2088 TRACE("(%p, %p)\n", iface, clsid);
2090 if (This->reverted)
2091 return STG_E_REVERTED;
2093 hRes = StorageBaseImpl_ReadDirEntry(This,
2094 This->storageDirEntry,
2095 &currentEntry);
2096 if (SUCCEEDED(hRes))
2098 currentEntry.clsid = *clsid;
2100 hRes = StorageBaseImpl_WriteDirEntry(This,
2101 This->storageDirEntry,
2102 &currentEntry);
2105 if (SUCCEEDED(hRes))
2106 hRes = StorageBaseImpl_Flush(This);
2108 return hRes;
2111 /************************************************************************
2112 * StorageBaseImpl_CreateStorage (IStorage)
2114 * This method will create the storage object within the provided storage.
2116 * See Windows documentation for more details on IStorage methods.
2118 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
2119 IStorage* iface,
2120 const OLECHAR *pwcsName, /* [string][in] */
2121 DWORD grfMode, /* [in] */
2122 DWORD reserved1, /* [in] */
2123 DWORD reserved2, /* [in] */
2124 IStorage **ppstg) /* [out] */
2126 StorageBaseImpl* This = impl_from_IStorage(iface);
2128 DirEntry currentEntry;
2129 DirEntry newEntry;
2130 DirRef currentEntryRef;
2131 DirRef newEntryRef;
2132 HRESULT hr;
2134 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2135 iface, debugstr_w(pwcsName), grfMode,
2136 reserved1, reserved2, ppstg);
2138 if (ppstg == 0)
2139 return STG_E_INVALIDPOINTER;
2141 if (This->openFlags & STGM_SIMPLE)
2143 return STG_E_INVALIDFUNCTION;
2146 if (pwcsName == 0)
2147 return STG_E_INVALIDNAME;
2149 *ppstg = NULL;
2151 if ( FAILED( validateSTGM(grfMode) ) ||
2152 (grfMode & STGM_DELETEONRELEASE) )
2154 WARN("bad grfMode: 0x%x\n", grfMode);
2155 return STG_E_INVALIDFLAG;
2158 if (This->reverted)
2159 return STG_E_REVERTED;
2162 * Check that we're compatible with the parent's storage mode
2164 if ( !(This->openFlags & STGM_TRANSACTED) &&
2165 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2167 WARN("access denied\n");
2168 return STG_E_ACCESSDENIED;
2171 currentEntryRef = findElement(This,
2172 This->storageDirEntry,
2173 pwcsName,
2174 &currentEntry);
2176 if (currentEntryRef != DIRENTRY_NULL)
2179 * An element with this name already exists
2181 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2182 ((This->openFlags & STGM_TRANSACTED) ||
2183 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2185 hr = IStorage_DestroyElement(iface, pwcsName);
2186 if (FAILED(hr))
2187 return hr;
2189 else
2191 WARN("file already exists\n");
2192 return STG_E_FILEALREADYEXISTS;
2195 else if (!(This->openFlags & STGM_TRANSACTED) &&
2196 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2198 WARN("read-only storage\n");
2199 return STG_E_ACCESSDENIED;
2202 memset(&newEntry, 0, sizeof(DirEntry));
2204 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2206 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2208 FIXME("name too long\n");
2209 return STG_E_INVALIDNAME;
2212 strcpyW(newEntry.name, pwcsName);
2214 newEntry.stgType = STGTY_STORAGE;
2215 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2216 newEntry.size.u.LowPart = 0;
2217 newEntry.size.u.HighPart = 0;
2219 newEntry.leftChild = DIRENTRY_NULL;
2220 newEntry.rightChild = DIRENTRY_NULL;
2221 newEntry.dirRootEntry = DIRENTRY_NULL;
2223 /* call CoFileTime to get the current time
2224 newEntry.ctime
2225 newEntry.mtime
2228 /* newEntry.clsid */
2231 * Create a new directory entry for the storage
2233 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2234 if (FAILED(hr))
2235 return hr;
2238 * Insert the new directory entry into the parent storage's tree
2240 hr = insertIntoTree(
2241 This,
2242 This->storageDirEntry,
2243 newEntryRef);
2244 if (FAILED(hr))
2246 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2247 return hr;
2251 * Open it to get a pointer to return.
2253 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2255 if( (hr != S_OK) || (*ppstg == NULL))
2257 return hr;
2260 if (SUCCEEDED(hr))
2261 hr = StorageBaseImpl_Flush(This);
2263 return S_OK;
2266 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2267 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2268 SNB snbExclude, IStorage *pstgDest)
2270 DirEntry data;
2271 HRESULT hr;
2273 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2275 if (SUCCEEDED(hr))
2276 hr = IStorage_SetClass( pstgDest, &data.clsid );
2278 if (SUCCEEDED(hr))
2279 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2280 skip_stream, snbExclude, pstgDest );
2282 TRACE("<-- %08x\n", hr);
2283 return hr;
2286 /*************************************************************************
2287 * CopyTo (IStorage)
2289 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2290 IStorage* iface,
2291 DWORD ciidExclude, /* [in] */
2292 const IID* rgiidExclude, /* [size_is][unique][in] */
2293 SNB snbExclude, /* [unique][in] */
2294 IStorage* pstgDest) /* [unique][in] */
2296 StorageBaseImpl *This = impl_from_IStorage(iface);
2298 BOOL skip_storage = FALSE, skip_stream = FALSE;
2299 DWORD i;
2301 TRACE("(%p, %d, %p, %p, %p)\n",
2302 iface, ciidExclude, rgiidExclude,
2303 snbExclude, pstgDest);
2305 if ( pstgDest == 0 )
2306 return STG_E_INVALIDPOINTER;
2308 for(i = 0; i < ciidExclude; ++i)
2310 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2311 skip_storage = TRUE;
2312 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2313 skip_stream = TRUE;
2314 else
2315 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2318 if (!skip_storage)
2320 /* Give up early if it looks like this would be infinitely recursive.
2321 * Oddly enough, this includes some cases that aren't really recursive, like
2322 * copying to a transacted child. */
2323 IStorage *pstgDestAncestor = pstgDest;
2324 IStorage *pstgDestAncestorChild = NULL;
2326 /* Go up the chain from the destination until we find the source storage. */
2327 while (pstgDestAncestor != iface) {
2328 pstgDestAncestorChild = pstgDest;
2330 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2332 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2334 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2336 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2338 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2340 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2342 else
2343 break;
2346 if (pstgDestAncestor == iface)
2348 BOOL fail = TRUE;
2350 if (pstgDestAncestorChild && snbExclude)
2352 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2353 DirEntry data;
2354 WCHAR **snb = snbExclude;
2356 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2358 while ( *snb != NULL && fail )
2360 if ( lstrcmpW(data.name, *snb) == 0 )
2361 fail = FALSE;
2362 ++snb;
2366 if (fail)
2367 return STG_E_ACCESSDENIED;
2371 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2372 skip_storage, skip_stream, snbExclude, pstgDest );
2375 /*************************************************************************
2376 * MoveElementTo (IStorage)
2378 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2379 IStorage* iface,
2380 const OLECHAR *pwcsName, /* [string][in] */
2381 IStorage *pstgDest, /* [unique][in] */
2382 const OLECHAR *pwcsNewName,/* [string][in] */
2383 DWORD grfFlags) /* [in] */
2385 FIXME("(%p %s %p %s %u): stub\n", iface,
2386 debugstr_w(pwcsName), pstgDest,
2387 debugstr_w(pwcsNewName), grfFlags);
2388 return E_NOTIMPL;
2391 /*************************************************************************
2392 * Commit (IStorage)
2394 * Ensures that any changes made to a storage object open in transacted mode
2395 * are reflected in the parent storage
2397 * In a non-transacted mode, this ensures all cached writes are completed.
2399 static HRESULT WINAPI StorageBaseImpl_Commit(
2400 IStorage* iface,
2401 DWORD grfCommitFlags)/* [in] */
2403 StorageBaseImpl* This = impl_from_IStorage(iface);
2404 TRACE("(%p %d)\n", iface, grfCommitFlags);
2405 return StorageBaseImpl_Flush(This);
2408 /*************************************************************************
2409 * Revert (IStorage)
2411 * Discard all changes that have been made since the last commit operation
2413 static HRESULT WINAPI StorageBaseImpl_Revert(
2414 IStorage* iface)
2416 TRACE("(%p)\n", iface);
2417 return S_OK;
2420 /*********************************************************************
2422 * Internal helper function for StorageBaseImpl_DestroyElement()
2424 * Delete the contents of a storage entry.
2427 static HRESULT deleteStorageContents(
2428 StorageBaseImpl *parentStorage,
2429 DirRef indexToDelete,
2430 DirEntry entryDataToDelete)
2432 IEnumSTATSTG *elements = 0;
2433 IStorage *childStorage = 0;
2434 STATSTG currentElement;
2435 HRESULT hr;
2436 HRESULT destroyHr = S_OK;
2437 StorageInternalImpl *stg, *stg2;
2439 TRACE("%p,%d\n", parentStorage, indexToDelete);
2441 /* Invalidate any open storage objects. */
2442 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2444 if (stg->base.storageDirEntry == indexToDelete)
2446 StorageBaseImpl_Invalidate(&stg->base);
2451 * Open the storage and enumerate it
2453 hr = IStorage_OpenStorage(
2454 &parentStorage->IStorage_iface,
2455 entryDataToDelete.name,
2457 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2460 &childStorage);
2462 if (hr != S_OK)
2464 TRACE("<-- %08x\n", hr);
2465 return hr;
2469 * Enumerate the elements
2471 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2472 if (FAILED(hr))
2474 IStorage_Release(childStorage);
2475 TRACE("<-- %08x\n", hr);
2476 return hr;
2482 * Obtain the next element
2484 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2485 if (hr==S_OK)
2487 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2489 CoTaskMemFree(currentElement.pwcsName);
2493 * We need to Reset the enumeration every time because we delete elements
2494 * and the enumeration could be invalid
2496 IEnumSTATSTG_Reset(elements);
2498 } while ((hr == S_OK) && (destroyHr == S_OK));
2500 IStorage_Release(childStorage);
2501 IEnumSTATSTG_Release(elements);
2503 TRACE("%08x\n", hr);
2504 return destroyHr;
2507 /*********************************************************************
2509 * Internal helper function for StorageBaseImpl_DestroyElement()
2511 * Perform the deletion of a stream's data
2514 static HRESULT deleteStreamContents(
2515 StorageBaseImpl *parentStorage,
2516 DirRef indexToDelete,
2517 DirEntry entryDataToDelete)
2519 IStream *pis;
2520 HRESULT hr;
2521 ULARGE_INTEGER size;
2522 StgStreamImpl *strm, *strm2;
2524 /* Invalidate any open stream objects. */
2525 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2527 if (strm->dirEntry == indexToDelete)
2529 TRACE("Stream deleted %p\n", strm);
2530 strm->parentStorage = NULL;
2531 list_remove(&strm->StrmListEntry);
2535 size.u.HighPart = 0;
2536 size.u.LowPart = 0;
2538 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2539 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2541 if (hr!=S_OK)
2543 TRACE("<-- %08x\n", hr);
2544 return(hr);
2548 * Zap the stream
2550 hr = IStream_SetSize(pis, size);
2552 if(hr != S_OK)
2554 TRACE("<-- %08x\n", hr);
2555 return hr;
2559 * Release the stream object.
2561 IStream_Release(pis);
2562 TRACE("<-- %08x\n", hr);
2563 return S_OK;
2566 /*************************************************************************
2567 * DestroyElement (IStorage)
2569 * Strategy: This implementation is built this way for simplicity not for speed.
2570 * I always delete the topmost element of the enumeration and adjust
2571 * the deleted element pointer all the time. This takes longer to
2572 * do but allows reinvoking DestroyElement whenever we encounter a
2573 * storage object. The optimisation resides in the usage of another
2574 * enumeration strategy that would give all the leaves of a storage
2575 * first. (postfix order)
2577 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2578 IStorage* iface,
2579 const OLECHAR *pwcsName)/* [string][in] */
2581 StorageBaseImpl *This = impl_from_IStorage(iface);
2583 HRESULT hr = S_OK;
2584 DirEntry entryToDelete;
2585 DirRef entryToDeleteRef;
2587 TRACE("(%p, %s)\n",
2588 iface, debugstr_w(pwcsName));
2590 if (pwcsName==NULL)
2591 return STG_E_INVALIDPOINTER;
2593 if (This->reverted)
2594 return STG_E_REVERTED;
2596 if ( !(This->openFlags & STGM_TRANSACTED) &&
2597 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2598 return STG_E_ACCESSDENIED;
2600 entryToDeleteRef = findElement(
2601 This,
2602 This->storageDirEntry,
2603 pwcsName,
2604 &entryToDelete);
2606 if ( entryToDeleteRef == DIRENTRY_NULL )
2608 TRACE("<-- STG_E_FILENOTFOUND\n");
2609 return STG_E_FILENOTFOUND;
2612 if ( entryToDelete.stgType == STGTY_STORAGE )
2614 hr = deleteStorageContents(
2615 This,
2616 entryToDeleteRef,
2617 entryToDelete);
2619 else if ( entryToDelete.stgType == STGTY_STREAM )
2621 hr = deleteStreamContents(
2622 This,
2623 entryToDeleteRef,
2624 entryToDelete);
2627 if (hr!=S_OK)
2629 TRACE("<-- %08x\n", hr);
2630 return hr;
2634 * Remove the entry from its parent storage
2636 hr = removeFromTree(
2637 This,
2638 This->storageDirEntry,
2639 entryToDeleteRef);
2642 * Invalidate the entry
2644 if (SUCCEEDED(hr))
2645 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2647 if (SUCCEEDED(hr))
2648 hr = StorageBaseImpl_Flush(This);
2650 TRACE("<-- %08x\n", hr);
2651 return hr;
2654 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2656 struct list *cur, *cur2;
2657 StgStreamImpl *strm=NULL;
2658 StorageInternalImpl *childstg=NULL;
2660 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2661 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2662 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2663 strm->parentStorage = NULL;
2664 list_remove(cur);
2667 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2668 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2669 StorageBaseImpl_Invalidate( &childstg->base );
2672 if (stg->transactedChild)
2674 StorageBaseImpl_Invalidate(stg->transactedChild);
2676 stg->transactedChild = NULL;
2680 /******************************************************************************
2681 * SetElementTimes (IStorage)
2683 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2684 IStorage* iface,
2685 const OLECHAR *pwcsName,/* [string][in] */
2686 const FILETIME *pctime, /* [in] */
2687 const FILETIME *patime, /* [in] */
2688 const FILETIME *pmtime) /* [in] */
2690 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2691 return S_OK;
2694 /******************************************************************************
2695 * SetStateBits (IStorage)
2697 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2698 IStorage* iface,
2699 DWORD grfStateBits,/* [in] */
2700 DWORD grfMask) /* [in] */
2702 StorageBaseImpl *This = impl_from_IStorage(iface);
2704 if (This->reverted)
2705 return STG_E_REVERTED;
2707 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2708 return S_OK;
2711 /******************************************************************************
2712 * Internal stream list handlers
2715 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2717 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2718 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2721 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2723 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2724 list_remove(&(strm->StrmListEntry));
2727 static HRESULT StorageBaseImpl_CopyStream(
2728 StorageBaseImpl *dst, DirRef dst_entry,
2729 StorageBaseImpl *src, DirRef src_entry)
2731 HRESULT hr;
2732 BYTE data[4096];
2733 DirEntry srcdata;
2734 ULARGE_INTEGER bytes_copied;
2735 ULONG bytestocopy, bytesread, byteswritten;
2737 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2739 if (SUCCEEDED(hr))
2741 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2743 bytes_copied.QuadPart = 0;
2744 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2746 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2748 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2749 data, &bytesread);
2750 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2752 if (SUCCEEDED(hr))
2753 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2754 data, &byteswritten);
2755 if (SUCCEEDED(hr))
2757 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2758 bytes_copied.QuadPart += byteswritten;
2763 return hr;
2766 static HRESULT StorageBaseImpl_DupStorageTree(
2767 StorageBaseImpl *dst, DirRef *dst_entry,
2768 StorageBaseImpl *src, DirRef src_entry)
2770 HRESULT hr;
2771 DirEntry data;
2772 BOOL has_stream=FALSE;
2774 if (src_entry == DIRENTRY_NULL)
2776 *dst_entry = DIRENTRY_NULL;
2777 return S_OK;
2780 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2781 if (SUCCEEDED(hr))
2783 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2784 data.startingBlock = BLOCK_END_OF_CHAIN;
2785 data.size.QuadPart = 0;
2787 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2790 if (SUCCEEDED(hr))
2791 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2793 if (SUCCEEDED(hr))
2794 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2796 if (SUCCEEDED(hr))
2797 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2799 if (SUCCEEDED(hr) && has_stream)
2800 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2802 return hr;
2805 static HRESULT StorageBaseImpl_CopyStorageTree(
2806 StorageBaseImpl *dst, DirRef dst_entry,
2807 StorageBaseImpl *src, DirRef src_entry)
2809 HRESULT hr;
2810 DirEntry src_data, dst_data;
2811 DirRef new_root_entry;
2813 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2815 if (SUCCEEDED(hr))
2817 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2820 if (SUCCEEDED(hr))
2822 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
2823 dst_data.clsid = src_data.clsid;
2824 dst_data.ctime = src_data.ctime;
2825 dst_data.mtime = src_data.mtime;
2826 dst_data.dirRootEntry = new_root_entry;
2829 if (SUCCEEDED(hr))
2830 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2832 return hr;
2835 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2837 HRESULT hr;
2838 DirEntry data;
2839 ULARGE_INTEGER zero;
2841 if (entry == DIRENTRY_NULL)
2842 return S_OK;
2844 zero.QuadPart = 0;
2846 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2848 if (SUCCEEDED(hr) && include_siblings)
2849 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2851 if (SUCCEEDED(hr) && include_siblings)
2852 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2854 if (SUCCEEDED(hr))
2855 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2857 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2858 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2860 if (SUCCEEDED(hr))
2861 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2863 return hr;
2867 /************************************************************************
2868 * StorageImpl implementation
2869 ***********************************************************************/
2871 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2872 ULARGE_INTEGER offset,
2873 void* buffer,
2874 ULONG size,
2875 ULONG* bytesRead)
2877 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2880 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2881 ULARGE_INTEGER offset,
2882 const void* buffer,
2883 const ULONG size,
2884 ULONG* bytesWritten)
2886 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2889 /******************************************************************************
2890 * StorageImpl_LoadFileHeader
2892 * This method will read in the file header
2894 static HRESULT StorageImpl_LoadFileHeader(
2895 StorageImpl* This)
2897 HRESULT hr;
2898 BYTE headerBigBlock[HEADER_SIZE];
2899 int index;
2900 ULARGE_INTEGER offset;
2901 DWORD bytes_read;
2903 TRACE("\n");
2905 * Get a pointer to the big block of data containing the header.
2907 offset.u.HighPart = 0;
2908 offset.u.LowPart = 0;
2909 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2910 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2911 hr = STG_E_FILENOTFOUND;
2914 * Extract the information from the header.
2916 if (SUCCEEDED(hr))
2919 * Check for the "magic number" signature and return an error if it is not
2920 * found.
2922 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2924 return STG_E_OLDFORMAT;
2927 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2929 return STG_E_INVALIDHEADER;
2932 StorageUtl_ReadWord(
2933 headerBigBlock,
2934 OFFSET_BIGBLOCKSIZEBITS,
2935 &This->bigBlockSizeBits);
2937 StorageUtl_ReadWord(
2938 headerBigBlock,
2939 OFFSET_SMALLBLOCKSIZEBITS,
2940 &This->smallBlockSizeBits);
2942 StorageUtl_ReadDWord(
2943 headerBigBlock,
2944 OFFSET_BBDEPOTCOUNT,
2945 &This->bigBlockDepotCount);
2947 StorageUtl_ReadDWord(
2948 headerBigBlock,
2949 OFFSET_ROOTSTARTBLOCK,
2950 &This->rootStartBlock);
2952 StorageUtl_ReadDWord(
2953 headerBigBlock,
2954 OFFSET_TRANSACTIONSIG,
2955 &This->transactionSig);
2957 StorageUtl_ReadDWord(
2958 headerBigBlock,
2959 OFFSET_SMALLBLOCKLIMIT,
2960 &This->smallBlockLimit);
2962 StorageUtl_ReadDWord(
2963 headerBigBlock,
2964 OFFSET_SBDEPOTSTART,
2965 &This->smallBlockDepotStart);
2967 StorageUtl_ReadDWord(
2968 headerBigBlock,
2969 OFFSET_EXTBBDEPOTSTART,
2970 &This->extBigBlockDepotStart);
2972 StorageUtl_ReadDWord(
2973 headerBigBlock,
2974 OFFSET_EXTBBDEPOTCOUNT,
2975 &This->extBigBlockDepotCount);
2977 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2979 StorageUtl_ReadDWord(
2980 headerBigBlock,
2981 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2982 &(This->bigBlockDepotStart[index]));
2986 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2988 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2989 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2992 * Right now, the code is making some assumptions about the size of the
2993 * blocks, just make sure they are what we're expecting.
2995 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2996 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2997 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2999 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3000 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3001 hr = STG_E_INVALIDHEADER;
3003 else
3004 hr = S_OK;
3007 return hr;
3010 /******************************************************************************
3011 * StorageImpl_SaveFileHeader
3013 * This method will save to the file the header
3015 static void StorageImpl_SaveFileHeader(
3016 StorageImpl* This)
3018 BYTE headerBigBlock[HEADER_SIZE];
3019 int index;
3020 HRESULT hr;
3021 ULARGE_INTEGER offset;
3022 DWORD bytes_read, bytes_written;
3023 DWORD major_version, dirsectorcount;
3026 * Get a pointer to the big block of data containing the header.
3028 offset.u.HighPart = 0;
3029 offset.u.LowPart = 0;
3030 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3031 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3032 hr = STG_E_FILENOTFOUND;
3034 if (This->bigBlockSizeBits == 0x9)
3035 major_version = 3;
3036 else if (This->bigBlockSizeBits == 0xc)
3037 major_version = 4;
3038 else
3040 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3041 major_version = 4;
3045 * If the block read failed, the file is probably new.
3047 if (FAILED(hr))
3050 * Initialize for all unknown fields.
3052 memset(headerBigBlock, 0, HEADER_SIZE);
3055 * Initialize the magic number.
3057 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3061 * Write the information to the header.
3063 StorageUtl_WriteWord(
3064 headerBigBlock,
3065 OFFSET_MINORVERSION,
3066 0x3e);
3068 StorageUtl_WriteWord(
3069 headerBigBlock,
3070 OFFSET_MAJORVERSION,
3071 major_version);
3073 StorageUtl_WriteWord(
3074 headerBigBlock,
3075 OFFSET_BYTEORDERMARKER,
3076 (WORD)-2);
3078 StorageUtl_WriteWord(
3079 headerBigBlock,
3080 OFFSET_BIGBLOCKSIZEBITS,
3081 This->bigBlockSizeBits);
3083 StorageUtl_WriteWord(
3084 headerBigBlock,
3085 OFFSET_SMALLBLOCKSIZEBITS,
3086 This->smallBlockSizeBits);
3088 if (major_version >= 4)
3090 if (This->rootBlockChain)
3091 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3092 else
3093 /* This file is being created, and it will start out with one block. */
3094 dirsectorcount = 1;
3096 else
3097 /* This field must be 0 in versions older than 4 */
3098 dirsectorcount = 0;
3100 StorageUtl_WriteDWord(
3101 headerBigBlock,
3102 OFFSET_DIRSECTORCOUNT,
3103 dirsectorcount);
3105 StorageUtl_WriteDWord(
3106 headerBigBlock,
3107 OFFSET_BBDEPOTCOUNT,
3108 This->bigBlockDepotCount);
3110 StorageUtl_WriteDWord(
3111 headerBigBlock,
3112 OFFSET_ROOTSTARTBLOCK,
3113 This->rootStartBlock);
3115 StorageUtl_WriteDWord(
3116 headerBigBlock,
3117 OFFSET_TRANSACTIONSIG,
3118 This->transactionSig);
3120 StorageUtl_WriteDWord(
3121 headerBigBlock,
3122 OFFSET_SMALLBLOCKLIMIT,
3123 This->smallBlockLimit);
3125 StorageUtl_WriteDWord(
3126 headerBigBlock,
3127 OFFSET_SBDEPOTSTART,
3128 This->smallBlockDepotStart);
3130 StorageUtl_WriteDWord(
3131 headerBigBlock,
3132 OFFSET_SBDEPOTCOUNT,
3133 This->smallBlockDepotChain ?
3134 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3136 StorageUtl_WriteDWord(
3137 headerBigBlock,
3138 OFFSET_EXTBBDEPOTSTART,
3139 This->extBigBlockDepotStart);
3141 StorageUtl_WriteDWord(
3142 headerBigBlock,
3143 OFFSET_EXTBBDEPOTCOUNT,
3144 This->extBigBlockDepotCount);
3146 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3148 StorageUtl_WriteDWord(
3149 headerBigBlock,
3150 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3151 (This->bigBlockDepotStart[index]));
3155 * Write the big block back to the file.
3157 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3161 /************************************************************************
3162 * StorageImpl implementation : DirEntry methods
3163 ***********************************************************************/
3165 /******************************************************************************
3166 * StorageImpl_ReadRawDirEntry
3168 * This method will read the raw data from a directory entry in the file.
3170 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3172 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3174 ULARGE_INTEGER offset;
3175 HRESULT hr;
3176 ULONG bytesRead;
3178 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3180 hr = BlockChainStream_ReadAt(
3181 This->rootBlockChain,
3182 offset,
3183 RAW_DIRENTRY_SIZE,
3184 buffer,
3185 &bytesRead);
3187 if (bytesRead != RAW_DIRENTRY_SIZE)
3188 return STG_E_READFAULT;
3190 return hr;
3193 /******************************************************************************
3194 * StorageImpl_WriteRawDirEntry
3196 * This method will write the raw data from a directory entry in the file.
3198 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3200 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3202 ULARGE_INTEGER offset;
3203 ULONG bytesRead;
3205 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3207 return BlockChainStream_WriteAt(
3208 This->rootBlockChain,
3209 offset,
3210 RAW_DIRENTRY_SIZE,
3211 buffer,
3212 &bytesRead);
3215 /***************************************************************************
3217 * Internal Method
3219 * Mark a directory entry in the file as free.
3221 static HRESULT StorageImpl_DestroyDirEntry(
3222 StorageBaseImpl *base,
3223 DirRef index)
3225 BYTE emptyData[RAW_DIRENTRY_SIZE];
3226 StorageImpl *storage = (StorageImpl*)base;
3228 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3230 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3233 /******************************************************************************
3234 * UpdateRawDirEntry
3236 * Update raw directory entry data from the fields in newData.
3238 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3240 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3242 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3244 memcpy(
3245 buffer + OFFSET_PS_NAME,
3246 newData->name,
3247 DIRENTRY_NAME_BUFFER_LEN );
3249 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3251 StorageUtl_WriteWord(
3252 buffer,
3253 OFFSET_PS_NAMELENGTH,
3254 newData->sizeOfNameString);
3256 StorageUtl_WriteDWord(
3257 buffer,
3258 OFFSET_PS_LEFTCHILD,
3259 newData->leftChild);
3261 StorageUtl_WriteDWord(
3262 buffer,
3263 OFFSET_PS_RIGHTCHILD,
3264 newData->rightChild);
3266 StorageUtl_WriteDWord(
3267 buffer,
3268 OFFSET_PS_DIRROOT,
3269 newData->dirRootEntry);
3271 StorageUtl_WriteGUID(
3272 buffer,
3273 OFFSET_PS_GUID,
3274 &newData->clsid);
3276 StorageUtl_WriteDWord(
3277 buffer,
3278 OFFSET_PS_CTIMELOW,
3279 newData->ctime.dwLowDateTime);
3281 StorageUtl_WriteDWord(
3282 buffer,
3283 OFFSET_PS_CTIMEHIGH,
3284 newData->ctime.dwHighDateTime);
3286 StorageUtl_WriteDWord(
3287 buffer,
3288 OFFSET_PS_MTIMELOW,
3289 newData->mtime.dwLowDateTime);
3291 StorageUtl_WriteDWord(
3292 buffer,
3293 OFFSET_PS_MTIMEHIGH,
3294 newData->ctime.dwHighDateTime);
3296 StorageUtl_WriteDWord(
3297 buffer,
3298 OFFSET_PS_STARTBLOCK,
3299 newData->startingBlock);
3301 StorageUtl_WriteDWord(
3302 buffer,
3303 OFFSET_PS_SIZE,
3304 newData->size.u.LowPart);
3306 StorageUtl_WriteDWord(
3307 buffer,
3308 OFFSET_PS_SIZE_HIGH,
3309 newData->size.u.HighPart);
3312 /***************************************************************************
3314 * Internal Method
3316 * Reserve a directory entry in the file and initialize it.
3318 static HRESULT StorageImpl_CreateDirEntry(
3319 StorageBaseImpl *base,
3320 const DirEntry *newData,
3321 DirRef *index)
3323 StorageImpl *storage = (StorageImpl*)base;
3324 ULONG currentEntryIndex = 0;
3325 ULONG newEntryIndex = DIRENTRY_NULL;
3326 HRESULT hr = S_OK;
3327 BYTE currentData[RAW_DIRENTRY_SIZE];
3328 WORD sizeOfNameString;
3332 hr = StorageImpl_ReadRawDirEntry(storage,
3333 currentEntryIndex,
3334 currentData);
3336 if (SUCCEEDED(hr))
3338 StorageUtl_ReadWord(
3339 currentData,
3340 OFFSET_PS_NAMELENGTH,
3341 &sizeOfNameString);
3343 if (sizeOfNameString == 0)
3346 * The entry exists and is available, we found it.
3348 newEntryIndex = currentEntryIndex;
3351 else
3354 * We exhausted the directory entries, we will create more space below
3356 newEntryIndex = currentEntryIndex;
3358 currentEntryIndex++;
3360 } while (newEntryIndex == DIRENTRY_NULL);
3363 * grow the directory stream
3365 if (FAILED(hr))
3367 BYTE emptyData[RAW_DIRENTRY_SIZE];
3368 ULARGE_INTEGER newSize;
3369 ULONG entryIndex;
3370 ULONG lastEntry = 0;
3371 ULONG blockCount = 0;
3374 * obtain the new count of blocks in the directory stream
3376 blockCount = BlockChainStream_GetCount(
3377 storage->rootBlockChain)+1;
3380 * initialize the size used by the directory stream
3382 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3385 * add a block to the directory stream
3387 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3390 * memset the empty entry in order to initialize the unused newly
3391 * created entries
3393 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3396 * initialize them
3398 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3400 for(
3401 entryIndex = newEntryIndex + 1;
3402 entryIndex < lastEntry;
3403 entryIndex++)
3405 StorageImpl_WriteRawDirEntry(
3406 storage,
3407 entryIndex,
3408 emptyData);
3411 StorageImpl_SaveFileHeader(storage);
3414 UpdateRawDirEntry(currentData, newData);
3416 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3418 if (SUCCEEDED(hr))
3419 *index = newEntryIndex;
3421 return hr;
3424 /******************************************************************************
3425 * StorageImpl_ReadDirEntry
3427 * This method will read the specified directory entry.
3429 static HRESULT StorageImpl_ReadDirEntry(
3430 StorageImpl* This,
3431 DirRef index,
3432 DirEntry* buffer)
3434 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3435 HRESULT readRes;
3437 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3439 if (SUCCEEDED(readRes))
3441 memset(buffer->name, 0, sizeof(buffer->name));
3442 memcpy(
3443 buffer->name,
3444 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3445 DIRENTRY_NAME_BUFFER_LEN );
3446 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3448 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3450 StorageUtl_ReadWord(
3451 currentEntry,
3452 OFFSET_PS_NAMELENGTH,
3453 &buffer->sizeOfNameString);
3455 StorageUtl_ReadDWord(
3456 currentEntry,
3457 OFFSET_PS_LEFTCHILD,
3458 &buffer->leftChild);
3460 StorageUtl_ReadDWord(
3461 currentEntry,
3462 OFFSET_PS_RIGHTCHILD,
3463 &buffer->rightChild);
3465 StorageUtl_ReadDWord(
3466 currentEntry,
3467 OFFSET_PS_DIRROOT,
3468 &buffer->dirRootEntry);
3470 StorageUtl_ReadGUID(
3471 currentEntry,
3472 OFFSET_PS_GUID,
3473 &buffer->clsid);
3475 StorageUtl_ReadDWord(
3476 currentEntry,
3477 OFFSET_PS_CTIMELOW,
3478 &buffer->ctime.dwLowDateTime);
3480 StorageUtl_ReadDWord(
3481 currentEntry,
3482 OFFSET_PS_CTIMEHIGH,
3483 &buffer->ctime.dwHighDateTime);
3485 StorageUtl_ReadDWord(
3486 currentEntry,
3487 OFFSET_PS_MTIMELOW,
3488 &buffer->mtime.dwLowDateTime);
3490 StorageUtl_ReadDWord(
3491 currentEntry,
3492 OFFSET_PS_MTIMEHIGH,
3493 &buffer->mtime.dwHighDateTime);
3495 StorageUtl_ReadDWord(
3496 currentEntry,
3497 OFFSET_PS_STARTBLOCK,
3498 &buffer->startingBlock);
3500 StorageUtl_ReadDWord(
3501 currentEntry,
3502 OFFSET_PS_SIZE,
3503 &buffer->size.u.LowPart);
3505 if (This->bigBlockSize < 4096)
3507 /* Version 3 files may have junk in the high part of size. */
3508 buffer->size.u.HighPart = 0;
3510 else
3512 StorageUtl_ReadDWord(
3513 currentEntry,
3514 OFFSET_PS_SIZE_HIGH,
3515 &buffer->size.u.HighPart);
3519 return readRes;
3522 /*********************************************************************
3523 * Write the specified directory entry to the file
3525 static HRESULT StorageImpl_WriteDirEntry(
3526 StorageImpl* This,
3527 DirRef index,
3528 const DirEntry* buffer)
3530 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3532 UpdateRawDirEntry(currentEntry, buffer);
3534 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3538 /************************************************************************
3539 * StorageImpl implementation : Block methods
3540 ***********************************************************************/
3542 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3544 return (ULONGLONG)(index+1) * This->bigBlockSize;
3547 static HRESULT StorageImpl_ReadBigBlock(
3548 StorageImpl* This,
3549 ULONG blockIndex,
3550 void* buffer,
3551 ULONG* out_read)
3553 ULARGE_INTEGER ulOffset;
3554 DWORD read=0;
3555 HRESULT hr;
3557 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3559 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3561 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3563 /* File ends during this block; fill the rest with 0's. */
3564 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3567 if (out_read) *out_read = read;
3569 return hr;
3572 static BOOL StorageImpl_ReadDWordFromBigBlock(
3573 StorageImpl* This,
3574 ULONG blockIndex,
3575 ULONG offset,
3576 DWORD* value)
3578 ULARGE_INTEGER ulOffset;
3579 DWORD read;
3580 DWORD tmp;
3582 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3583 ulOffset.QuadPart += offset;
3585 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3586 *value = lendian32toh(tmp);
3587 return (read == sizeof(DWORD));
3590 static BOOL StorageImpl_WriteBigBlock(
3591 StorageImpl* This,
3592 ULONG blockIndex,
3593 const void* buffer)
3595 ULARGE_INTEGER ulOffset;
3596 DWORD wrote;
3598 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3600 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3601 return (wrote == This->bigBlockSize);
3604 static BOOL StorageImpl_WriteDWordToBigBlock(
3605 StorageImpl* This,
3606 ULONG blockIndex,
3607 ULONG offset,
3608 DWORD value)
3610 ULARGE_INTEGER ulOffset;
3611 DWORD wrote;
3613 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3614 ulOffset.QuadPart += offset;
3616 value = htole32(value);
3617 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3618 return (wrote == sizeof(DWORD));
3621 /******************************************************************************
3622 * Storage32Impl_SmallBlocksToBigBlocks
3624 * This method will convert a small block chain to a big block chain.
3625 * The small block chain will be destroyed.
3627 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3628 StorageImpl* This,
3629 SmallBlockChainStream** ppsbChain)
3631 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3632 ULARGE_INTEGER size, offset;
3633 ULONG cbRead, cbWritten;
3634 ULARGE_INTEGER cbTotalRead;
3635 DirRef streamEntryRef;
3636 HRESULT resWrite = S_OK;
3637 HRESULT resRead;
3638 DirEntry streamEntry;
3639 BYTE *buffer;
3640 BlockChainStream *bbTempChain = NULL;
3641 BlockChainStream *bigBlockChain = NULL;
3644 * Create a temporary big block chain that doesn't have
3645 * an associated directory entry. This temporary chain will be
3646 * used to copy data from small blocks to big blocks.
3648 bbTempChain = BlockChainStream_Construct(This,
3649 &bbHeadOfChain,
3650 DIRENTRY_NULL);
3651 if(!bbTempChain) return NULL;
3653 * Grow the big block chain.
3655 size = SmallBlockChainStream_GetSize(*ppsbChain);
3656 BlockChainStream_SetSize(bbTempChain, size);
3659 * Copy the contents of the small block chain to the big block chain
3660 * by small block size increments.
3662 offset.u.LowPart = 0;
3663 offset.u.HighPart = 0;
3664 cbTotalRead.QuadPart = 0;
3666 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3669 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3670 offset,
3671 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3672 buffer,
3673 &cbRead);
3674 if (FAILED(resRead))
3675 break;
3677 if (cbRead > 0)
3679 cbTotalRead.QuadPart += cbRead;
3681 resWrite = BlockChainStream_WriteAt(bbTempChain,
3682 offset,
3683 cbRead,
3684 buffer,
3685 &cbWritten);
3687 if (FAILED(resWrite))
3688 break;
3690 offset.u.LowPart += cbRead;
3692 else
3694 resRead = STG_E_READFAULT;
3695 break;
3697 } while (cbTotalRead.QuadPart < size.QuadPart);
3698 HeapFree(GetProcessHeap(),0,buffer);
3700 size.u.HighPart = 0;
3701 size.u.LowPart = 0;
3703 if (FAILED(resRead) || FAILED(resWrite))
3705 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3706 BlockChainStream_SetSize(bbTempChain, size);
3707 BlockChainStream_Destroy(bbTempChain);
3708 return NULL;
3712 * Destroy the small block chain.
3714 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3715 SmallBlockChainStream_SetSize(*ppsbChain, size);
3716 SmallBlockChainStream_Destroy(*ppsbChain);
3717 *ppsbChain = 0;
3720 * Change the directory entry. This chain is now a big block chain
3721 * and it doesn't reside in the small blocks chain anymore.
3723 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3725 streamEntry.startingBlock = bbHeadOfChain;
3727 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3730 * Destroy the temporary entryless big block chain.
3731 * Create a new big block chain associated with this entry.
3733 BlockChainStream_Destroy(bbTempChain);
3734 bigBlockChain = BlockChainStream_Construct(This,
3735 NULL,
3736 streamEntryRef);
3738 return bigBlockChain;
3741 /******************************************************************************
3742 * Storage32Impl_BigBlocksToSmallBlocks
3744 * This method will convert a big block chain to a small block chain.
3745 * The big block chain will be destroyed on success.
3747 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3748 StorageImpl* This,
3749 BlockChainStream** ppbbChain,
3750 ULARGE_INTEGER newSize)
3752 ULARGE_INTEGER size, offset, cbTotalRead;
3753 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3754 DirRef streamEntryRef;
3755 HRESULT resWrite = S_OK, resRead = S_OK;
3756 DirEntry streamEntry;
3757 BYTE* buffer;
3758 SmallBlockChainStream* sbTempChain;
3760 TRACE("%p %p\n", This, ppbbChain);
3762 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3763 DIRENTRY_NULL);
3765 if(!sbTempChain)
3766 return NULL;
3768 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3769 size = BlockChainStream_GetSize(*ppbbChain);
3770 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3772 offset.u.HighPart = 0;
3773 offset.u.LowPart = 0;
3774 cbTotalRead.QuadPart = 0;
3775 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3776 while(cbTotalRead.QuadPart < size.QuadPart)
3778 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3779 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3780 buffer, &cbRead);
3782 if(FAILED(resRead))
3783 break;
3785 if(cbRead > 0)
3787 cbTotalRead.QuadPart += cbRead;
3789 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3790 cbRead, buffer, &cbWritten);
3792 if(FAILED(resWrite))
3793 break;
3795 offset.u.LowPart += cbRead;
3797 else
3799 resRead = STG_E_READFAULT;
3800 break;
3803 HeapFree(GetProcessHeap(), 0, buffer);
3805 size.u.HighPart = 0;
3806 size.u.LowPart = 0;
3808 if(FAILED(resRead) || FAILED(resWrite))
3810 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3811 SmallBlockChainStream_SetSize(sbTempChain, size);
3812 SmallBlockChainStream_Destroy(sbTempChain);
3813 return NULL;
3816 /* destroy the original big block chain */
3817 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3818 BlockChainStream_SetSize(*ppbbChain, size);
3819 BlockChainStream_Destroy(*ppbbChain);
3820 *ppbbChain = NULL;
3822 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3823 streamEntry.startingBlock = sbHeadOfChain;
3824 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3826 SmallBlockChainStream_Destroy(sbTempChain);
3827 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3830 /******************************************************************************
3831 * Storage32Impl_AddBlockDepot
3833 * This will create a depot block, essentially it is a block initialized
3834 * to BLOCK_UNUSEDs.
3836 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3838 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3839 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3840 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3841 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3844 * Initialize blocks as free
3846 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3848 /* Reserve the range lock sector */
3849 if (depotIndex == rangeLockDepot)
3851 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3854 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3857 /******************************************************************************
3858 * Storage32Impl_GetExtDepotBlock
3860 * Returns the index of the block that corresponds to the specified depot
3861 * index. This method is only for depot indexes equal or greater than
3862 * COUNT_BBDEPOTINHEADER.
3864 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3866 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3867 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3868 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3869 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3870 ULONG blockIndex = BLOCK_UNUSED;
3871 ULONG extBlockIndex;
3872 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3873 int index, num_blocks;
3875 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3877 if (extBlockCount >= This->extBigBlockDepotCount)
3878 return BLOCK_UNUSED;
3880 if (This->indexExtBlockDepotCached != extBlockCount)
3882 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3884 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3886 num_blocks = This->bigBlockSize / 4;
3888 for (index = 0; index < num_blocks; index++)
3890 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3891 This->extBlockDepotCached[index] = blockIndex;
3894 This->indexExtBlockDepotCached = extBlockCount;
3897 blockIndex = This->extBlockDepotCached[extBlockOffset];
3899 return blockIndex;
3902 /******************************************************************************
3903 * Storage32Impl_SetExtDepotBlock
3905 * Associates the specified block index to the specified depot index.
3906 * This method is only for depot indexes equal or greater than
3907 * COUNT_BBDEPOTINHEADER.
3909 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3911 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3912 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3913 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3914 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3915 ULONG extBlockIndex;
3917 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3919 assert(extBlockCount < This->extBigBlockDepotCount);
3921 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3923 if (extBlockIndex != BLOCK_UNUSED)
3925 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3926 extBlockOffset * sizeof(ULONG),
3927 blockIndex);
3930 if (This->indexExtBlockDepotCached == extBlockCount)
3932 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3936 /******************************************************************************
3937 * Storage32Impl_AddExtBlockDepot
3939 * Creates an extended depot block.
3941 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3943 ULONG numExtBlocks = This->extBigBlockDepotCount;
3944 ULONG nextExtBlock = This->extBigBlockDepotStart;
3945 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3946 ULONG index = BLOCK_UNUSED;
3947 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3948 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3949 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3951 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3952 blocksPerDepotBlock;
3954 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3957 * The first extended block.
3959 This->extBigBlockDepotStart = index;
3961 else
3964 * Find the last existing extended block.
3966 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3969 * Add the new extended block to the chain.
3971 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3972 index);
3976 * Initialize this block.
3978 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3979 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3981 /* Add the block to our cache. */
3982 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3984 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3985 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3987 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3988 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3990 This->extBigBlockDepotLocations = new_cache;
3991 This->extBigBlockDepotLocationsSize = new_cache_size;
3993 This->extBigBlockDepotLocations[numExtBlocks] = index;
3995 return index;
3998 /************************************************************************
3999 * StorageImpl_GetNextBlockInChain
4001 * This method will retrieve the block index of the next big block in
4002 * in the chain.
4004 * Params: This - Pointer to the Storage object.
4005 * blockIndex - Index of the block to retrieve the chain
4006 * for.
4007 * nextBlockIndex - receives the return value.
4009 * Returns: This method returns the index of the next block in the chain.
4010 * It will return the constants:
4011 * BLOCK_SPECIAL - If the block given was not part of a
4012 * chain.
4013 * BLOCK_END_OF_CHAIN - If the block given was the last in
4014 * a chain.
4015 * BLOCK_UNUSED - If the block given was not past of a chain
4016 * and is available.
4017 * BLOCK_EXTBBDEPOT - This block is part of the extended
4018 * big block depot.
4020 * See Windows documentation for more details on IStorage methods.
4022 static HRESULT StorageImpl_GetNextBlockInChain(
4023 StorageImpl* This,
4024 ULONG blockIndex,
4025 ULONG* nextBlockIndex)
4027 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4028 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4029 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4030 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4031 ULONG read;
4032 ULONG depotBlockIndexPos;
4033 int index, num_blocks;
4035 *nextBlockIndex = BLOCK_SPECIAL;
4037 if(depotBlockCount >= This->bigBlockDepotCount)
4039 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
4040 This->bigBlockDepotCount);
4041 return STG_E_READFAULT;
4045 * Cache the currently accessed depot block.
4047 if (depotBlockCount != This->indexBlockDepotCached)
4049 This->indexBlockDepotCached = depotBlockCount;
4051 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4053 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4055 else
4058 * We have to look in the extended depot.
4060 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4063 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4065 if (!read)
4066 return STG_E_READFAULT;
4068 num_blocks = This->bigBlockSize / 4;
4070 for (index = 0; index < num_blocks; index++)
4072 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4073 This->blockDepotCached[index] = *nextBlockIndex;
4077 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4079 return S_OK;
4082 /******************************************************************************
4083 * Storage32Impl_GetNextExtendedBlock
4085 * Given an extended block this method will return the next extended block.
4087 * NOTES:
4088 * The last ULONG of an extended block is the block index of the next
4089 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4090 * depot.
4092 * Return values:
4093 * - The index of the next extended block
4094 * - BLOCK_UNUSED: there is no next extended block.
4095 * - Any other return values denotes failure.
4097 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4099 ULONG nextBlockIndex = BLOCK_SPECIAL;
4100 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4102 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4103 &nextBlockIndex);
4105 return nextBlockIndex;
4108 /******************************************************************************
4109 * StorageImpl_SetNextBlockInChain
4111 * This method will write the index of the specified block's next block
4112 * in the big block depot.
4114 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4115 * do the following
4117 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4118 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4119 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4122 static void StorageImpl_SetNextBlockInChain(
4123 StorageImpl* This,
4124 ULONG blockIndex,
4125 ULONG nextBlock)
4127 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4128 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4129 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4130 ULONG depotBlockIndexPos;
4132 assert(depotBlockCount < This->bigBlockDepotCount);
4133 assert(blockIndex != nextBlock);
4135 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4136 /* This should never happen (storage file format spec forbids it), but
4137 * older versions of Wine may have generated broken files. We don't want to
4138 * assert and potentially lose data, but we do want to know if this ever
4139 * happens in a newly-created file. */
4140 ERR("Using range lock page\n");
4142 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4144 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4146 else
4149 * We have to look in the extended depot.
4151 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4154 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4155 nextBlock);
4157 * Update the cached block depot, if necessary.
4159 if (depotBlockCount == This->indexBlockDepotCached)
4161 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4165 /******************************************************************************
4166 * StorageImpl_GetNextFreeBigBlock
4168 * Returns the index of the next free big block.
4169 * If the big block depot is filled, this method will enlarge it.
4172 static ULONG StorageImpl_GetNextFreeBigBlock(
4173 StorageImpl* This)
4175 ULONG depotBlockIndexPos;
4176 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4177 ULONG depotBlockOffset;
4178 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4179 ULONG nextBlockIndex = BLOCK_SPECIAL;
4180 int depotIndex = 0;
4181 ULONG freeBlock = BLOCK_UNUSED;
4182 ULONG read;
4183 ULARGE_INTEGER neededSize;
4184 STATSTG statstg;
4186 depotIndex = This->prevFreeBlock / blocksPerDepot;
4187 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4190 * Scan the entire big block depot until we find a block marked free
4192 while (nextBlockIndex != BLOCK_UNUSED)
4194 if (depotIndex < COUNT_BBDEPOTINHEADER)
4196 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4199 * Grow the primary depot.
4201 if (depotBlockIndexPos == BLOCK_UNUSED)
4203 depotBlockIndexPos = depotIndex*blocksPerDepot;
4206 * Add a block depot.
4208 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4209 This->bigBlockDepotCount++;
4210 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4213 * Flag it as a block depot.
4215 StorageImpl_SetNextBlockInChain(This,
4216 depotBlockIndexPos,
4217 BLOCK_SPECIAL);
4219 /* Save new header information.
4221 StorageImpl_SaveFileHeader(This);
4224 else
4226 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4228 if (depotBlockIndexPos == BLOCK_UNUSED)
4231 * Grow the extended depot.
4233 ULONG extIndex = BLOCK_UNUSED;
4234 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4235 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4237 if (extBlockOffset == 0)
4239 /* We need an extended block.
4241 extIndex = Storage32Impl_AddExtBlockDepot(This);
4242 This->extBigBlockDepotCount++;
4243 depotBlockIndexPos = extIndex + 1;
4245 else
4246 depotBlockIndexPos = depotIndex * blocksPerDepot;
4249 * Add a block depot and mark it in the extended block.
4251 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4252 This->bigBlockDepotCount++;
4253 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4255 /* Flag the block depot.
4257 StorageImpl_SetNextBlockInChain(This,
4258 depotBlockIndexPos,
4259 BLOCK_SPECIAL);
4261 /* If necessary, flag the extended depot block.
4263 if (extIndex != BLOCK_UNUSED)
4264 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4266 /* Save header information.
4268 StorageImpl_SaveFileHeader(This);
4272 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4274 if (read)
4276 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4277 ( nextBlockIndex != BLOCK_UNUSED))
4279 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4281 if (nextBlockIndex == BLOCK_UNUSED)
4283 freeBlock = (depotIndex * blocksPerDepot) +
4284 (depotBlockOffset/sizeof(ULONG));
4287 depotBlockOffset += sizeof(ULONG);
4291 depotIndex++;
4292 depotBlockOffset = 0;
4296 * make sure that the block physically exists before using it
4298 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4300 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4302 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4303 ILockBytes_SetSize(This->lockBytes, neededSize);
4305 This->prevFreeBlock = freeBlock;
4307 return freeBlock;
4310 /******************************************************************************
4311 * StorageImpl_FreeBigBlock
4313 * This method will flag the specified block as free in the big block depot.
4315 static void StorageImpl_FreeBigBlock(
4316 StorageImpl* This,
4317 ULONG blockIndex)
4319 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4321 if (blockIndex < This->prevFreeBlock)
4322 This->prevFreeBlock = blockIndex;
4326 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4327 DirRef index, const DirEntry *data)
4329 StorageImpl *This = (StorageImpl*)base;
4330 return StorageImpl_WriteDirEntry(This, index, data);
4333 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4334 DirRef index, DirEntry *data)
4336 StorageImpl *This = (StorageImpl*)base;
4337 return StorageImpl_ReadDirEntry(This, index, data);
4340 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4342 int i;
4344 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4346 if (!This->blockChainCache[i])
4348 return &This->blockChainCache[i];
4352 i = This->blockChainToEvict;
4354 BlockChainStream_Destroy(This->blockChainCache[i]);
4355 This->blockChainCache[i] = NULL;
4357 This->blockChainToEvict++;
4358 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4359 This->blockChainToEvict = 0;
4361 return &This->blockChainCache[i];
4364 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4365 DirRef index)
4367 int i, free_index=-1;
4369 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4371 if (!This->blockChainCache[i])
4373 if (free_index == -1) free_index = i;
4375 else if (This->blockChainCache[i]->ownerDirEntry == index)
4377 return &This->blockChainCache[i];
4381 if (free_index == -1)
4383 free_index = This->blockChainToEvict;
4385 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4386 This->blockChainCache[free_index] = NULL;
4388 This->blockChainToEvict++;
4389 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4390 This->blockChainToEvict = 0;
4393 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4394 return &This->blockChainCache[free_index];
4397 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4399 int i;
4401 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4403 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4405 BlockChainStream_Destroy(This->blockChainCache[i]);
4406 This->blockChainCache[i] = NULL;
4407 return;
4412 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4413 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4415 StorageImpl *This = (StorageImpl*)base;
4416 DirEntry data;
4417 HRESULT hr;
4418 ULONG bytesToRead;
4420 hr = StorageImpl_ReadDirEntry(This, index, &data);
4421 if (FAILED(hr)) return hr;
4423 if (data.size.QuadPart == 0)
4425 *bytesRead = 0;
4426 return S_OK;
4429 if (offset.QuadPart + size > data.size.QuadPart)
4431 bytesToRead = data.size.QuadPart - offset.QuadPart;
4433 else
4435 bytesToRead = size;
4438 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4440 SmallBlockChainStream *stream;
4442 stream = SmallBlockChainStream_Construct(This, NULL, index);
4443 if (!stream) return E_OUTOFMEMORY;
4445 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4447 SmallBlockChainStream_Destroy(stream);
4449 return hr;
4451 else
4453 BlockChainStream *stream = NULL;
4455 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4456 if (!stream) return E_OUTOFMEMORY;
4458 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4460 return hr;
4464 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4465 ULARGE_INTEGER newsize)
4467 StorageImpl *This = (StorageImpl*)base;
4468 DirEntry data;
4469 HRESULT hr;
4470 SmallBlockChainStream *smallblock=NULL;
4471 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4473 hr = StorageImpl_ReadDirEntry(This, index, &data);
4474 if (FAILED(hr)) return hr;
4476 /* In simple mode keep the stream size above the small block limit */
4477 if (This->base.openFlags & STGM_SIMPLE)
4478 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4480 if (data.size.QuadPart == newsize.QuadPart)
4481 return S_OK;
4483 /* Create a block chain object of the appropriate type */
4484 if (data.size.QuadPart == 0)
4486 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4488 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4489 if (!smallblock) return E_OUTOFMEMORY;
4491 else
4493 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4494 bigblock = *pbigblock;
4495 if (!bigblock) return E_OUTOFMEMORY;
4498 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4500 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4501 if (!smallblock) return E_OUTOFMEMORY;
4503 else
4505 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4506 bigblock = *pbigblock;
4507 if (!bigblock) return E_OUTOFMEMORY;
4510 /* Change the block chain type if necessary. */
4511 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4513 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4514 if (!bigblock)
4516 SmallBlockChainStream_Destroy(smallblock);
4517 return E_FAIL;
4520 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4521 *pbigblock = bigblock;
4523 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4525 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4526 if (!smallblock)
4527 return E_FAIL;
4530 /* Set the size of the block chain. */
4531 if (smallblock)
4533 SmallBlockChainStream_SetSize(smallblock, newsize);
4534 SmallBlockChainStream_Destroy(smallblock);
4536 else
4538 BlockChainStream_SetSize(bigblock, newsize);
4541 /* Set the size in the directory entry. */
4542 hr = StorageImpl_ReadDirEntry(This, index, &data);
4543 if (SUCCEEDED(hr))
4545 data.size = newsize;
4547 hr = StorageImpl_WriteDirEntry(This, index, &data);
4549 return hr;
4552 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4553 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4555 StorageImpl *This = (StorageImpl*)base;
4556 DirEntry data;
4557 HRESULT hr;
4558 ULARGE_INTEGER newSize;
4560 hr = StorageImpl_ReadDirEntry(This, index, &data);
4561 if (FAILED(hr)) return hr;
4563 /* Grow the stream if necessary */
4564 newSize.QuadPart = offset.QuadPart + size;
4566 if (newSize.QuadPart > data.size.QuadPart)
4568 hr = StorageImpl_StreamSetSize(base, index, newSize);
4569 if (FAILED(hr))
4570 return hr;
4572 hr = StorageImpl_ReadDirEntry(This, index, &data);
4573 if (FAILED(hr)) return hr;
4576 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4578 SmallBlockChainStream *stream;
4580 stream = SmallBlockChainStream_Construct(This, NULL, index);
4581 if (!stream) return E_OUTOFMEMORY;
4583 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4585 SmallBlockChainStream_Destroy(stream);
4587 return hr;
4589 else
4591 BlockChainStream *stream;
4593 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4594 if (!stream) return E_OUTOFMEMORY;
4596 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4600 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4601 DirRef src)
4603 StorageImpl *This = (StorageImpl*)base;
4604 DirEntry dst_data, src_data;
4605 HRESULT hr;
4607 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4609 if (SUCCEEDED(hr))
4610 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4612 if (SUCCEEDED(hr))
4614 StorageImpl_DeleteCachedBlockChainStream(This, src);
4615 dst_data.startingBlock = src_data.startingBlock;
4616 dst_data.size = src_data.size;
4618 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4621 return hr;
4624 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4626 HRESULT hr=S_OK;
4627 DirEntry currentEntry;
4628 DirRef currentEntryRef;
4629 BlockChainStream *blockChainStream;
4631 if (create)
4633 ULARGE_INTEGER size;
4634 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4636 /* Discard any existing data. */
4637 size.QuadPart = 0;
4638 ILockBytes_SetSize(This->lockBytes, size);
4641 * Initialize all header variables:
4642 * - The big block depot consists of one block and it is at block 0
4643 * - The directory table starts at block 1
4644 * - There is no small block depot
4646 memset( This->bigBlockDepotStart,
4647 BLOCK_UNUSED,
4648 sizeof(This->bigBlockDepotStart));
4650 This->bigBlockDepotCount = 1;
4651 This->bigBlockDepotStart[0] = 0;
4652 This->rootStartBlock = 1;
4653 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4654 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4655 if (This->bigBlockSize == 4096)
4656 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4657 else
4658 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4659 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4660 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4661 This->extBigBlockDepotCount = 0;
4663 StorageImpl_SaveFileHeader(This);
4666 * Add one block for the big block depot and one block for the directory table
4668 size.u.HighPart = 0;
4669 size.u.LowPart = This->bigBlockSize * 3;
4670 ILockBytes_SetSize(This->lockBytes, size);
4673 * Initialize the big block depot
4675 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4676 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4677 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4678 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4680 else
4683 * Load the header for the file.
4685 hr = StorageImpl_LoadFileHeader(This);
4687 if (FAILED(hr))
4689 return hr;
4694 * There is no block depot cached yet.
4696 This->indexBlockDepotCached = 0xFFFFFFFF;
4697 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4700 * Start searching for free blocks with block 0.
4702 This->prevFreeBlock = 0;
4704 This->firstFreeSmallBlock = 0;
4706 /* Read the extended big block depot locations. */
4707 if (This->extBigBlockDepotCount != 0)
4709 ULONG current_block = This->extBigBlockDepotStart;
4710 ULONG cache_size = This->extBigBlockDepotCount * 2;
4711 ULONG i;
4713 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4714 if (!This->extBigBlockDepotLocations)
4716 return E_OUTOFMEMORY;
4719 This->extBigBlockDepotLocationsSize = cache_size;
4721 for (i=0; i<This->extBigBlockDepotCount; i++)
4723 if (current_block == BLOCK_END_OF_CHAIN)
4725 WARN("File has too few extended big block depot blocks.\n");
4726 return STG_E_DOCFILECORRUPT;
4728 This->extBigBlockDepotLocations[i] = current_block;
4729 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4732 else
4734 This->extBigBlockDepotLocations = NULL;
4735 This->extBigBlockDepotLocationsSize = 0;
4739 * Create the block chain abstractions.
4741 if(!(blockChainStream =
4742 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4744 return STG_E_READFAULT;
4746 if (!new_object)
4747 BlockChainStream_Destroy(This->rootBlockChain);
4748 This->rootBlockChain = blockChainStream;
4750 if(!(blockChainStream =
4751 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4752 DIRENTRY_NULL)))
4754 return STG_E_READFAULT;
4756 if (!new_object)
4757 BlockChainStream_Destroy(This->smallBlockDepotChain);
4758 This->smallBlockDepotChain = blockChainStream;
4761 * Write the root storage entry (memory only)
4763 if (create)
4765 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4766 DirEntry rootEntry;
4768 * Initialize the directory table
4770 memset(&rootEntry, 0, sizeof(rootEntry));
4771 strcpyW(rootEntry.name, rootentryW);
4772 rootEntry.sizeOfNameString = sizeof(rootentryW);
4773 rootEntry.stgType = STGTY_ROOT;
4774 rootEntry.leftChild = DIRENTRY_NULL;
4775 rootEntry.rightChild = DIRENTRY_NULL;
4776 rootEntry.dirRootEntry = DIRENTRY_NULL;
4777 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4778 rootEntry.size.u.HighPart = 0;
4779 rootEntry.size.u.LowPart = 0;
4781 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4785 * Find the ID of the root storage.
4787 currentEntryRef = 0;
4791 hr = StorageImpl_ReadDirEntry(
4792 This,
4793 currentEntryRef,
4794 &currentEntry);
4796 if (SUCCEEDED(hr))
4798 if ( (currentEntry.sizeOfNameString != 0 ) &&
4799 (currentEntry.stgType == STGTY_ROOT) )
4801 This->base.storageDirEntry = currentEntryRef;
4805 currentEntryRef++;
4807 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4809 if (FAILED(hr))
4811 return STG_E_READFAULT;
4815 * Create the block chain abstraction for the small block root chain.
4817 if(!(blockChainStream =
4818 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4820 return STG_E_READFAULT;
4822 if (!new_object)
4823 BlockChainStream_Destroy(This->smallBlockRootChain);
4824 This->smallBlockRootChain = blockChainStream;
4826 if (!new_object)
4828 int i;
4829 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4831 BlockChainStream_Destroy(This->blockChainCache[i]);
4832 This->blockChainCache[i] = NULL;
4836 return hr;
4839 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4840 ULONG* result, BOOL refresh)
4842 StorageImpl *This = (StorageImpl*)base;
4843 HRESULT hr=S_OK;
4844 DWORD oldTransactionSig = This->transactionSig;
4846 if (refresh)
4848 ULARGE_INTEGER offset;
4849 ULONG bytes_read;
4850 BYTE data[4];
4852 offset.u.HighPart = 0;
4853 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4854 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4856 if (SUCCEEDED(hr))
4858 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4860 if (oldTransactionSig != This->transactionSig)
4862 /* Someone else wrote to this, so toss all cached information. */
4863 TRACE("signature changed\n");
4865 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4868 if (FAILED(hr))
4869 This->transactionSig = oldTransactionSig;
4873 *result = This->transactionSig;
4875 return hr;
4878 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4879 ULONG value)
4881 StorageImpl *This = (StorageImpl*)base;
4883 This->transactionSig = value;
4884 StorageImpl_SaveFileHeader(This);
4886 return S_OK;
4889 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4890 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4892 if ((dwLockType & This->locks_supported) == 0)
4894 if (supported) *supported = FALSE;
4895 return S_OK;
4898 if (supported) *supported = TRUE;
4899 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4902 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4903 ULARGE_INTEGER cb, DWORD dwLockType)
4905 if ((dwLockType & This->locks_supported) == 0)
4906 return S_OK;
4908 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4911 /* Internal function */
4912 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4913 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4915 HRESULT hr;
4916 int delay = 0;
4917 DWORD start_time = GetTickCount();
4918 DWORD last_sanity_check = start_time;
4919 ULARGE_INTEGER sanity_offset, sanity_cb;
4921 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4922 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4926 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4928 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4930 DWORD current_time = GetTickCount();
4931 if (current_time - start_time >= 20000)
4933 /* timeout */
4934 break;
4936 if (current_time - last_sanity_check >= 500)
4938 /* Any storage implementation with the file open in a
4939 * shared mode should not lock these bytes for writing. However,
4940 * some programs (LibreOffice Writer) will keep ALL bytes locked
4941 * when opening in exclusive mode. We can use a read lock to
4942 * detect this case early, and not hang a full 20 seconds.
4944 * This can collide with another attempt to open the file in
4945 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4946 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4947 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4948 break;
4949 if (SUCCEEDED(hr))
4951 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4952 hr = STG_E_ACCESSDENIED;
4955 last_sanity_check = current_time;
4957 Sleep(delay);
4958 if (delay < 150) delay++;
4960 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4962 return hr;
4965 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4967 StorageImpl *This = (StorageImpl*)base;
4968 HRESULT hr;
4969 ULARGE_INTEGER offset, cb;
4971 if (write)
4973 /* Synchronous grab of second priority range, the commit lock, and the
4974 * lock-checking lock. */
4975 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4976 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4978 else
4980 offset.QuadPart = RANGELOCK_COMMIT;
4981 cb.QuadPart = 1;
4984 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4986 return hr;
4989 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4991 StorageImpl *This = (StorageImpl*)base;
4992 HRESULT hr;
4993 ULARGE_INTEGER offset, cb;
4995 if (write)
4997 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4998 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
5000 else
5002 offset.QuadPart = RANGELOCK_COMMIT;
5003 cb.QuadPart = 1;
5006 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5008 return hr;
5011 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5013 StorageImpl *This = (StorageImpl*) iface;
5014 STATSTG statstg;
5015 HRESULT hr;
5017 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
5019 *result = statstg.pwcsName;
5021 return hr;
5024 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
5025 ULONG end, HRESULT fail_hr)
5027 HRESULT hr;
5028 ULARGE_INTEGER offset, cb;
5030 offset.QuadPart = start;
5031 cb.QuadPart = 1 + end - start;
5033 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5034 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5036 if (FAILED(hr))
5037 return fail_hr;
5038 else
5039 return S_OK;
5042 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
5044 HRESULT hr=S_OK;
5045 int i, j;
5046 ULARGE_INTEGER offset, cb;
5048 cb.QuadPart = 1;
5050 for (i=start; i<=end; i++)
5052 offset.QuadPart = i;
5053 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5054 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
5055 break;
5058 if (SUCCEEDED(hr))
5060 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
5062 if (This->locked_bytes[j] == 0)
5064 This->locked_bytes[j] = i;
5065 break;
5070 return hr;
5073 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5075 HRESULT hr;
5076 ULARGE_INTEGER offset;
5077 ULARGE_INTEGER cb;
5078 DWORD share_mode = STGM_SHARE_MODE(openFlags);
5079 BOOL supported;
5081 if (openFlags & STGM_NOSNAPSHOT)
5083 /* STGM_NOSNAPSHOT implies deny write */
5084 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5085 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5088 /* Wrap all other locking inside a single lock so we can check ranges safely */
5089 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5090 cb.QuadPart = 1;
5091 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5093 /* If the ILockBytes doesn't support locking that's ok. */
5094 if (!supported) return S_OK;
5095 else if (FAILED(hr)) return hr;
5097 hr = S_OK;
5099 /* First check for any conflicting locks. */
5100 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5101 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5103 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5104 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5106 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5107 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5109 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5110 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5112 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5113 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5115 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5117 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5119 if (SUCCEEDED(hr))
5120 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5123 /* Then grab our locks. */
5124 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5126 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5127 if (SUCCEEDED(hr))
5128 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5131 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5132 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5134 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5135 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5137 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5138 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5140 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5141 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5143 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5144 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5146 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5147 cb.QuadPart = 1;
5148 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5150 return hr;
5153 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5155 StorageImpl *This = (StorageImpl*)storage;
5156 int i;
5157 HRESULT hr;
5158 TRACE("(%p)\n", This);
5160 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5162 if (SUCCEEDED(hr))
5163 hr = BlockChainStream_Flush(This->rootBlockChain);
5165 if (SUCCEEDED(hr))
5166 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5168 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5169 if (This->blockChainCache[i])
5170 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5172 if (SUCCEEDED(hr))
5173 hr = ILockBytes_Flush(This->lockBytes);
5175 return hr;
5178 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5180 StorageImpl *This = (StorageImpl*) iface;
5182 StorageBaseImpl_DeleteAll(&This->base);
5184 This->base.reverted = TRUE;
5187 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5189 StorageImpl *This = (StorageImpl*) iface;
5190 int i;
5191 TRACE("(%p)\n", This);
5193 StorageImpl_Flush(iface);
5195 StorageImpl_Invalidate(iface);
5197 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5199 BlockChainStream_Destroy(This->smallBlockRootChain);
5200 BlockChainStream_Destroy(This->rootBlockChain);
5201 BlockChainStream_Destroy(This->smallBlockDepotChain);
5203 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
5204 BlockChainStream_Destroy(This->blockChainCache[i]);
5206 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
5208 ULARGE_INTEGER offset, cb;
5209 cb.QuadPart = 1;
5210 if (This->locked_bytes[i] != 0)
5212 offset.QuadPart = This->locked_bytes[i];
5213 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5217 if (This->lockBytes)
5218 ILockBytes_Release(This->lockBytes);
5219 HeapFree(GetProcessHeap(), 0, This);
5223 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5225 StorageImpl_Destroy,
5226 StorageImpl_Invalidate,
5227 StorageImpl_Flush,
5228 StorageImpl_GetFilename,
5229 StorageImpl_CreateDirEntry,
5230 StorageImpl_BaseWriteDirEntry,
5231 StorageImpl_BaseReadDirEntry,
5232 StorageImpl_DestroyDirEntry,
5233 StorageImpl_StreamReadAt,
5234 StorageImpl_StreamWriteAt,
5235 StorageImpl_StreamSetSize,
5236 StorageImpl_StreamLink,
5237 StorageImpl_GetTransactionSig,
5238 StorageImpl_SetTransactionSig,
5239 StorageImpl_LockTransaction,
5240 StorageImpl_UnlockTransaction
5245 * Virtual function table for the IStorageBaseImpl class.
5247 static const IStorageVtbl StorageImpl_Vtbl =
5249 StorageBaseImpl_QueryInterface,
5250 StorageBaseImpl_AddRef,
5251 StorageBaseImpl_Release,
5252 StorageBaseImpl_CreateStream,
5253 StorageBaseImpl_OpenStream,
5254 StorageBaseImpl_CreateStorage,
5255 StorageBaseImpl_OpenStorage,
5256 StorageBaseImpl_CopyTo,
5257 StorageBaseImpl_MoveElementTo,
5258 StorageBaseImpl_Commit,
5259 StorageBaseImpl_Revert,
5260 StorageBaseImpl_EnumElements,
5261 StorageBaseImpl_DestroyElement,
5262 StorageBaseImpl_RenameElement,
5263 StorageBaseImpl_SetElementTimes,
5264 StorageBaseImpl_SetClass,
5265 StorageBaseImpl_SetStateBits,
5266 StorageBaseImpl_Stat
5269 static HRESULT StorageImpl_Construct(
5270 HANDLE hFile,
5271 LPCOLESTR pwcsName,
5272 ILockBytes* pLkbyt,
5273 DWORD openFlags,
5274 BOOL fileBased,
5275 BOOL create,
5276 ULONG sector_size,
5277 StorageImpl** result)
5279 StorageImpl* This;
5280 HRESULT hr = S_OK;
5281 STATSTG stat;
5283 if ( FAILED( validateSTGM(openFlags) ))
5284 return STG_E_INVALIDFLAG;
5286 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5287 if (!This)
5288 return E_OUTOFMEMORY;
5290 memset(This, 0, sizeof(StorageImpl));
5292 list_init(&This->base.strmHead);
5294 list_init(&This->base.storageHead);
5296 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5297 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5298 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5299 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5300 This->base.openFlags = (openFlags & ~STGM_CREATE);
5301 This->base.ref = 1;
5302 This->base.create = create;
5304 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5305 This->base.lockingrole = SWMR_Writer;
5306 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5307 This->base.lockingrole = SWMR_Reader;
5308 else
5309 This->base.lockingrole = SWMR_None;
5311 This->base.reverted = FALSE;
5314 * Initialize the big block cache.
5316 This->bigBlockSize = sector_size;
5317 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5318 if (hFile)
5319 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5320 else
5322 This->lockBytes = pLkbyt;
5323 ILockBytes_AddRef(pLkbyt);
5326 if (SUCCEEDED(hr))
5327 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5329 if (SUCCEEDED(hr))
5331 This->locks_supported = stat.grfLocksSupported;
5332 if (!hFile)
5333 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5334 This->locks_supported &= ~WINE_LOCK_READ;
5336 hr = StorageImpl_GrabLocks(This, openFlags);
5339 if (SUCCEEDED(hr))
5340 hr = StorageImpl_Refresh(This, TRUE, create);
5342 if (FAILED(hr))
5344 IStorage_Release(&This->base.IStorage_iface);
5345 *result = NULL;
5347 else
5349 StorageImpl_Flush(&This->base);
5350 *result = This;
5353 return hr;
5357 /************************************************************************
5358 * StorageInternalImpl implementation
5359 ***********************************************************************/
5361 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5363 StorageInternalImpl* This = (StorageInternalImpl*) base;
5365 if (!This->base.reverted)
5367 TRACE("Storage invalidated (stg=%p)\n", This);
5369 This->base.reverted = TRUE;
5371 This->parentStorage = NULL;
5373 StorageBaseImpl_DeleteAll(&This->base);
5375 list_remove(&This->ParentListEntry);
5379 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5381 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5383 StorageInternalImpl_Invalidate(&This->base);
5385 HeapFree(GetProcessHeap(), 0, This);
5388 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5390 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5392 return StorageBaseImpl_Flush(This->parentStorage);
5395 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5397 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5399 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5402 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5403 const DirEntry *newData, DirRef *index)
5405 StorageInternalImpl* This = (StorageInternalImpl*) base;
5407 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5408 newData, index);
5411 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5412 DirRef index, const DirEntry *data)
5414 StorageInternalImpl* This = (StorageInternalImpl*) base;
5416 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5417 index, data);
5420 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5421 DirRef index, DirEntry *data)
5423 StorageInternalImpl* This = (StorageInternalImpl*) base;
5425 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5426 index, data);
5429 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5430 DirRef index)
5432 StorageInternalImpl* This = (StorageInternalImpl*) base;
5434 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5435 index);
5438 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5439 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5441 StorageInternalImpl* This = (StorageInternalImpl*) base;
5443 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5444 index, offset, size, buffer, bytesRead);
5447 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5448 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5450 StorageInternalImpl* This = (StorageInternalImpl*) base;
5452 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5453 index, offset, size, buffer, bytesWritten);
5456 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5457 DirRef index, ULARGE_INTEGER newsize)
5459 StorageInternalImpl* This = (StorageInternalImpl*) base;
5461 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5462 index, newsize);
5465 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5466 DirRef dst, DirRef src)
5468 StorageInternalImpl* This = (StorageInternalImpl*) base;
5470 return StorageBaseImpl_StreamLink(This->parentStorage,
5471 dst, src);
5474 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5475 ULONG* result, BOOL refresh)
5477 return E_NOTIMPL;
5480 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5481 ULONG value)
5483 return E_NOTIMPL;
5486 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5488 return E_NOTIMPL;
5491 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5493 return E_NOTIMPL;
5496 /******************************************************************************
5498 ** StorageInternalImpl_Commit
5501 static HRESULT WINAPI StorageInternalImpl_Commit(
5502 IStorage* iface,
5503 DWORD grfCommitFlags) /* [in] */
5505 StorageBaseImpl* This = impl_from_IStorage(iface);
5506 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5507 return StorageBaseImpl_Flush(This);
5510 /******************************************************************************
5512 ** StorageInternalImpl_Revert
5515 static HRESULT WINAPI StorageInternalImpl_Revert(
5516 IStorage* iface)
5518 FIXME("(%p): stub\n", iface);
5519 return S_OK;
5523 * Virtual function table for the StorageInternalImpl class.
5525 static const IStorageVtbl StorageInternalImpl_Vtbl =
5527 StorageBaseImpl_QueryInterface,
5528 StorageBaseImpl_AddRef,
5529 StorageBaseImpl_Release,
5530 StorageBaseImpl_CreateStream,
5531 StorageBaseImpl_OpenStream,
5532 StorageBaseImpl_CreateStorage,
5533 StorageBaseImpl_OpenStorage,
5534 StorageBaseImpl_CopyTo,
5535 StorageBaseImpl_MoveElementTo,
5536 StorageInternalImpl_Commit,
5537 StorageInternalImpl_Revert,
5538 StorageBaseImpl_EnumElements,
5539 StorageBaseImpl_DestroyElement,
5540 StorageBaseImpl_RenameElement,
5541 StorageBaseImpl_SetElementTimes,
5542 StorageBaseImpl_SetClass,
5543 StorageBaseImpl_SetStateBits,
5544 StorageBaseImpl_Stat
5547 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5549 StorageInternalImpl_Destroy,
5550 StorageInternalImpl_Invalidate,
5551 StorageInternalImpl_Flush,
5552 StorageInternalImpl_GetFilename,
5553 StorageInternalImpl_CreateDirEntry,
5554 StorageInternalImpl_WriteDirEntry,
5555 StorageInternalImpl_ReadDirEntry,
5556 StorageInternalImpl_DestroyDirEntry,
5557 StorageInternalImpl_StreamReadAt,
5558 StorageInternalImpl_StreamWriteAt,
5559 StorageInternalImpl_StreamSetSize,
5560 StorageInternalImpl_StreamLink,
5561 StorageInternalImpl_GetTransactionSig,
5562 StorageInternalImpl_SetTransactionSig,
5563 StorageInternalImpl_LockTransaction,
5564 StorageInternalImpl_UnlockTransaction
5567 static StorageInternalImpl* StorageInternalImpl_Construct(
5568 StorageBaseImpl* parentStorage,
5569 DWORD openFlags,
5570 DirRef storageDirEntry)
5572 StorageInternalImpl* newStorage;
5574 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5576 if (newStorage!=0)
5578 list_init(&newStorage->base.strmHead);
5580 list_init(&newStorage->base.storageHead);
5583 * Initialize the virtual function table.
5585 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5586 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5587 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5588 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5590 newStorage->base.reverted = FALSE;
5592 newStorage->base.ref = 1;
5594 newStorage->parentStorage = parentStorage;
5597 * Keep a reference to the directory entry of this storage
5599 newStorage->base.storageDirEntry = storageDirEntry;
5601 newStorage->base.create = FALSE;
5603 return newStorage;
5606 return 0;
5610 /************************************************************************
5611 * TransactedSnapshotImpl implementation
5612 ***********************************************************************/
5614 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5616 DirRef result=This->firstFreeEntry;
5618 while (result < This->entries_size && This->entries[result].inuse)
5619 result++;
5621 if (result == This->entries_size)
5623 ULONG new_size = This->entries_size * 2;
5624 TransactedDirEntry *new_entries;
5626 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5627 if (!new_entries) return DIRENTRY_NULL;
5629 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5630 HeapFree(GetProcessHeap(), 0, This->entries);
5632 This->entries = new_entries;
5633 This->entries_size = new_size;
5636 This->entries[result].inuse = TRUE;
5638 This->firstFreeEntry = result+1;
5640 return result;
5643 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5644 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5646 DirRef stubEntryRef;
5647 TransactedDirEntry *entry;
5649 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5651 if (stubEntryRef != DIRENTRY_NULL)
5653 entry = &This->entries[stubEntryRef];
5655 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5657 entry->read = FALSE;
5660 return stubEntryRef;
5663 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5664 TransactedSnapshotImpl *This, DirRef entry)
5666 HRESULT hr=S_OK;
5667 DirEntry data;
5669 if (!This->entries[entry].read)
5671 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5672 This->entries[entry].transactedParentEntry,
5673 &data);
5675 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5677 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5679 if (data.leftChild == DIRENTRY_NULL)
5680 hr = E_OUTOFMEMORY;
5683 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5685 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5687 if (data.rightChild == DIRENTRY_NULL)
5688 hr = E_OUTOFMEMORY;
5691 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5693 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5695 if (data.dirRootEntry == DIRENTRY_NULL)
5696 hr = E_OUTOFMEMORY;
5699 if (SUCCEEDED(hr))
5701 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5702 This->entries[entry].read = TRUE;
5706 return hr;
5709 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5710 TransactedSnapshotImpl *This, DirRef entry)
5712 HRESULT hr = S_OK;
5714 if (!This->entries[entry].stream_dirty)
5716 DirEntry new_entrydata;
5718 memset(&new_entrydata, 0, sizeof(DirEntry));
5719 new_entrydata.name[0] = 'S';
5720 new_entrydata.sizeOfNameString = 1;
5721 new_entrydata.stgType = STGTY_STREAM;
5722 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5723 new_entrydata.leftChild = DIRENTRY_NULL;
5724 new_entrydata.rightChild = DIRENTRY_NULL;
5725 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5727 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5728 &This->entries[entry].stream_entry);
5730 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5732 hr = StorageBaseImpl_CopyStream(
5733 This->scratch, This->entries[entry].stream_entry,
5734 This->transactedParent, This->entries[entry].transactedParentEntry);
5736 if (FAILED(hr))
5737 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5740 if (SUCCEEDED(hr))
5741 This->entries[entry].stream_dirty = TRUE;
5743 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5745 /* Since this entry is modified, and we aren't using its stream data, we
5746 * no longer care about the original entry. */
5747 DirRef delete_ref;
5748 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5750 if (delete_ref != DIRENTRY_NULL)
5751 This->entries[delete_ref].deleted = TRUE;
5753 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5757 return hr;
5760 /* Find the first entry in a depth-first traversal. */
5761 static DirRef TransactedSnapshotImpl_FindFirstChild(
5762 TransactedSnapshotImpl* This, DirRef parent)
5764 DirRef cursor, prev;
5765 TransactedDirEntry *entry;
5767 cursor = parent;
5768 entry = &This->entries[cursor];
5769 while (entry->read)
5771 if (entry->data.leftChild != DIRENTRY_NULL)
5773 prev = cursor;
5774 cursor = entry->data.leftChild;
5775 entry = &This->entries[cursor];
5776 entry->parent = prev;
5778 else if (entry->data.rightChild != DIRENTRY_NULL)
5780 prev = cursor;
5781 cursor = entry->data.rightChild;
5782 entry = &This->entries[cursor];
5783 entry->parent = prev;
5785 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5787 prev = cursor;
5788 cursor = entry->data.dirRootEntry;
5789 entry = &This->entries[cursor];
5790 entry->parent = prev;
5792 else
5793 break;
5796 return cursor;
5799 /* Find the next entry in a depth-first traversal. */
5800 static DirRef TransactedSnapshotImpl_FindNextChild(
5801 TransactedSnapshotImpl* This, DirRef current)
5803 DirRef parent;
5804 TransactedDirEntry *parent_entry;
5806 parent = This->entries[current].parent;
5807 parent_entry = &This->entries[parent];
5809 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5811 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5813 This->entries[parent_entry->data.rightChild].parent = parent;
5814 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5817 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5819 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5820 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5824 return parent;
5827 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5828 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5829 TransactedSnapshotImpl* This, DirRef entry)
5831 return entry != DIRENTRY_NULL &&
5832 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5835 /* Destroy the entries created by CopyTree. */
5836 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5837 TransactedSnapshotImpl* This, DirRef stop)
5839 DirRef cursor;
5840 TransactedDirEntry *entry;
5841 ULARGE_INTEGER zero;
5843 zero.QuadPart = 0;
5845 if (!This->entries[This->base.storageDirEntry].read)
5846 return;
5848 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5850 if (cursor == DIRENTRY_NULL)
5851 return;
5853 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5855 while (cursor != DIRENTRY_NULL && cursor != stop)
5857 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5859 entry = &This->entries[cursor];
5861 if (entry->stream_dirty)
5862 StorageBaseImpl_StreamSetSize(This->transactedParent,
5863 entry->newTransactedParentEntry, zero);
5865 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5866 entry->newTransactedParentEntry);
5868 entry->newTransactedParentEntry = entry->transactedParentEntry;
5871 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5875 /* Make a copy of our edited tree that we can use in the parent. */
5876 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5878 DirRef cursor;
5879 TransactedDirEntry *entry;
5880 HRESULT hr = S_OK;
5882 cursor = This->base.storageDirEntry;
5883 entry = &This->entries[cursor];
5884 entry->parent = DIRENTRY_NULL;
5885 entry->newTransactedParentEntry = entry->transactedParentEntry;
5887 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5888 return S_OK;
5890 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5892 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5893 entry = &This->entries[cursor];
5895 while (cursor != DIRENTRY_NULL)
5897 /* Make a copy of this entry in the transacted parent. */
5898 if (!entry->read ||
5899 (!entry->dirty && !entry->stream_dirty &&
5900 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5901 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5902 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5903 entry->newTransactedParentEntry = entry->transactedParentEntry;
5904 else
5906 DirEntry newData;
5908 memcpy(&newData, &entry->data, sizeof(DirEntry));
5910 newData.size.QuadPart = 0;
5911 newData.startingBlock = BLOCK_END_OF_CHAIN;
5913 if (newData.leftChild != DIRENTRY_NULL)
5914 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5916 if (newData.rightChild != DIRENTRY_NULL)
5917 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5919 if (newData.dirRootEntry != DIRENTRY_NULL)
5920 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5922 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5923 &entry->newTransactedParentEntry);
5924 if (FAILED(hr))
5926 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5927 return hr;
5930 if (entry->stream_dirty)
5932 hr = StorageBaseImpl_CopyStream(
5933 This->transactedParent, entry->newTransactedParentEntry,
5934 This->scratch, entry->stream_entry);
5936 else if (entry->data.size.QuadPart)
5938 hr = StorageBaseImpl_StreamLink(
5939 This->transactedParent, entry->newTransactedParentEntry,
5940 entry->transactedParentEntry);
5943 if (FAILED(hr))
5945 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5946 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5947 return hr;
5951 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5952 entry = &This->entries[cursor];
5955 return hr;
5958 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5959 IStorage* iface,
5960 DWORD grfCommitFlags) /* [in] */
5962 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5963 TransactedDirEntry *root_entry;
5964 DirRef i, dir_root_ref;
5965 DirEntry data;
5966 ULARGE_INTEGER zero;
5967 HRESULT hr;
5968 ULONG transactionSig;
5970 zero.QuadPart = 0;
5972 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5974 /* Cannot commit a read-only transacted storage */
5975 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5976 return STG_E_ACCESSDENIED;
5978 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5979 if (hr == E_NOTIMPL) hr = S_OK;
5980 if (SUCCEEDED(hr))
5982 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5983 if (SUCCEEDED(hr))
5985 if (transactionSig != This->lastTransactionSig)
5987 ERR("file was externally modified\n");
5988 hr = STG_E_NOTCURRENT;
5991 if (SUCCEEDED(hr))
5993 This->lastTransactionSig = transactionSig+1;
5994 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5997 else if (hr == E_NOTIMPL)
5998 hr = S_OK;
6000 if (FAILED(hr)) goto end;
6002 /* To prevent data loss, we create the new structure in the file before we
6003 * delete the old one, so that in case of errors the old data is intact. We
6004 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
6005 * needed in the rare situation where we have just enough free disk space to
6006 * overwrite the existing data. */
6008 root_entry = &This->entries[This->base.storageDirEntry];
6010 if (!root_entry->read)
6011 goto end;
6013 hr = TransactedSnapshotImpl_CopyTree(This);
6014 if (FAILED(hr)) goto end;
6016 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
6017 dir_root_ref = DIRENTRY_NULL;
6018 else
6019 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
6021 hr = StorageBaseImpl_Flush(This->transactedParent);
6023 /* Update the storage to use the new data in one step. */
6024 if (SUCCEEDED(hr))
6025 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
6026 root_entry->transactedParentEntry, &data);
6028 if (SUCCEEDED(hr))
6030 data.dirRootEntry = dir_root_ref;
6031 data.clsid = root_entry->data.clsid;
6032 data.ctime = root_entry->data.ctime;
6033 data.mtime = root_entry->data.mtime;
6035 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
6036 root_entry->transactedParentEntry, &data);
6039 /* Try to flush after updating the root storage, but if the flush fails, keep
6040 * going, on the theory that it'll either succeed later or the subsequent
6041 * writes will fail. */
6042 StorageBaseImpl_Flush(This->transactedParent);
6044 if (SUCCEEDED(hr))
6046 /* Destroy the old now-orphaned data. */
6047 for (i=0; i<This->entries_size; i++)
6049 TransactedDirEntry *entry = &This->entries[i];
6050 if (entry->inuse)
6052 if (entry->deleted)
6054 StorageBaseImpl_StreamSetSize(This->transactedParent,
6055 entry->transactedParentEntry, zero);
6056 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6057 entry->transactedParentEntry);
6058 memset(entry, 0, sizeof(TransactedDirEntry));
6059 This->firstFreeEntry = min(i, This->firstFreeEntry);
6061 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6063 if (entry->transactedParentEntry != DIRENTRY_NULL)
6064 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6065 entry->transactedParentEntry);
6066 if (entry->stream_dirty)
6068 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6069 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6070 entry->stream_dirty = FALSE;
6072 entry->dirty = FALSE;
6073 entry->transactedParentEntry = entry->newTransactedParentEntry;
6078 else
6080 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6083 if (SUCCEEDED(hr))
6084 hr = StorageBaseImpl_Flush(This->transactedParent);
6085 end:
6086 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6089 TRACE("<-- %08x\n", hr);
6090 return hr;
6093 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6094 IStorage* iface)
6096 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6097 ULARGE_INTEGER zero;
6098 ULONG i;
6100 TRACE("(%p)\n", iface);
6102 /* Destroy the open objects. */
6103 StorageBaseImpl_DeleteAll(&This->base);
6105 /* Clear out the scratch file. */
6106 zero.QuadPart = 0;
6107 for (i=0; i<This->entries_size; i++)
6109 if (This->entries[i].stream_dirty)
6111 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6112 zero);
6114 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6118 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6120 This->firstFreeEntry = 0;
6121 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6123 return S_OK;
6126 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6128 if (!This->reverted)
6130 TRACE("Storage invalidated (stg=%p)\n", This);
6132 This->reverted = TRUE;
6134 StorageBaseImpl_DeleteAll(This);
6138 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6140 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6142 IStorage_Revert(&This->base.IStorage_iface);
6143 IStorage_Release(&This->transactedParent->IStorage_iface);
6144 IStorage_Release(&This->scratch->IStorage_iface);
6145 HeapFree(GetProcessHeap(), 0, This->entries);
6146 HeapFree(GetProcessHeap(), 0, This);
6149 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6151 /* We only need to flush when committing. */
6152 return S_OK;
6155 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6157 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6159 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6162 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6163 const DirEntry *newData, DirRef *index)
6165 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6166 DirRef new_ref;
6167 TransactedDirEntry *new_entry;
6169 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6170 if (new_ref == DIRENTRY_NULL)
6171 return E_OUTOFMEMORY;
6173 new_entry = &This->entries[new_ref];
6175 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6176 new_entry->read = TRUE;
6177 new_entry->dirty = TRUE;
6178 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6180 *index = new_ref;
6182 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6184 return S_OK;
6187 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6188 DirRef index, const DirEntry *data)
6190 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6191 HRESULT hr;
6193 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6195 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6196 if (FAILED(hr))
6198 TRACE("<-- %08x\n", hr);
6199 return hr;
6202 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6204 if (index != This->base.storageDirEntry)
6206 This->entries[index].dirty = TRUE;
6208 if (data->size.QuadPart == 0 &&
6209 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6211 /* Since this entry is modified, and we aren't using its stream data, we
6212 * no longer care about the original entry. */
6213 DirRef delete_ref;
6214 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6216 if (delete_ref != DIRENTRY_NULL)
6217 This->entries[delete_ref].deleted = TRUE;
6219 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6222 TRACE("<-- S_OK\n");
6223 return S_OK;
6226 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6227 DirRef index, DirEntry *data)
6229 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6230 HRESULT hr;
6232 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6233 if (FAILED(hr))
6235 TRACE("<-- %08x\n", hr);
6236 return hr;
6239 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6241 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6243 return S_OK;
6246 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6247 DirRef index)
6249 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6251 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6252 This->entries[index].data.size.QuadPart != 0)
6254 /* If we deleted this entry while it has stream data. We must have left the
6255 * data because some other entry is using it, and we need to leave the
6256 * original entry alone. */
6257 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6258 This->firstFreeEntry = min(index, This->firstFreeEntry);
6260 else
6262 This->entries[index].deleted = TRUE;
6265 return S_OK;
6268 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6269 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6271 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6273 if (This->entries[index].stream_dirty)
6275 return StorageBaseImpl_StreamReadAt(This->scratch,
6276 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6278 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6280 /* This stream doesn't live in the parent, and we haven't allocated storage
6281 * for it yet */
6282 *bytesRead = 0;
6283 return S_OK;
6285 else
6287 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6288 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6292 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6293 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6295 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6296 HRESULT hr;
6298 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6299 if (FAILED(hr))
6301 TRACE("<-- %08x\n", hr);
6302 return hr;
6305 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6306 if (FAILED(hr))
6308 TRACE("<-- %08x\n", hr);
6309 return hr;
6312 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6313 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6315 if (SUCCEEDED(hr) && size != 0)
6316 This->entries[index].data.size.QuadPart = max(
6317 This->entries[index].data.size.QuadPart,
6318 offset.QuadPart + size);
6320 TRACE("<-- %08x\n", hr);
6321 return hr;
6324 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6325 DirRef index, ULARGE_INTEGER newsize)
6327 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6328 HRESULT hr;
6330 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6331 if (FAILED(hr))
6333 TRACE("<-- %08x\n", hr);
6334 return hr;
6337 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6338 return S_OK;
6340 if (newsize.QuadPart == 0)
6342 /* Destroy any parent references or entries in the scratch file. */
6343 if (This->entries[index].stream_dirty)
6345 ULARGE_INTEGER zero;
6346 zero.QuadPart = 0;
6347 StorageBaseImpl_StreamSetSize(This->scratch,
6348 This->entries[index].stream_entry, zero);
6349 StorageBaseImpl_DestroyDirEntry(This->scratch,
6350 This->entries[index].stream_entry);
6351 This->entries[index].stream_dirty = FALSE;
6353 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6355 DirRef delete_ref;
6356 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6358 if (delete_ref != DIRENTRY_NULL)
6359 This->entries[delete_ref].deleted = TRUE;
6361 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6364 else
6366 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6367 if (FAILED(hr)) return hr;
6369 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6370 This->entries[index].stream_entry, newsize);
6373 if (SUCCEEDED(hr))
6374 This->entries[index].data.size = newsize;
6376 TRACE("<-- %08x\n", hr);
6377 return hr;
6380 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6381 DirRef dst, DirRef src)
6383 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6384 HRESULT hr;
6385 TransactedDirEntry *dst_entry, *src_entry;
6387 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6388 if (FAILED(hr))
6390 TRACE("<-- %08x\n", hr);
6391 return hr;
6394 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6395 if (FAILED(hr))
6397 TRACE("<-- %08x\n", hr);
6398 return hr;
6401 dst_entry = &This->entries[dst];
6402 src_entry = &This->entries[src];
6404 dst_entry->stream_dirty = src_entry->stream_dirty;
6405 dst_entry->stream_entry = src_entry->stream_entry;
6406 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6407 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6408 dst_entry->data.size = src_entry->data.size;
6410 return S_OK;
6413 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6414 ULONG* result, BOOL refresh)
6416 return E_NOTIMPL;
6419 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6420 ULONG value)
6422 return E_NOTIMPL;
6425 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6427 return E_NOTIMPL;
6430 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6432 return E_NOTIMPL;
6435 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6437 StorageBaseImpl_QueryInterface,
6438 StorageBaseImpl_AddRef,
6439 StorageBaseImpl_Release,
6440 StorageBaseImpl_CreateStream,
6441 StorageBaseImpl_OpenStream,
6442 StorageBaseImpl_CreateStorage,
6443 StorageBaseImpl_OpenStorage,
6444 StorageBaseImpl_CopyTo,
6445 StorageBaseImpl_MoveElementTo,
6446 TransactedSnapshotImpl_Commit,
6447 TransactedSnapshotImpl_Revert,
6448 StorageBaseImpl_EnumElements,
6449 StorageBaseImpl_DestroyElement,
6450 StorageBaseImpl_RenameElement,
6451 StorageBaseImpl_SetElementTimes,
6452 StorageBaseImpl_SetClass,
6453 StorageBaseImpl_SetStateBits,
6454 StorageBaseImpl_Stat
6457 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6459 TransactedSnapshotImpl_Destroy,
6460 TransactedSnapshotImpl_Invalidate,
6461 TransactedSnapshotImpl_Flush,
6462 TransactedSnapshotImpl_GetFilename,
6463 TransactedSnapshotImpl_CreateDirEntry,
6464 TransactedSnapshotImpl_WriteDirEntry,
6465 TransactedSnapshotImpl_ReadDirEntry,
6466 TransactedSnapshotImpl_DestroyDirEntry,
6467 TransactedSnapshotImpl_StreamReadAt,
6468 TransactedSnapshotImpl_StreamWriteAt,
6469 TransactedSnapshotImpl_StreamSetSize,
6470 TransactedSnapshotImpl_StreamLink,
6471 TransactedSnapshotImpl_GetTransactionSig,
6472 TransactedSnapshotImpl_SetTransactionSig,
6473 TransactedSnapshotImpl_LockTransaction,
6474 TransactedSnapshotImpl_UnlockTransaction
6477 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6478 TransactedSnapshotImpl** result)
6480 HRESULT hr;
6482 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6483 if (*result)
6485 IStorage *scratch;
6487 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6489 /* This is OK because the property set storage functions use the IStorage functions. */
6490 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6491 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6493 list_init(&(*result)->base.strmHead);
6495 list_init(&(*result)->base.storageHead);
6497 (*result)->base.ref = 1;
6499 (*result)->base.openFlags = parentStorage->openFlags;
6501 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6502 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6504 /* Create a new temporary storage to act as the scratch file. */
6505 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6506 0, &scratch);
6507 (*result)->scratch = impl_from_IStorage(scratch);
6509 if (SUCCEEDED(hr))
6511 ULONG num_entries = 20;
6513 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6514 (*result)->entries_size = num_entries;
6515 (*result)->firstFreeEntry = 0;
6517 if ((*result)->entries)
6519 /* parentStorage already has 1 reference, which we take over here. */
6520 (*result)->transactedParent = parentStorage;
6522 parentStorage->transactedChild = &(*result)->base;
6524 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6526 else
6528 IStorage_Release(scratch);
6530 hr = E_OUTOFMEMORY;
6534 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6536 return hr;
6538 else
6539 return E_OUTOFMEMORY;
6543 /************************************************************************
6544 * TransactedSharedImpl implementation
6545 ***********************************************************************/
6547 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6549 if (!This->reverted)
6551 TRACE("Storage invalidated (stg=%p)\n", This);
6553 This->reverted = TRUE;
6555 StorageBaseImpl_DeleteAll(This);
6559 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6561 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6563 TransactedSharedImpl_Invalidate(&This->base);
6564 IStorage_Release(&This->transactedParent->IStorage_iface);
6565 IStorage_Release(&This->scratch->base.IStorage_iface);
6566 HeapFree(GetProcessHeap(), 0, This);
6569 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6571 /* We only need to flush when committing. */
6572 return S_OK;
6575 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6577 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6579 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6582 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6583 const DirEntry *newData, DirRef *index)
6585 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6587 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6588 newData, index);
6591 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6592 DirRef index, const DirEntry *data)
6594 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6596 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6597 index, data);
6600 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6601 DirRef index, DirEntry *data)
6603 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6605 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6606 index, data);
6609 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6610 DirRef index)
6612 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6614 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6615 index);
6618 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6619 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6621 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6623 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6624 index, offset, size, buffer, bytesRead);
6627 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6628 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6630 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6632 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6633 index, offset, size, buffer, bytesWritten);
6636 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6637 DirRef index, ULARGE_INTEGER newsize)
6639 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6641 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6642 index, newsize);
6645 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6646 DirRef dst, DirRef src)
6648 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6650 return StorageBaseImpl_StreamLink(&This->scratch->base,
6651 dst, src);
6654 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6655 ULONG* result, BOOL refresh)
6657 return E_NOTIMPL;
6660 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6661 ULONG value)
6663 return E_NOTIMPL;
6666 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6668 return E_NOTIMPL;
6671 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6673 return E_NOTIMPL;
6676 static HRESULT WINAPI TransactedSharedImpl_Commit(
6677 IStorage* iface,
6678 DWORD grfCommitFlags) /* [in] */
6680 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6681 DirRef new_storage_ref, prev_storage_ref;
6682 DirEntry src_data, dst_data;
6683 HRESULT hr;
6684 ULONG transactionSig;
6686 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6688 /* Cannot commit a read-only transacted storage */
6689 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6690 return STG_E_ACCESSDENIED;
6692 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6693 if (hr == E_NOTIMPL) hr = S_OK;
6694 if (SUCCEEDED(hr))
6696 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6697 if (SUCCEEDED(hr))
6699 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6700 hr = STG_E_NOTCURRENT;
6702 if (SUCCEEDED(hr))
6703 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6705 else if (hr == E_NOTIMPL)
6706 hr = S_OK;
6708 if (SUCCEEDED(hr))
6709 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6711 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6712 if (SUCCEEDED(hr))
6713 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6715 if (SUCCEEDED(hr))
6716 hr = StorageBaseImpl_Flush(This->transactedParent);
6718 if (SUCCEEDED(hr))
6719 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6721 if (SUCCEEDED(hr))
6723 prev_storage_ref = dst_data.dirRootEntry;
6724 dst_data.dirRootEntry = new_storage_ref;
6725 dst_data.clsid = src_data.clsid;
6726 dst_data.ctime = src_data.ctime;
6727 dst_data.mtime = src_data.mtime;
6728 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6731 if (SUCCEEDED(hr))
6733 /* Try to flush after updating the root storage, but if the flush fails, keep
6734 * going, on the theory that it'll either succeed later or the subsequent
6735 * writes will fail. */
6736 StorageBaseImpl_Flush(This->transactedParent);
6738 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6741 if (SUCCEEDED(hr))
6742 hr = StorageBaseImpl_Flush(This->transactedParent);
6744 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6746 if (SUCCEEDED(hr))
6747 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6749 if (SUCCEEDED(hr))
6751 This->lastTransactionSig = transactionSig+1;
6754 TRACE("<-- %08x\n", hr);
6755 return hr;
6758 static HRESULT WINAPI TransactedSharedImpl_Revert(
6759 IStorage* iface)
6761 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6763 TRACE("(%p)\n", iface);
6765 /* Destroy the open objects. */
6766 StorageBaseImpl_DeleteAll(&This->base);
6768 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6771 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6773 StorageBaseImpl_QueryInterface,
6774 StorageBaseImpl_AddRef,
6775 StorageBaseImpl_Release,
6776 StorageBaseImpl_CreateStream,
6777 StorageBaseImpl_OpenStream,
6778 StorageBaseImpl_CreateStorage,
6779 StorageBaseImpl_OpenStorage,
6780 StorageBaseImpl_CopyTo,
6781 StorageBaseImpl_MoveElementTo,
6782 TransactedSharedImpl_Commit,
6783 TransactedSharedImpl_Revert,
6784 StorageBaseImpl_EnumElements,
6785 StorageBaseImpl_DestroyElement,
6786 StorageBaseImpl_RenameElement,
6787 StorageBaseImpl_SetElementTimes,
6788 StorageBaseImpl_SetClass,
6789 StorageBaseImpl_SetStateBits,
6790 StorageBaseImpl_Stat
6793 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6795 TransactedSharedImpl_Destroy,
6796 TransactedSharedImpl_Invalidate,
6797 TransactedSharedImpl_Flush,
6798 TransactedSharedImpl_GetFilename,
6799 TransactedSharedImpl_CreateDirEntry,
6800 TransactedSharedImpl_WriteDirEntry,
6801 TransactedSharedImpl_ReadDirEntry,
6802 TransactedSharedImpl_DestroyDirEntry,
6803 TransactedSharedImpl_StreamReadAt,
6804 TransactedSharedImpl_StreamWriteAt,
6805 TransactedSharedImpl_StreamSetSize,
6806 TransactedSharedImpl_StreamLink,
6807 TransactedSharedImpl_GetTransactionSig,
6808 TransactedSharedImpl_SetTransactionSig,
6809 TransactedSharedImpl_LockTransaction,
6810 TransactedSharedImpl_UnlockTransaction
6813 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6814 TransactedSharedImpl** result)
6816 HRESULT hr;
6818 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6819 if (*result)
6821 IStorage *scratch;
6823 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6825 /* This is OK because the property set storage functions use the IStorage functions. */
6826 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6827 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6829 list_init(&(*result)->base.strmHead);
6831 list_init(&(*result)->base.storageHead);
6833 (*result)->base.ref = 1;
6835 (*result)->base.openFlags = parentStorage->openFlags;
6837 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6839 if (SUCCEEDED(hr))
6841 STGOPTIONS stgo;
6843 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6844 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6846 stgo.usVersion = 1;
6847 stgo.reserved = 0;
6848 stgo.ulSectorSize = 4096;
6849 stgo.pwcsTemplateFile = NULL;
6851 /* Create a new temporary storage to act as the scratch file. */
6852 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6853 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6854 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6856 if (SUCCEEDED(hr))
6858 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6859 parentStorage, parentStorage->storageDirEntry);
6861 if (SUCCEEDED(hr))
6863 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6865 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6866 (*result)->transactedParent = parentStorage;
6869 if (FAILED(hr))
6870 IStorage_Release(scratch);
6873 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6876 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6878 return hr;
6880 else
6881 return E_OUTOFMEMORY;
6884 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6885 BOOL toplevel, StorageBaseImpl** result)
6887 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6889 if (parentStorage->openFlags & fixme_flags)
6891 fixme_flags &= ~parentStorage->openFlags;
6892 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6895 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6896 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6897 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6899 /* Need to create a temp file for the snapshot */
6900 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6903 return TransactedSnapshotImpl_Construct(parentStorage,
6904 (TransactedSnapshotImpl**)result);
6907 static HRESULT Storage_Construct(
6908 HANDLE hFile,
6909 LPCOLESTR pwcsName,
6910 ILockBytes* pLkbyt,
6911 DWORD openFlags,
6912 BOOL fileBased,
6913 BOOL create,
6914 ULONG sector_size,
6915 StorageBaseImpl** result)
6917 StorageImpl *newStorage;
6918 StorageBaseImpl *newTransactedStorage;
6919 HRESULT hr;
6921 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6922 if (FAILED(hr)) goto end;
6924 if (openFlags & STGM_TRANSACTED)
6926 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6927 if (FAILED(hr))
6928 IStorage_Release(&newStorage->base.IStorage_iface);
6929 else
6930 *result = newTransactedStorage;
6932 else
6933 *result = &newStorage->base;
6935 end:
6936 return hr;
6940 /************************************************************************
6941 * StorageUtl helper functions
6942 ***********************************************************************/
6944 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6946 WORD tmp;
6948 memcpy(&tmp, buffer+offset, sizeof(WORD));
6949 *value = lendian16toh(tmp);
6952 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6954 value = htole16(value);
6955 memcpy(buffer+offset, &value, sizeof(WORD));
6958 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6960 DWORD tmp;
6962 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6963 *value = lendian32toh(tmp);
6966 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6968 value = htole32(value);
6969 memcpy(buffer+offset, &value, sizeof(DWORD));
6972 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6973 ULARGE_INTEGER* value)
6975 #ifdef WORDS_BIGENDIAN
6976 ULARGE_INTEGER tmp;
6978 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6979 value->u.LowPart = htole32(tmp.u.HighPart);
6980 value->u.HighPart = htole32(tmp.u.LowPart);
6981 #else
6982 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6983 #endif
6986 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6987 const ULARGE_INTEGER *value)
6989 #ifdef WORDS_BIGENDIAN
6990 ULARGE_INTEGER tmp;
6992 tmp.u.LowPart = htole32(value->u.HighPart);
6993 tmp.u.HighPart = htole32(value->u.LowPart);
6994 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6995 #else
6996 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6997 #endif
7000 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
7002 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
7003 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
7004 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
7006 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
7009 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
7011 StorageUtl_WriteDWord(buffer, offset, value->Data1);
7012 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
7013 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
7015 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
7018 void StorageUtl_CopyDirEntryToSTATSTG(
7019 StorageBaseImpl* storage,
7020 STATSTG* destination,
7021 const DirEntry* source,
7022 int statFlags)
7025 * The copy of the string occurs only when the flag is not set
7027 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
7029 /* Use the filename for the root storage. */
7030 destination->pwcsName = 0;
7031 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
7033 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
7034 (source->name[0] == 0) )
7036 destination->pwcsName = 0;
7038 else
7040 destination->pwcsName =
7041 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
7043 strcpyW(destination->pwcsName, source->name);
7046 switch (source->stgType)
7048 case STGTY_STORAGE:
7049 case STGTY_ROOT:
7050 destination->type = STGTY_STORAGE;
7051 break;
7052 case STGTY_STREAM:
7053 destination->type = STGTY_STREAM;
7054 break;
7055 default:
7056 destination->type = STGTY_STREAM;
7057 break;
7060 destination->cbSize = source->size;
7062 currentReturnStruct->mtime = {0}; TODO
7063 currentReturnStruct->ctime = {0};
7064 currentReturnStruct->atime = {0};
7066 destination->grfMode = 0;
7067 destination->grfLocksSupported = 0;
7068 destination->clsid = source->clsid;
7069 destination->grfStateBits = 0;
7070 destination->reserved = 0;
7074 /************************************************************************
7075 * BlockChainStream implementation
7076 ***********************************************************************/
7078 /******************************************************************************
7079 * BlockChainStream_GetHeadOfChain
7081 * Returns the head of this stream chain.
7082 * Some special chains don't have directory entries, their heads are kept in
7083 * This->headOfStreamPlaceHolder.
7086 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7088 DirEntry chainEntry;
7089 HRESULT hr;
7091 if (This->headOfStreamPlaceHolder != 0)
7092 return *(This->headOfStreamPlaceHolder);
7094 if (This->ownerDirEntry != DIRENTRY_NULL)
7096 hr = StorageImpl_ReadDirEntry(
7097 This->parentStorage,
7098 This->ownerDirEntry,
7099 &chainEntry);
7101 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7102 return chainEntry.startingBlock;
7105 return BLOCK_END_OF_CHAIN;
7108 /* Read and save the index of all blocks in this stream. */
7109 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7111 ULONG next_sector, next_offset;
7112 HRESULT hr;
7113 struct BlockChainRun *last_run;
7115 if (This->indexCacheLen == 0)
7117 last_run = NULL;
7118 next_offset = 0;
7119 next_sector = BlockChainStream_GetHeadOfChain(This);
7121 else
7123 last_run = &This->indexCache[This->indexCacheLen-1];
7124 next_offset = last_run->lastOffset+1;
7125 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7126 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7127 &next_sector);
7128 if (FAILED(hr)) return hr;
7131 while (next_sector != BLOCK_END_OF_CHAIN)
7133 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7135 /* Add the current block to the cache. */
7136 if (This->indexCacheSize == 0)
7138 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7139 if (!This->indexCache) return E_OUTOFMEMORY;
7140 This->indexCacheSize = 16;
7142 else if (This->indexCacheSize == This->indexCacheLen)
7144 struct BlockChainRun *new_cache;
7145 ULONG new_size;
7147 new_size = This->indexCacheSize * 2;
7148 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7149 if (!new_cache) return E_OUTOFMEMORY;
7150 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7152 HeapFree(GetProcessHeap(), 0, This->indexCache);
7153 This->indexCache = new_cache;
7154 This->indexCacheSize = new_size;
7157 This->indexCacheLen++;
7158 last_run = &This->indexCache[This->indexCacheLen-1];
7159 last_run->firstSector = next_sector;
7160 last_run->firstOffset = next_offset;
7163 last_run->lastOffset = next_offset;
7165 /* Find the next block. */
7166 next_offset++;
7167 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7168 if (FAILED(hr)) return hr;
7171 if (This->indexCacheLen)
7173 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7174 This->numBlocks = last_run->lastOffset+1;
7176 else
7178 This->tailIndex = BLOCK_END_OF_CHAIN;
7179 This->numBlocks = 0;
7182 return S_OK;
7185 /* Locate the nth block in this stream. */
7186 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7188 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7189 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7191 if (offset >= This->numBlocks)
7192 return BLOCK_END_OF_CHAIN;
7194 while (min_run < max_run)
7196 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7197 if (offset < This->indexCache[run_to_check].firstOffset)
7199 max_offset = This->indexCache[run_to_check].firstOffset-1;
7200 max_run = run_to_check-1;
7202 else if (offset > This->indexCache[run_to_check].lastOffset)
7204 min_offset = This->indexCache[run_to_check].lastOffset+1;
7205 min_run = run_to_check+1;
7207 else
7208 /* Block is in this run. */
7209 min_run = max_run = run_to_check;
7212 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7215 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7216 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7218 BlockChainBlock *result=NULL;
7219 int i;
7221 for (i=0; i<2; i++)
7222 if (This->cachedBlocks[i].index == index)
7224 *sector = This->cachedBlocks[i].sector;
7225 *block = &This->cachedBlocks[i];
7226 return S_OK;
7229 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7230 if (*sector == BLOCK_END_OF_CHAIN)
7231 return STG_E_DOCFILECORRUPT;
7233 if (create)
7235 if (This->cachedBlocks[0].index == 0xffffffff)
7236 result = &This->cachedBlocks[0];
7237 else if (This->cachedBlocks[1].index == 0xffffffff)
7238 result = &This->cachedBlocks[1];
7239 else
7241 result = &This->cachedBlocks[This->blockToEvict++];
7242 if (This->blockToEvict == 2)
7243 This->blockToEvict = 0;
7246 if (result->dirty)
7248 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7249 return STG_E_WRITEFAULT;
7250 result->dirty = FALSE;
7253 result->read = FALSE;
7254 result->index = index;
7255 result->sector = *sector;
7258 *block = result;
7259 return S_OK;
7262 BlockChainStream* BlockChainStream_Construct(
7263 StorageImpl* parentStorage,
7264 ULONG* headOfStreamPlaceHolder,
7265 DirRef dirEntry)
7267 BlockChainStream* newStream;
7269 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7271 newStream->parentStorage = parentStorage;
7272 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7273 newStream->ownerDirEntry = dirEntry;
7274 newStream->indexCache = NULL;
7275 newStream->indexCacheLen = 0;
7276 newStream->indexCacheSize = 0;
7277 newStream->cachedBlocks[0].index = 0xffffffff;
7278 newStream->cachedBlocks[0].dirty = FALSE;
7279 newStream->cachedBlocks[1].index = 0xffffffff;
7280 newStream->cachedBlocks[1].dirty = FALSE;
7281 newStream->blockToEvict = 0;
7283 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7285 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7286 HeapFree(GetProcessHeap(), 0, newStream);
7287 return NULL;
7290 return newStream;
7293 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7295 int i;
7296 if (!This) return S_OK;
7297 for (i=0; i<2; i++)
7299 if (This->cachedBlocks[i].dirty)
7301 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7302 This->cachedBlocks[i].dirty = FALSE;
7303 else
7304 return STG_E_WRITEFAULT;
7307 return S_OK;
7310 void BlockChainStream_Destroy(BlockChainStream* This)
7312 if (This)
7314 BlockChainStream_Flush(This);
7315 HeapFree(GetProcessHeap(), 0, This->indexCache);
7317 HeapFree(GetProcessHeap(), 0, This);
7320 /******************************************************************************
7321 * BlockChainStream_Shrink
7323 * Shrinks this chain in the big block depot.
7325 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7326 ULARGE_INTEGER newSize)
7328 ULONG blockIndex;
7329 ULONG numBlocks;
7330 int i;
7333 * Figure out how many blocks are needed to contain the new size
7335 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7337 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7338 numBlocks++;
7340 if (numBlocks)
7343 * Go to the new end of chain
7345 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7347 /* Mark the new end of chain */
7348 StorageImpl_SetNextBlockInChain(
7349 This->parentStorage,
7350 blockIndex,
7351 BLOCK_END_OF_CHAIN);
7353 This->tailIndex = blockIndex;
7355 else
7357 if (This->headOfStreamPlaceHolder != 0)
7359 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7361 else
7363 DirEntry chainEntry;
7364 assert(This->ownerDirEntry != DIRENTRY_NULL);
7366 StorageImpl_ReadDirEntry(
7367 This->parentStorage,
7368 This->ownerDirEntry,
7369 &chainEntry);
7371 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7373 StorageImpl_WriteDirEntry(
7374 This->parentStorage,
7375 This->ownerDirEntry,
7376 &chainEntry);
7379 This->tailIndex = BLOCK_END_OF_CHAIN;
7382 This->numBlocks = numBlocks;
7385 * Mark the extra blocks as free
7387 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7389 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7390 StorageImpl_FreeBigBlock(This->parentStorage,
7391 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7392 if (last_run->lastOffset == last_run->firstOffset)
7393 This->indexCacheLen--;
7394 else
7395 last_run->lastOffset--;
7399 * Reset the last accessed block cache.
7401 for (i=0; i<2; i++)
7403 if (This->cachedBlocks[i].index >= numBlocks)
7405 This->cachedBlocks[i].index = 0xffffffff;
7406 This->cachedBlocks[i].dirty = FALSE;
7410 return TRUE;
7413 /******************************************************************************
7414 * BlockChainStream_Enlarge
7416 * Grows this chain in the big block depot.
7418 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7419 ULARGE_INTEGER newSize)
7421 ULONG blockIndex, currentBlock;
7422 ULONG newNumBlocks;
7423 ULONG oldNumBlocks = 0;
7425 blockIndex = BlockChainStream_GetHeadOfChain(This);
7428 * Empty chain. Create the head.
7430 if (blockIndex == BLOCK_END_OF_CHAIN)
7432 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7433 StorageImpl_SetNextBlockInChain(This->parentStorage,
7434 blockIndex,
7435 BLOCK_END_OF_CHAIN);
7437 if (This->headOfStreamPlaceHolder != 0)
7439 *(This->headOfStreamPlaceHolder) = blockIndex;
7441 else
7443 DirEntry chainEntry;
7444 assert(This->ownerDirEntry != DIRENTRY_NULL);
7446 StorageImpl_ReadDirEntry(
7447 This->parentStorage,
7448 This->ownerDirEntry,
7449 &chainEntry);
7451 chainEntry.startingBlock = blockIndex;
7453 StorageImpl_WriteDirEntry(
7454 This->parentStorage,
7455 This->ownerDirEntry,
7456 &chainEntry);
7459 This->tailIndex = blockIndex;
7460 This->numBlocks = 1;
7464 * Figure out how many blocks are needed to contain this stream
7466 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7468 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7469 newNumBlocks++;
7472 * Go to the current end of chain
7474 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7476 currentBlock = blockIndex;
7478 while (blockIndex != BLOCK_END_OF_CHAIN)
7480 This->numBlocks++;
7481 currentBlock = blockIndex;
7483 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7484 &blockIndex)))
7485 return FALSE;
7488 This->tailIndex = currentBlock;
7491 currentBlock = This->tailIndex;
7492 oldNumBlocks = This->numBlocks;
7495 * Add new blocks to the chain
7497 if (oldNumBlocks < newNumBlocks)
7499 while (oldNumBlocks < newNumBlocks)
7501 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7503 StorageImpl_SetNextBlockInChain(
7504 This->parentStorage,
7505 currentBlock,
7506 blockIndex);
7508 StorageImpl_SetNextBlockInChain(
7509 This->parentStorage,
7510 blockIndex,
7511 BLOCK_END_OF_CHAIN);
7513 currentBlock = blockIndex;
7514 oldNumBlocks++;
7517 This->tailIndex = blockIndex;
7518 This->numBlocks = newNumBlocks;
7521 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7522 return FALSE;
7524 return TRUE;
7528 /******************************************************************************
7529 * BlockChainStream_GetSize
7531 * Returns the size of this chain.
7532 * Will return the block count if this chain doesn't have a directory entry.
7534 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7536 DirEntry chainEntry;
7538 if(This->headOfStreamPlaceHolder == NULL)
7541 * This chain has a directory entry so use the size value from there.
7543 StorageImpl_ReadDirEntry(
7544 This->parentStorage,
7545 This->ownerDirEntry,
7546 &chainEntry);
7548 return chainEntry.size;
7550 else
7553 * this chain is a chain that does not have a directory entry, figure out the
7554 * size by making the product number of used blocks times the
7555 * size of them
7557 ULARGE_INTEGER result;
7558 result.QuadPart =
7559 (ULONGLONG)BlockChainStream_GetCount(This) *
7560 This->parentStorage->bigBlockSize;
7562 return result;
7566 /******************************************************************************
7567 * BlockChainStream_SetSize
7569 * Sets the size of this stream. The big block depot will be updated.
7570 * The file will grow if we grow the chain.
7572 * TODO: Free the actual blocks in the file when we shrink the chain.
7573 * Currently, the blocks are still in the file. So the file size
7574 * doesn't shrink even if we shrink streams.
7576 BOOL BlockChainStream_SetSize(
7577 BlockChainStream* This,
7578 ULARGE_INTEGER newSize)
7580 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7582 if (newSize.QuadPart == size.QuadPart)
7583 return TRUE;
7585 if (newSize.QuadPart < size.QuadPart)
7587 BlockChainStream_Shrink(This, newSize);
7589 else
7591 BlockChainStream_Enlarge(This, newSize);
7594 return TRUE;
7597 /******************************************************************************
7598 * BlockChainStream_ReadAt
7600 * Reads a specified number of bytes from this chain at the specified offset.
7601 * bytesRead may be NULL.
7602 * Failure will be returned if the specified number of bytes has not been read.
7604 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7605 ULARGE_INTEGER offset,
7606 ULONG size,
7607 void* buffer,
7608 ULONG* bytesRead)
7610 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7611 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7612 ULONG bytesToReadInBuffer;
7613 ULONG blockIndex;
7614 BYTE* bufferWalker;
7615 ULARGE_INTEGER stream_size;
7616 HRESULT hr;
7617 BlockChainBlock *cachedBlock;
7619 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7622 * Find the first block in the stream that contains part of the buffer.
7624 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7626 *bytesRead = 0;
7628 stream_size = BlockChainStream_GetSize(This);
7629 if (stream_size.QuadPart > offset.QuadPart)
7630 size = min(stream_size.QuadPart - offset.QuadPart, size);
7631 else
7632 return S_OK;
7635 * Start reading the buffer.
7637 bufferWalker = buffer;
7639 while (size > 0)
7641 ULARGE_INTEGER ulOffset;
7642 DWORD bytesReadAt;
7645 * Calculate how many bytes we can copy from this big block.
7647 bytesToReadInBuffer =
7648 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7650 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7652 if (FAILED(hr))
7653 return hr;
7655 if (!cachedBlock)
7657 /* Not in cache, and we're going to read past the end of the block. */
7658 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7659 offsetInBlock;
7661 StorageImpl_ReadAt(This->parentStorage,
7662 ulOffset,
7663 bufferWalker,
7664 bytesToReadInBuffer,
7665 &bytesReadAt);
7667 else
7669 if (!cachedBlock->read)
7671 ULONG read;
7672 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7673 return STG_E_READFAULT;
7675 cachedBlock->read = TRUE;
7678 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7679 bytesReadAt = bytesToReadInBuffer;
7682 blockNoInSequence++;
7683 bufferWalker += bytesReadAt;
7684 size -= bytesReadAt;
7685 *bytesRead += bytesReadAt;
7686 offsetInBlock = 0; /* There is no offset on the next block */
7688 if (bytesToReadInBuffer != bytesReadAt)
7689 break;
7692 return S_OK;
7695 /******************************************************************************
7696 * BlockChainStream_WriteAt
7698 * Writes the specified number of bytes to this chain at the specified offset.
7699 * Will fail if not all specified number of bytes have been written.
7701 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7702 ULARGE_INTEGER offset,
7703 ULONG size,
7704 const void* buffer,
7705 ULONG* bytesWritten)
7707 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7708 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7709 ULONG bytesToWrite;
7710 ULONG blockIndex;
7711 const BYTE* bufferWalker;
7712 HRESULT hr;
7713 BlockChainBlock *cachedBlock;
7715 *bytesWritten = 0;
7716 bufferWalker = buffer;
7718 while (size > 0)
7720 ULARGE_INTEGER ulOffset;
7721 DWORD bytesWrittenAt;
7724 * Calculate how many bytes we can copy to this big block.
7726 bytesToWrite =
7727 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7729 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7731 /* BlockChainStream_SetSize should have already been called to ensure we have
7732 * enough blocks in the chain to write into */
7733 if (FAILED(hr))
7735 ERR("not enough blocks in chain to write data\n");
7736 return hr;
7739 if (!cachedBlock)
7741 /* Not in cache, and we're going to write past the end of the block. */
7742 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7743 offsetInBlock;
7745 StorageImpl_WriteAt(This->parentStorage,
7746 ulOffset,
7747 bufferWalker,
7748 bytesToWrite,
7749 &bytesWrittenAt);
7751 else
7753 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7755 ULONG read;
7756 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7757 return STG_E_READFAULT;
7760 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7761 bytesWrittenAt = bytesToWrite;
7762 cachedBlock->read = TRUE;
7763 cachedBlock->dirty = TRUE;
7766 blockNoInSequence++;
7767 bufferWalker += bytesWrittenAt;
7768 size -= bytesWrittenAt;
7769 *bytesWritten += bytesWrittenAt;
7770 offsetInBlock = 0; /* There is no offset on the next block */
7772 if (bytesWrittenAt != bytesToWrite)
7773 break;
7776 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7780 /************************************************************************
7781 * SmallBlockChainStream implementation
7782 ***********************************************************************/
7784 SmallBlockChainStream* SmallBlockChainStream_Construct(
7785 StorageImpl* parentStorage,
7786 ULONG* headOfStreamPlaceHolder,
7787 DirRef dirEntry)
7789 SmallBlockChainStream* newStream;
7791 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7793 newStream->parentStorage = parentStorage;
7794 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7795 newStream->ownerDirEntry = dirEntry;
7797 return newStream;
7800 void SmallBlockChainStream_Destroy(
7801 SmallBlockChainStream* This)
7803 HeapFree(GetProcessHeap(), 0, This);
7806 /******************************************************************************
7807 * SmallBlockChainStream_GetHeadOfChain
7809 * Returns the head of this chain of small blocks.
7811 static ULONG SmallBlockChainStream_GetHeadOfChain(
7812 SmallBlockChainStream* This)
7814 DirEntry chainEntry;
7815 HRESULT hr;
7817 if (This->headOfStreamPlaceHolder != NULL)
7818 return *(This->headOfStreamPlaceHolder);
7820 if (This->ownerDirEntry)
7822 hr = StorageImpl_ReadDirEntry(
7823 This->parentStorage,
7824 This->ownerDirEntry,
7825 &chainEntry);
7827 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7828 return chainEntry.startingBlock;
7831 return BLOCK_END_OF_CHAIN;
7834 /******************************************************************************
7835 * SmallBlockChainStream_GetNextBlockInChain
7837 * Returns the index of the next small block in this chain.
7839 * Return Values:
7840 * - BLOCK_END_OF_CHAIN: end of this chain
7841 * - BLOCK_UNUSED: small block 'blockIndex' is free
7843 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7844 SmallBlockChainStream* This,
7845 ULONG blockIndex,
7846 ULONG* nextBlockInChain)
7848 ULARGE_INTEGER offsetOfBlockInDepot;
7849 DWORD buffer;
7850 ULONG bytesRead;
7851 HRESULT res;
7853 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7855 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7858 * Read those bytes in the buffer from the small block file.
7860 res = BlockChainStream_ReadAt(
7861 This->parentStorage->smallBlockDepotChain,
7862 offsetOfBlockInDepot,
7863 sizeof(DWORD),
7864 &buffer,
7865 &bytesRead);
7867 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7868 res = STG_E_READFAULT;
7870 if (SUCCEEDED(res))
7872 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7873 return S_OK;
7876 return res;
7879 /******************************************************************************
7880 * SmallBlockChainStream_SetNextBlockInChain
7882 * Writes the index of the next block of the specified block in the small
7883 * block depot.
7884 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7885 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7887 static void SmallBlockChainStream_SetNextBlockInChain(
7888 SmallBlockChainStream* This,
7889 ULONG blockIndex,
7890 ULONG nextBlock)
7892 ULARGE_INTEGER offsetOfBlockInDepot;
7893 DWORD buffer;
7894 ULONG bytesWritten;
7896 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7898 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7901 * Read those bytes in the buffer from the small block file.
7903 BlockChainStream_WriteAt(
7904 This->parentStorage->smallBlockDepotChain,
7905 offsetOfBlockInDepot,
7906 sizeof(DWORD),
7907 &buffer,
7908 &bytesWritten);
7911 /******************************************************************************
7912 * SmallBlockChainStream_FreeBlock
7914 * Flag small block 'blockIndex' as free in the small block depot.
7916 static void SmallBlockChainStream_FreeBlock(
7917 SmallBlockChainStream* This,
7918 ULONG blockIndex)
7920 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7923 /******************************************************************************
7924 * SmallBlockChainStream_GetNextFreeBlock
7926 * Returns the index of a free small block. The small block depot will be
7927 * enlarged if necessary. The small block chain will also be enlarged if
7928 * necessary.
7930 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7931 SmallBlockChainStream* This)
7933 ULARGE_INTEGER offsetOfBlockInDepot;
7934 DWORD buffer;
7935 ULONG bytesRead;
7936 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7937 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7938 HRESULT res = S_OK;
7939 ULONG smallBlocksPerBigBlock;
7940 DirEntry rootEntry;
7941 ULONG blocksRequired;
7942 ULARGE_INTEGER old_size, size_required;
7944 offsetOfBlockInDepot.u.HighPart = 0;
7947 * Scan the small block depot for a free block
7949 while (nextBlockIndex != BLOCK_UNUSED)
7951 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7953 res = BlockChainStream_ReadAt(
7954 This->parentStorage->smallBlockDepotChain,
7955 offsetOfBlockInDepot,
7956 sizeof(DWORD),
7957 &buffer,
7958 &bytesRead);
7961 * If we run out of space for the small block depot, enlarge it
7963 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7965 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7967 if (nextBlockIndex != BLOCK_UNUSED)
7968 blockIndex++;
7970 else
7972 ULONG count =
7973 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7975 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7976 ULARGE_INTEGER newSize, offset;
7977 ULONG bytesWritten;
7979 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7980 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7983 * Initialize all the small blocks to free
7985 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7986 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7987 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7988 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7990 StorageImpl_SaveFileHeader(This->parentStorage);
7994 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7996 smallBlocksPerBigBlock =
7997 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
8000 * Verify if we have to allocate big blocks to contain small blocks
8002 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
8004 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
8006 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
8008 if (size_required.QuadPart > old_size.QuadPart)
8010 BlockChainStream_SetSize(
8011 This->parentStorage->smallBlockRootChain,
8012 size_required);
8014 StorageImpl_ReadDirEntry(
8015 This->parentStorage,
8016 This->parentStorage->base.storageDirEntry,
8017 &rootEntry);
8019 rootEntry.size = size_required;
8021 StorageImpl_WriteDirEntry(
8022 This->parentStorage,
8023 This->parentStorage->base.storageDirEntry,
8024 &rootEntry);
8027 return blockIndex;
8030 /******************************************************************************
8031 * SmallBlockChainStream_ReadAt
8033 * Reads a specified number of bytes from this chain at the specified offset.
8034 * bytesRead may be NULL.
8035 * Failure will be returned if the specified number of bytes has not been read.
8037 HRESULT SmallBlockChainStream_ReadAt(
8038 SmallBlockChainStream* This,
8039 ULARGE_INTEGER offset,
8040 ULONG size,
8041 void* buffer,
8042 ULONG* bytesRead)
8044 HRESULT rc = S_OK;
8045 ULARGE_INTEGER offsetInBigBlockFile;
8046 ULONG blockNoInSequence =
8047 offset.u.LowPart / This->parentStorage->smallBlockSize;
8049 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8050 ULONG bytesToReadInBuffer;
8051 ULONG blockIndex;
8052 ULONG bytesReadFromBigBlockFile;
8053 BYTE* bufferWalker;
8054 ULARGE_INTEGER stream_size;
8057 * This should never happen on a small block file.
8059 assert(offset.u.HighPart==0);
8061 *bytesRead = 0;
8063 stream_size = SmallBlockChainStream_GetSize(This);
8064 if (stream_size.QuadPart > offset.QuadPart)
8065 size = min(stream_size.QuadPart - offset.QuadPart, size);
8066 else
8067 return S_OK;
8070 * Find the first block in the stream that contains part of the buffer.
8072 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8074 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8076 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8077 if(FAILED(rc))
8078 return rc;
8079 blockNoInSequence--;
8083 * Start reading the buffer.
8085 bufferWalker = buffer;
8087 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8090 * Calculate how many bytes we can copy from this small block.
8092 bytesToReadInBuffer =
8093 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8096 * Calculate the offset of the small block in the small block file.
8098 offsetInBigBlockFile.QuadPart =
8099 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8101 offsetInBigBlockFile.QuadPart += offsetInBlock;
8104 * Read those bytes in the buffer from the small block file.
8105 * The small block has already been identified so it shouldn't fail
8106 * unless the file is corrupt.
8108 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8109 offsetInBigBlockFile,
8110 bytesToReadInBuffer,
8111 bufferWalker,
8112 &bytesReadFromBigBlockFile);
8114 if (FAILED(rc))
8115 return rc;
8117 if (!bytesReadFromBigBlockFile)
8118 return STG_E_DOCFILECORRUPT;
8121 * Step to the next big block.
8123 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8124 if(FAILED(rc))
8125 return STG_E_DOCFILECORRUPT;
8127 bufferWalker += bytesReadFromBigBlockFile;
8128 size -= bytesReadFromBigBlockFile;
8129 *bytesRead += bytesReadFromBigBlockFile;
8130 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8133 return S_OK;
8136 /******************************************************************************
8137 * SmallBlockChainStream_WriteAt
8139 * Writes the specified number of bytes to this chain at the specified offset.
8140 * Will fail if not all specified number of bytes have been written.
8142 HRESULT SmallBlockChainStream_WriteAt(
8143 SmallBlockChainStream* This,
8144 ULARGE_INTEGER offset,
8145 ULONG size,
8146 const void* buffer,
8147 ULONG* bytesWritten)
8149 ULARGE_INTEGER offsetInBigBlockFile;
8150 ULONG blockNoInSequence =
8151 offset.u.LowPart / This->parentStorage->smallBlockSize;
8153 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8154 ULONG bytesToWriteInBuffer;
8155 ULONG blockIndex;
8156 ULONG bytesWrittenToBigBlockFile;
8157 const BYTE* bufferWalker;
8158 HRESULT res;
8161 * This should never happen on a small block file.
8163 assert(offset.u.HighPart==0);
8166 * Find the first block in the stream that contains part of the buffer.
8168 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8170 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8172 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8173 return STG_E_DOCFILECORRUPT;
8174 blockNoInSequence--;
8178 * Start writing the buffer.
8180 *bytesWritten = 0;
8181 bufferWalker = buffer;
8182 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8185 * Calculate how many bytes we can copy to this small block.
8187 bytesToWriteInBuffer =
8188 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8191 * Calculate the offset of the small block in the small block file.
8193 offsetInBigBlockFile.QuadPart =
8194 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8196 offsetInBigBlockFile.QuadPart += offsetInBlock;
8199 * Write those bytes in the buffer to the small block file.
8201 res = BlockChainStream_WriteAt(
8202 This->parentStorage->smallBlockRootChain,
8203 offsetInBigBlockFile,
8204 bytesToWriteInBuffer,
8205 bufferWalker,
8206 &bytesWrittenToBigBlockFile);
8207 if (FAILED(res))
8208 return res;
8211 * Step to the next big block.
8213 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8214 if (FAILED(res))
8215 return res;
8216 bufferWalker += bytesWrittenToBigBlockFile;
8217 size -= bytesWrittenToBigBlockFile;
8218 *bytesWritten += bytesWrittenToBigBlockFile;
8219 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8222 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8225 /******************************************************************************
8226 * SmallBlockChainStream_Shrink
8228 * Shrinks this chain in the small block depot.
8230 static BOOL SmallBlockChainStream_Shrink(
8231 SmallBlockChainStream* This,
8232 ULARGE_INTEGER newSize)
8234 ULONG blockIndex, extraBlock;
8235 ULONG numBlocks;
8236 ULONG count = 0;
8238 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8240 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8241 numBlocks++;
8243 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8246 * Go to the new end of chain
8248 while (count < numBlocks)
8250 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8251 &blockIndex)))
8252 return FALSE;
8253 count++;
8257 * If the count is 0, we have a special case, the head of the chain was
8258 * just freed.
8260 if (count == 0)
8262 DirEntry chainEntry;
8264 StorageImpl_ReadDirEntry(This->parentStorage,
8265 This->ownerDirEntry,
8266 &chainEntry);
8268 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8270 StorageImpl_WriteDirEntry(This->parentStorage,
8271 This->ownerDirEntry,
8272 &chainEntry);
8275 * We start freeing the chain at the head block.
8277 extraBlock = blockIndex;
8279 else
8281 /* Get the next block before marking the new end */
8282 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8283 &extraBlock)))
8284 return FALSE;
8286 /* Mark the new end of chain */
8287 SmallBlockChainStream_SetNextBlockInChain(
8288 This,
8289 blockIndex,
8290 BLOCK_END_OF_CHAIN);
8294 * Mark the extra blocks as free
8296 while (extraBlock != BLOCK_END_OF_CHAIN)
8298 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8299 &blockIndex)))
8300 return FALSE;
8301 SmallBlockChainStream_FreeBlock(This, extraBlock);
8302 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8303 extraBlock = blockIndex;
8306 return TRUE;
8309 /******************************************************************************
8310 * SmallBlockChainStream_Enlarge
8312 * Grows this chain in the small block depot.
8314 static BOOL SmallBlockChainStream_Enlarge(
8315 SmallBlockChainStream* This,
8316 ULARGE_INTEGER newSize)
8318 ULONG blockIndex, currentBlock;
8319 ULONG newNumBlocks;
8320 ULONG oldNumBlocks = 0;
8322 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8325 * Empty chain. Create the head.
8327 if (blockIndex == BLOCK_END_OF_CHAIN)
8329 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8330 SmallBlockChainStream_SetNextBlockInChain(
8331 This,
8332 blockIndex,
8333 BLOCK_END_OF_CHAIN);
8335 if (This->headOfStreamPlaceHolder != NULL)
8337 *(This->headOfStreamPlaceHolder) = blockIndex;
8339 else
8341 DirEntry chainEntry;
8343 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8344 &chainEntry);
8346 chainEntry.startingBlock = blockIndex;
8348 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8349 &chainEntry);
8353 currentBlock = blockIndex;
8356 * Figure out how many blocks are needed to contain this stream
8358 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8360 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8361 newNumBlocks++;
8364 * Go to the current end of chain
8366 while (blockIndex != BLOCK_END_OF_CHAIN)
8368 oldNumBlocks++;
8369 currentBlock = blockIndex;
8370 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8371 return FALSE;
8375 * Add new blocks to the chain
8377 while (oldNumBlocks < newNumBlocks)
8379 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8380 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8382 SmallBlockChainStream_SetNextBlockInChain(
8383 This,
8384 blockIndex,
8385 BLOCK_END_OF_CHAIN);
8387 currentBlock = blockIndex;
8388 oldNumBlocks++;
8391 return TRUE;
8394 /******************************************************************************
8395 * SmallBlockChainStream_SetSize
8397 * Sets the size of this stream.
8398 * The file will grow if we grow the chain.
8400 * TODO: Free the actual blocks in the file when we shrink the chain.
8401 * Currently, the blocks are still in the file. So the file size
8402 * doesn't shrink even if we shrink streams.
8404 BOOL SmallBlockChainStream_SetSize(
8405 SmallBlockChainStream* This,
8406 ULARGE_INTEGER newSize)
8408 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8410 if (newSize.u.LowPart == size.u.LowPart)
8411 return TRUE;
8413 if (newSize.u.LowPart < size.u.LowPart)
8415 SmallBlockChainStream_Shrink(This, newSize);
8417 else
8419 SmallBlockChainStream_Enlarge(This, newSize);
8422 return TRUE;
8425 /******************************************************************************
8426 * SmallBlockChainStream_GetCount
8428 * Returns the number of small blocks that comprises this chain.
8429 * This is not the size of the stream as the last block may not be full!
8432 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8434 ULONG blockIndex;
8435 ULONG count = 0;
8437 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8439 while(blockIndex != BLOCK_END_OF_CHAIN)
8441 count++;
8443 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8444 blockIndex, &blockIndex)))
8445 return 0;
8448 return count;
8451 /******************************************************************************
8452 * SmallBlockChainStream_GetSize
8454 * Returns the size of this chain.
8456 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8458 DirEntry chainEntry;
8460 if(This->headOfStreamPlaceHolder != NULL)
8462 ULARGE_INTEGER result;
8463 result.u.HighPart = 0;
8465 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8466 This->parentStorage->smallBlockSize;
8468 return result;
8471 StorageImpl_ReadDirEntry(
8472 This->parentStorage,
8473 This->ownerDirEntry,
8474 &chainEntry);
8476 return chainEntry.size;
8480 /************************************************************************
8481 * Miscellaneous storage functions
8482 ***********************************************************************/
8484 static HRESULT create_storagefile(
8485 LPCOLESTR pwcsName,
8486 DWORD grfMode,
8487 DWORD grfAttrs,
8488 STGOPTIONS* pStgOptions,
8489 REFIID riid,
8490 void** ppstgOpen)
8492 StorageBaseImpl* newStorage = 0;
8493 HANDLE hFile = INVALID_HANDLE_VALUE;
8494 HRESULT hr = STG_E_INVALIDFLAG;
8495 DWORD shareMode;
8496 DWORD accessMode;
8497 DWORD creationMode;
8498 DWORD fileAttributes;
8499 WCHAR tempFileName[MAX_PATH];
8501 if (ppstgOpen == 0)
8502 return STG_E_INVALIDPOINTER;
8504 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8505 return STG_E_INVALIDPARAMETER;
8507 /* if no share mode given then DENY_NONE is the default */
8508 if (STGM_SHARE_MODE(grfMode) == 0)
8509 grfMode |= STGM_SHARE_DENY_NONE;
8511 if ( FAILED( validateSTGM(grfMode) ))
8512 goto end;
8514 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8515 switch(STGM_ACCESS_MODE(grfMode))
8517 case STGM_WRITE:
8518 case STGM_READWRITE:
8519 break;
8520 default:
8521 goto end;
8524 /* in direct mode, can only use SHARE_EXCLUSIVE */
8525 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8526 goto end;
8528 /* but in transacted mode, any share mode is valid */
8531 * Generate a unique name.
8533 if (pwcsName == 0)
8535 WCHAR tempPath[MAX_PATH];
8536 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8538 memset(tempPath, 0, sizeof(tempPath));
8539 memset(tempFileName, 0, sizeof(tempFileName));
8541 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8542 tempPath[0] = '.';
8544 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8545 pwcsName = tempFileName;
8546 else
8548 hr = STG_E_INSUFFICIENTMEMORY;
8549 goto end;
8552 creationMode = TRUNCATE_EXISTING;
8554 else
8556 creationMode = GetCreationModeFromSTGM(grfMode);
8560 * Interpret the STGM value grfMode
8562 shareMode = GetShareModeFromSTGM(grfMode);
8563 accessMode = GetAccessModeFromSTGM(grfMode);
8565 if (grfMode & STGM_DELETEONRELEASE)
8566 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8567 else
8568 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8570 *ppstgOpen = 0;
8572 hFile = CreateFileW(pwcsName,
8573 accessMode,
8574 shareMode,
8575 NULL,
8576 creationMode,
8577 fileAttributes,
8580 if (hFile == INVALID_HANDLE_VALUE)
8582 if(GetLastError() == ERROR_FILE_EXISTS)
8583 hr = STG_E_FILEALREADYEXISTS;
8584 else
8585 hr = E_FAIL;
8586 goto end;
8590 * Allocate and initialize the new IStorage object.
8592 hr = Storage_Construct(
8593 hFile,
8594 pwcsName,
8595 NULL,
8596 grfMode,
8597 TRUE,
8598 TRUE,
8599 pStgOptions->ulSectorSize,
8600 &newStorage);
8602 if (FAILED(hr))
8604 goto end;
8607 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8608 IStorage_Release(&newStorage->IStorage_iface);
8610 end:
8611 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8613 return hr;
8616 /******************************************************************************
8617 * StgCreateDocfile [OLE32.@]
8618 * Creates a new compound file storage object
8620 * PARAMS
8621 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8622 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8623 * reserved [ ?] unused?, usually 0
8624 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8626 * RETURNS
8627 * S_OK if the file was successfully created
8628 * some STG_E_ value if error
8629 * NOTES
8630 * if pwcsName is NULL, create file with new unique name
8631 * the function can returns
8632 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8633 * (unrealized now)
8635 HRESULT WINAPI StgCreateDocfile(
8636 LPCOLESTR pwcsName,
8637 DWORD grfMode,
8638 DWORD reserved,
8639 IStorage **ppstgOpen)
8641 STGOPTIONS stgoptions = {1, 0, 512};
8643 TRACE("(%s, %x, %d, %p)\n",
8644 debugstr_w(pwcsName), grfMode,
8645 reserved, ppstgOpen);
8647 if (ppstgOpen == 0)
8648 return STG_E_INVALIDPOINTER;
8649 if (reserved != 0)
8650 return STG_E_INVALIDPARAMETER;
8652 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8655 /******************************************************************************
8656 * StgCreateStorageEx [OLE32.@]
8658 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8660 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8661 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8663 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8665 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8666 return STG_E_INVALIDPARAMETER;
8669 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8671 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8672 return STG_E_INVALIDPARAMETER;
8675 if (stgfmt == STGFMT_FILE)
8677 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8678 return STG_E_INVALIDPARAMETER;
8681 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8683 STGOPTIONS defaultOptions = {1, 0, 512};
8685 if (!pStgOptions) pStgOptions = &defaultOptions;
8686 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8690 ERR("Invalid stgfmt argument\n");
8691 return STG_E_INVALIDPARAMETER;
8694 /******************************************************************************
8695 * StgCreatePropSetStg [OLE32.@]
8697 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8698 IPropertySetStorage **propset)
8700 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8701 if (reserved)
8702 return STG_E_INVALIDPARAMETER;
8704 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8707 /******************************************************************************
8708 * StgOpenStorageEx [OLE32.@]
8710 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8712 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8713 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8715 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8717 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8718 return STG_E_INVALIDPARAMETER;
8721 switch (stgfmt)
8723 case STGFMT_FILE:
8724 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8725 return STG_E_INVALIDPARAMETER;
8727 case STGFMT_STORAGE:
8728 break;
8730 case STGFMT_DOCFILE:
8731 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8733 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8734 return STG_E_INVALIDPARAMETER;
8736 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8737 break;
8739 case STGFMT_ANY:
8740 WARN("STGFMT_ANY assuming storage\n");
8741 break;
8743 default:
8744 return STG_E_INVALIDPARAMETER;
8747 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8751 /******************************************************************************
8752 * StgOpenStorage [OLE32.@]
8754 HRESULT WINAPI StgOpenStorage(
8755 const OLECHAR *pwcsName,
8756 IStorage *pstgPriority,
8757 DWORD grfMode,
8758 SNB snbExclude,
8759 DWORD reserved,
8760 IStorage **ppstgOpen)
8762 StorageBaseImpl* newStorage = 0;
8763 HRESULT hr = S_OK;
8764 HANDLE hFile = 0;
8765 DWORD shareMode;
8766 DWORD accessMode;
8767 LPWSTR temp_name = NULL;
8769 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8770 debugstr_w(pwcsName), pstgPriority, grfMode,
8771 snbExclude, reserved, ppstgOpen);
8773 if (pstgPriority)
8775 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8776 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8777 if (FAILED(hr)) goto end;
8778 pwcsName = temp_name;
8779 TRACE("using filename %s\n", debugstr_w(temp_name));
8782 if (pwcsName == 0)
8784 hr = STG_E_INVALIDNAME;
8785 goto end;
8788 if (ppstgOpen == 0)
8790 hr = STG_E_INVALIDPOINTER;
8791 goto end;
8794 if (reserved)
8796 hr = STG_E_INVALIDPARAMETER;
8797 goto end;
8800 if (grfMode & STGM_PRIORITY)
8802 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8803 return STG_E_INVALIDFLAG;
8804 if (grfMode & STGM_DELETEONRELEASE)
8805 return STG_E_INVALIDFUNCTION;
8806 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8807 return STG_E_INVALIDFLAG;
8808 grfMode &= ~0xf0; /* remove the existing sharing mode */
8809 grfMode |= STGM_SHARE_DENY_NONE;
8813 * Validate the sharing mode
8815 if (grfMode & STGM_DIRECT_SWMR)
8817 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8818 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8820 hr = STG_E_INVALIDFLAG;
8821 goto end;
8824 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8825 switch(STGM_SHARE_MODE(grfMode))
8827 case STGM_SHARE_EXCLUSIVE:
8828 case STGM_SHARE_DENY_WRITE:
8829 break;
8830 default:
8831 hr = STG_E_INVALIDFLAG;
8832 goto end;
8835 if ( FAILED( validateSTGM(grfMode) ) ||
8836 (grfMode&STGM_CREATE))
8838 hr = STG_E_INVALIDFLAG;
8839 goto end;
8842 /* shared reading requires transacted or single writer mode */
8843 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8844 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8845 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8847 hr = STG_E_INVALIDFLAG;
8848 goto end;
8852 * Interpret the STGM value grfMode
8854 shareMode = GetShareModeFromSTGM(grfMode);
8855 accessMode = GetAccessModeFromSTGM(grfMode);
8857 *ppstgOpen = 0;
8859 hFile = CreateFileW( pwcsName,
8860 accessMode,
8861 shareMode,
8862 NULL,
8863 OPEN_EXISTING,
8864 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8867 if (hFile==INVALID_HANDLE_VALUE)
8869 DWORD last_error = GetLastError();
8871 hr = E_FAIL;
8873 switch (last_error)
8875 case ERROR_FILE_NOT_FOUND:
8876 hr = STG_E_FILENOTFOUND;
8877 break;
8879 case ERROR_PATH_NOT_FOUND:
8880 hr = STG_E_PATHNOTFOUND;
8881 break;
8883 case ERROR_ACCESS_DENIED:
8884 case ERROR_WRITE_PROTECT:
8885 hr = STG_E_ACCESSDENIED;
8886 break;
8888 case ERROR_SHARING_VIOLATION:
8889 hr = STG_E_SHAREVIOLATION;
8890 break;
8892 default:
8893 hr = E_FAIL;
8896 goto end;
8900 * Refuse to open the file if it's too small to be a structured storage file
8901 * FIXME: verify the file when reading instead of here
8903 if (GetFileSize(hFile, NULL) < HEADER_SIZE)
8905 CloseHandle(hFile);
8906 hr = STG_E_FILEALREADYEXISTS;
8907 goto end;
8911 * Allocate and initialize the new IStorage object.
8913 hr = Storage_Construct(
8914 hFile,
8915 pwcsName,
8916 NULL,
8917 grfMode,
8918 TRUE,
8919 FALSE,
8920 512,
8921 &newStorage);
8923 if (FAILED(hr))
8926 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8928 if(hr == STG_E_INVALIDHEADER)
8929 hr = STG_E_FILEALREADYEXISTS;
8930 goto end;
8933 *ppstgOpen = &newStorage->IStorage_iface;
8935 end:
8936 CoTaskMemFree(temp_name);
8937 if (pstgPriority) IStorage_Release(pstgPriority);
8938 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8939 return hr;
8942 /******************************************************************************
8943 * StgCreateDocfileOnILockBytes [OLE32.@]
8945 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8946 ILockBytes *plkbyt,
8947 DWORD grfMode,
8948 DWORD reserved,
8949 IStorage** ppstgOpen)
8951 StorageBaseImpl* newStorage = 0;
8952 HRESULT hr = S_OK;
8954 if ((ppstgOpen == 0) || (plkbyt == 0))
8955 return STG_E_INVALIDPOINTER;
8958 * Allocate and initialize the new IStorage object.
8960 hr = Storage_Construct(
8963 plkbyt,
8964 grfMode,
8965 FALSE,
8966 TRUE,
8967 512,
8968 &newStorage);
8970 if (FAILED(hr))
8972 return hr;
8975 *ppstgOpen = &newStorage->IStorage_iface;
8977 return hr;
8980 /******************************************************************************
8981 * StgOpenStorageOnILockBytes [OLE32.@]
8983 HRESULT WINAPI StgOpenStorageOnILockBytes(
8984 ILockBytes *plkbyt,
8985 IStorage *pstgPriority,
8986 DWORD grfMode,
8987 SNB snbExclude,
8988 DWORD reserved,
8989 IStorage **ppstgOpen)
8991 StorageBaseImpl* newStorage = 0;
8992 HRESULT hr = S_OK;
8994 if ((plkbyt == 0) || (ppstgOpen == 0))
8995 return STG_E_INVALIDPOINTER;
8997 if ( FAILED( validateSTGM(grfMode) ))
8998 return STG_E_INVALIDFLAG;
9000 *ppstgOpen = 0;
9003 * Allocate and initialize the new IStorage object.
9005 hr = Storage_Construct(
9008 plkbyt,
9009 grfMode,
9010 FALSE,
9011 FALSE,
9012 512,
9013 &newStorage);
9015 if (FAILED(hr))
9017 return hr;
9020 *ppstgOpen = &newStorage->IStorage_iface;
9022 return hr;
9025 /******************************************************************************
9026 * StgSetTimes [ole32.@]
9027 * StgSetTimes [OLE32.@]
9031 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
9032 FILETIME const *patime, FILETIME const *pmtime)
9034 IStorage *stg = NULL;
9035 HRESULT r;
9037 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
9039 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
9040 0, 0, &stg);
9041 if( SUCCEEDED(r) )
9043 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9044 IStorage_Release(stg);
9047 return r;
9050 /******************************************************************************
9051 * StgIsStorageILockBytes [OLE32.@]
9053 * Determines if the ILockBytes contains a storage object.
9055 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9057 BYTE sig[sizeof(STORAGE_magic)];
9058 ULARGE_INTEGER offset;
9059 ULONG read = 0;
9061 offset.u.HighPart = 0;
9062 offset.u.LowPart = 0;
9064 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9066 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9067 return S_OK;
9069 return S_FALSE;
9072 /******************************************************************************
9073 * WriteClassStg [OLE32.@]
9075 * This method will store the specified CLSID in the specified storage object
9077 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9079 if(!pStg)
9080 return E_INVALIDARG;
9082 if(!rclsid)
9083 return STG_E_INVALIDPOINTER;
9085 return IStorage_SetClass(pStg, rclsid);
9088 /***********************************************************************
9089 * ReadClassStg (OLE32.@)
9091 * This method reads the CLSID previously written to a storage object with
9092 * the WriteClassStg.
9094 * PARAMS
9095 * pstg [I] IStorage pointer
9096 * pclsid [O] Pointer to where the CLSID is written
9098 * RETURNS
9099 * Success: S_OK.
9100 * Failure: HRESULT code.
9102 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9104 STATSTG pstatstg;
9105 HRESULT hRes;
9107 TRACE("(%p, %p)\n", pstg, pclsid);
9109 if(!pstg || !pclsid)
9110 return E_INVALIDARG;
9113 * read a STATSTG structure (contains the clsid) from the storage
9115 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9117 if(SUCCEEDED(hRes))
9118 *pclsid=pstatstg.clsid;
9120 return hRes;
9123 /***********************************************************************
9124 * OleLoadFromStream (OLE32.@)
9126 * This function loads an object from stream
9128 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9130 CLSID clsid;
9131 HRESULT res;
9132 LPPERSISTSTREAM xstm;
9134 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9136 res=ReadClassStm(pStm,&clsid);
9137 if (FAILED(res))
9138 return res;
9139 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9140 if (FAILED(res))
9141 return res;
9142 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9143 if (FAILED(res)) {
9144 IUnknown_Release((IUnknown*)*ppvObj);
9145 return res;
9147 res=IPersistStream_Load(xstm,pStm);
9148 IPersistStream_Release(xstm);
9149 /* FIXME: all refcounts ok at this point? I think they should be:
9150 * pStm : unchanged
9151 * ppvObj : 1
9152 * xstm : 0 (released)
9154 return res;
9157 /***********************************************************************
9158 * OleSaveToStream (OLE32.@)
9160 * This function saves an object with the IPersistStream interface on it
9161 * to the specified stream.
9163 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9166 CLSID clsid;
9167 HRESULT res;
9169 TRACE("(%p,%p)\n",pPStm,pStm);
9171 res=IPersistStream_GetClassID(pPStm,&clsid);
9173 if (SUCCEEDED(res)){
9175 res=WriteClassStm(pStm,&clsid);
9177 if (SUCCEEDED(res))
9179 res=IPersistStream_Save(pPStm,pStm,TRUE);
9182 TRACE("Finished Save\n");
9183 return res;
9186 /*************************************************************************
9187 * STORAGE_CreateOleStream [Internal]
9189 * Creates the "\001OLE" stream in the IStorage if necessary.
9191 * PARAMS
9192 * storage [I] Dest storage to create the stream in
9193 * flags [I] flags to be set for newly created stream
9195 * RETURNS
9196 * HRESULT return value
9198 * NOTES
9200 * This stream is still unknown, MS Word seems to have extra data
9201 * but since the data is stored in the OLESTREAM there should be
9202 * no need to recreate the stream. If the stream is manually
9203 * deleted it will create it with this default data.
9206 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9208 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9209 static const DWORD version_magic = 0x02000001;
9210 IStream *stream;
9211 HRESULT hr;
9213 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9214 if (hr == S_OK)
9216 struct empty_1ole_stream {
9217 DWORD version_magic;
9218 DWORD flags;
9219 DWORD update_options;
9220 DWORD reserved;
9221 DWORD mon_stream_size;
9223 struct empty_1ole_stream stream_data;
9225 stream_data.version_magic = version_magic;
9226 stream_data.flags = flags;
9227 stream_data.update_options = 0;
9228 stream_data.reserved = 0;
9229 stream_data.mon_stream_size = 0;
9231 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9232 IStream_Release(stream);
9235 return hr;
9238 /* write a string to a stream, preceded by its length */
9239 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9241 HRESULT r;
9242 LPSTR str;
9243 DWORD len = 0;
9245 if( string )
9246 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9247 r = IStream_Write( stm, &len, sizeof(len), NULL);
9248 if( FAILED( r ) )
9249 return r;
9250 if(len == 0)
9251 return r;
9252 str = CoTaskMemAlloc( len );
9253 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9254 r = IStream_Write( stm, str, len, NULL);
9255 CoTaskMemFree( str );
9256 return r;
9259 /* read a string preceded by its length from a stream */
9260 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9262 HRESULT r;
9263 DWORD len, count = 0;
9264 LPSTR str;
9265 LPWSTR wstr;
9267 r = IStream_Read( stm, &len, sizeof(len), &count );
9268 if( FAILED( r ) )
9269 return r;
9270 if( count != sizeof(len) )
9271 return E_OUTOFMEMORY;
9273 TRACE("%d bytes\n",len);
9275 str = CoTaskMemAlloc( len );
9276 if( !str )
9277 return E_OUTOFMEMORY;
9278 count = 0;
9279 r = IStream_Read( stm, str, len, &count );
9280 if( FAILED( r ) )
9282 CoTaskMemFree( str );
9283 return r;
9285 if( count != len )
9287 CoTaskMemFree( str );
9288 return E_OUTOFMEMORY;
9291 TRACE("Read string %s\n",debugstr_an(str,len));
9293 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9294 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9295 if( wstr )
9297 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9298 wstr[len] = 0;
9300 CoTaskMemFree( str );
9302 *string = wstr;
9304 return r;
9308 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9309 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9311 IStream *pstm;
9312 HRESULT r = S_OK;
9313 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9315 static const BYTE unknown1[12] =
9316 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9317 0xFF, 0xFF, 0xFF, 0xFF};
9318 static const BYTE unknown2[16] =
9319 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9322 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9323 debugstr_w(lpszUserType), debugstr_w(szClipName),
9324 debugstr_w(szProgIDName));
9326 /* Create a CompObj stream */
9327 r = IStorage_CreateStream(pstg, szwStreamName,
9328 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9329 if( FAILED (r) )
9330 return r;
9332 /* Write CompObj Structure to stream */
9333 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9335 if( SUCCEEDED( r ) )
9336 r = WriteClassStm( pstm, clsid );
9338 if( SUCCEEDED( r ) )
9339 r = STREAM_WriteString( pstm, lpszUserType );
9340 if( SUCCEEDED( r ) )
9341 r = STREAM_WriteString( pstm, szClipName );
9342 if( SUCCEEDED( r ) )
9343 r = STREAM_WriteString( pstm, szProgIDName );
9344 if( SUCCEEDED( r ) )
9345 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9347 IStream_Release( pstm );
9349 return r;
9352 /***********************************************************************
9353 * WriteFmtUserTypeStg (OLE32.@)
9355 HRESULT WINAPI WriteFmtUserTypeStg(
9356 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9358 STATSTG stat;
9359 HRESULT r;
9360 WCHAR szwClipName[0x40];
9361 CLSID clsid;
9362 LPWSTR wstrProgID = NULL;
9363 DWORD n;
9365 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9367 /* get the clipboard format name */
9368 if( cf )
9370 n = GetClipboardFormatNameW( cf, szwClipName,
9371 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9372 szwClipName[n]=0;
9375 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9377 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9378 if(SUCCEEDED(r))
9379 clsid = stat.clsid;
9380 else
9381 clsid = CLSID_NULL;
9383 ProgIDFromCLSID(&clsid, &wstrProgID);
9385 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9387 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9388 cf ? szwClipName : NULL, wstrProgID );
9390 CoTaskMemFree(wstrProgID);
9392 return r;
9396 /******************************************************************************
9397 * ReadFmtUserTypeStg [OLE32.@]
9399 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9401 HRESULT r;
9402 IStream *stm = 0;
9403 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9404 unsigned char unknown1[12];
9405 unsigned char unknown2[16];
9406 DWORD count;
9407 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9408 CLSID clsid;
9410 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9412 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9413 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9414 if( FAILED ( r ) )
9416 WARN("Failed to open stream r = %08x\n", r);
9417 return r;
9420 /* read the various parts of the structure */
9421 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9422 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9423 goto end;
9424 r = ReadClassStm( stm, &clsid );
9425 if( FAILED( r ) )
9426 goto end;
9428 r = STREAM_ReadString( stm, &szCLSIDName );
9429 if( FAILED( r ) )
9430 goto end;
9432 r = STREAM_ReadString( stm, &szOleTypeName );
9433 if( FAILED( r ) )
9434 goto end;
9436 r = STREAM_ReadString( stm, &szProgIDName );
9437 if( FAILED( r ) )
9438 goto end;
9440 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9441 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9442 goto end;
9444 /* ok, success... now we just need to store what we found */
9445 if( pcf )
9446 *pcf = RegisterClipboardFormatW( szOleTypeName );
9448 if( lplpszUserType )
9450 *lplpszUserType = szCLSIDName;
9451 szCLSIDName = NULL;
9454 end:
9455 CoTaskMemFree( szCLSIDName );
9456 CoTaskMemFree( szOleTypeName );
9457 CoTaskMemFree( szProgIDName );
9458 IStream_Release( stm );
9460 return r;
9463 /******************************************************************************
9464 * StgIsStorageFile [OLE32.@]
9465 * Verify if the file contains a storage object
9467 * PARAMS
9468 * fn [ I] Filename
9470 * RETURNS
9471 * S_OK if file has magic bytes as a storage object
9472 * S_FALSE if file is not storage
9474 HRESULT WINAPI
9475 StgIsStorageFile(LPCOLESTR fn)
9477 HANDLE hf;
9478 BYTE magic[8];
9479 DWORD bytes_read;
9481 TRACE("%s\n", debugstr_w(fn));
9482 hf = CreateFileW(fn, GENERIC_READ,
9483 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9484 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9486 if (hf == INVALID_HANDLE_VALUE)
9487 return STG_E_FILENOTFOUND;
9489 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9491 WARN(" unable to read file\n");
9492 CloseHandle(hf);
9493 return S_FALSE;
9496 CloseHandle(hf);
9498 if (bytes_read != 8) {
9499 TRACE(" too short\n");
9500 return S_FALSE;
9503 if (!memcmp(magic,STORAGE_magic,8)) {
9504 TRACE(" -> YES\n");
9505 return S_OK;
9508 TRACE(" -> Invalid header.\n");
9509 return S_FALSE;
9512 /***********************************************************************
9513 * WriteClassStm (OLE32.@)
9515 * Writes a CLSID to a stream.
9517 * PARAMS
9518 * pStm [I] Stream to write to.
9519 * rclsid [I] CLSID to write.
9521 * RETURNS
9522 * Success: S_OK.
9523 * Failure: HRESULT code.
9525 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9527 TRACE("(%p,%p)\n",pStm,rclsid);
9529 if (!pStm || !rclsid)
9530 return E_INVALIDARG;
9532 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9535 /***********************************************************************
9536 * ReadClassStm (OLE32.@)
9538 * Reads a CLSID from a stream.
9540 * PARAMS
9541 * pStm [I] Stream to read from.
9542 * rclsid [O] CLSID to read.
9544 * RETURNS
9545 * Success: S_OK.
9546 * Failure: HRESULT code.
9548 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9550 ULONG nbByte;
9551 HRESULT res;
9553 TRACE("(%p,%p)\n",pStm,pclsid);
9555 if (!pStm || !pclsid)
9556 return E_INVALIDARG;
9558 /* clear the output args */
9559 *pclsid = CLSID_NULL;
9561 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9563 if (FAILED(res))
9564 return res;
9566 if (nbByte != sizeof(CLSID))
9567 return STG_E_READFAULT;
9568 else
9569 return S_OK;
9573 /************************************************************************
9574 * OleConvert Functions
9575 ***********************************************************************/
9577 #define OLESTREAM_ID 0x501
9578 #define OLESTREAM_MAX_STR_LEN 255
9580 /* OLESTREAM memory structure to use for Get and Put Routines */
9581 typedef struct
9583 DWORD dwOleID;
9584 DWORD dwTypeID;
9585 DWORD dwOleTypeNameLength;
9586 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9587 CHAR *pstrOleObjFileName;
9588 DWORD dwOleObjFileNameLength;
9589 DWORD dwMetaFileWidth;
9590 DWORD dwMetaFileHeight;
9591 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9592 DWORD dwDataLength;
9593 BYTE *pData;
9594 } OLECONVERT_OLESTREAM_DATA;
9596 /* CompObj Stream structure */
9597 typedef struct
9599 BYTE byUnknown1[12];
9600 CLSID clsid;
9601 DWORD dwCLSIDNameLength;
9602 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9603 DWORD dwOleTypeNameLength;
9604 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9605 DWORD dwProgIDNameLength;
9606 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9607 BYTE byUnknown2[16];
9608 } OLECONVERT_ISTORAGE_COMPOBJ;
9610 /* Ole Presentation Stream structure */
9611 typedef struct
9613 BYTE byUnknown1[28];
9614 DWORD dwExtentX;
9615 DWORD dwExtentY;
9616 DWORD dwSize;
9617 BYTE *pData;
9618 } OLECONVERT_ISTORAGE_OLEPRES;
9621 /*************************************************************************
9622 * OLECONVERT_LoadOLE10 [Internal]
9624 * Loads the OLE10 STREAM to memory
9626 * PARAMS
9627 * pOleStream [I] The OLESTREAM
9628 * pData [I] Data Structure for the OLESTREAM Data
9630 * RETURNS
9631 * Success: S_OK
9632 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9633 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9635 * NOTES
9636 * This function is used by OleConvertOLESTREAMToIStorage only.
9638 * Memory allocated for pData must be freed by the caller
9640 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9642 DWORD dwSize;
9643 HRESULT hRes = S_OK;
9644 int nTryCnt=0;
9645 int max_try = 6;
9647 pData->pData = NULL;
9648 pData->pstrOleObjFileName = NULL;
9650 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9652 /* Get the OleID */
9653 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9654 if(dwSize != sizeof(pData->dwOleID))
9656 hRes = CONVERT10_E_OLESTREAM_GET;
9658 else if(pData->dwOleID != OLESTREAM_ID)
9660 hRes = CONVERT10_E_OLESTREAM_FMT;
9662 else
9664 hRes = S_OK;
9665 break;
9669 if(hRes == S_OK)
9671 /* Get the TypeID... more info needed for this field */
9672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9673 if(dwSize != sizeof(pData->dwTypeID))
9675 hRes = CONVERT10_E_OLESTREAM_GET;
9678 if(hRes == S_OK)
9680 if(pData->dwTypeID != 0)
9682 /* Get the length of the OleTypeName */
9683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9684 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9686 hRes = CONVERT10_E_OLESTREAM_GET;
9689 if(hRes == S_OK)
9691 if(pData->dwOleTypeNameLength > 0)
9693 /* Get the OleTypeName */
9694 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9695 if(dwSize != pData->dwOleTypeNameLength)
9697 hRes = CONVERT10_E_OLESTREAM_GET;
9701 if(bStrem1)
9703 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9704 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9706 hRes = CONVERT10_E_OLESTREAM_GET;
9708 if(hRes == S_OK)
9710 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9711 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9712 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9713 if(pData->pstrOleObjFileName)
9715 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9716 if(dwSize != pData->dwOleObjFileNameLength)
9718 hRes = CONVERT10_E_OLESTREAM_GET;
9721 else
9722 hRes = CONVERT10_E_OLESTREAM_GET;
9725 else
9727 /* Get the Width of the Metafile */
9728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9729 if(dwSize != sizeof(pData->dwMetaFileWidth))
9731 hRes = CONVERT10_E_OLESTREAM_GET;
9733 if(hRes == S_OK)
9735 /* Get the Height of the Metafile */
9736 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9737 if(dwSize != sizeof(pData->dwMetaFileHeight))
9739 hRes = CONVERT10_E_OLESTREAM_GET;
9743 if(hRes == S_OK)
9745 /* Get the Length of the Data */
9746 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9747 if(dwSize != sizeof(pData->dwDataLength))
9749 hRes = CONVERT10_E_OLESTREAM_GET;
9753 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9755 if(!bStrem1) /* if it is a second OLE stream data */
9757 pData->dwDataLength -= 8;
9758 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9759 if(dwSize != sizeof(pData->strUnknown))
9761 hRes = CONVERT10_E_OLESTREAM_GET;
9765 if(hRes == S_OK)
9767 if(pData->dwDataLength > 0)
9769 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9771 /* Get Data (ex. IStorage, Metafile, or BMP) */
9772 if(pData->pData)
9774 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9775 if(dwSize != pData->dwDataLength)
9777 hRes = CONVERT10_E_OLESTREAM_GET;
9780 else
9782 hRes = CONVERT10_E_OLESTREAM_GET;
9788 return hRes;
9791 /*************************************************************************
9792 * OLECONVERT_SaveOLE10 [Internal]
9794 * Saves the OLE10 STREAM From memory
9796 * PARAMS
9797 * pData [I] Data Structure for the OLESTREAM Data
9798 * pOleStream [I] The OLESTREAM to save
9800 * RETURNS
9801 * Success: S_OK
9802 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9804 * NOTES
9805 * This function is used by OleConvertIStorageToOLESTREAM only.
9808 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9810 DWORD dwSize;
9811 HRESULT hRes = S_OK;
9814 /* Set the OleID */
9815 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9816 if(dwSize != sizeof(pData->dwOleID))
9818 hRes = CONVERT10_E_OLESTREAM_PUT;
9821 if(hRes == S_OK)
9823 /* Set the TypeID */
9824 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9825 if(dwSize != sizeof(pData->dwTypeID))
9827 hRes = CONVERT10_E_OLESTREAM_PUT;
9831 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9833 /* Set the Length of the OleTypeName */
9834 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9835 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9837 hRes = CONVERT10_E_OLESTREAM_PUT;
9840 if(hRes == S_OK)
9842 if(pData->dwOleTypeNameLength > 0)
9844 /* Set the OleTypeName */
9845 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9846 if(dwSize != pData->dwOleTypeNameLength)
9848 hRes = CONVERT10_E_OLESTREAM_PUT;
9853 if(hRes == S_OK)
9855 /* Set the width of the Metafile */
9856 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9857 if(dwSize != sizeof(pData->dwMetaFileWidth))
9859 hRes = CONVERT10_E_OLESTREAM_PUT;
9863 if(hRes == S_OK)
9865 /* Set the height of the Metafile */
9866 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9867 if(dwSize != sizeof(pData->dwMetaFileHeight))
9869 hRes = CONVERT10_E_OLESTREAM_PUT;
9873 if(hRes == S_OK)
9875 /* Set the length of the Data */
9876 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9877 if(dwSize != sizeof(pData->dwDataLength))
9879 hRes = CONVERT10_E_OLESTREAM_PUT;
9883 if(hRes == S_OK)
9885 if(pData->dwDataLength > 0)
9887 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9888 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9889 if(dwSize != pData->dwDataLength)
9891 hRes = CONVERT10_E_OLESTREAM_PUT;
9896 return hRes;
9899 /*************************************************************************
9900 * OLECONVERT_GetOLE20FromOLE10[Internal]
9902 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9903 * opens it, and copies the content to the dest IStorage for
9904 * OleConvertOLESTREAMToIStorage
9907 * PARAMS
9908 * pDestStorage [I] The IStorage to copy the data to
9909 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9910 * nBufferLength [I] The size of the buffer
9912 * RETURNS
9913 * Nothing
9915 * NOTES
9919 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9921 HRESULT hRes;
9922 HANDLE hFile;
9923 IStorage *pTempStorage;
9924 DWORD dwNumOfBytesWritten;
9925 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9926 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9928 /* Create a temp File */
9929 GetTempPathW(MAX_PATH, wstrTempDir);
9930 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9931 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9933 if(hFile != INVALID_HANDLE_VALUE)
9935 /* Write IStorage Data to File */
9936 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9937 CloseHandle(hFile);
9939 /* Open and copy temp storage to the Dest Storage */
9940 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9941 if(hRes == S_OK)
9943 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9944 IStorage_Release(pTempStorage);
9946 DeleteFileW(wstrTempFile);
9951 /*************************************************************************
9952 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9954 * Saves the OLE10 STREAM From memory
9956 * PARAMS
9957 * pStorage [I] The Src IStorage to copy
9958 * pData [I] The Dest Memory to write to.
9960 * RETURNS
9961 * The size in bytes allocated for pData
9963 * NOTES
9964 * Memory allocated for pData must be freed by the caller
9966 * Used by OleConvertIStorageToOLESTREAM only.
9969 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9971 HANDLE hFile;
9972 HRESULT hRes;
9973 DWORD nDataLength = 0;
9974 IStorage *pTempStorage;
9975 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9976 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9978 *pData = NULL;
9980 /* Create temp Storage */
9981 GetTempPathW(MAX_PATH, wstrTempDir);
9982 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9983 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9985 if(hRes == S_OK)
9987 /* Copy Src Storage to the Temp Storage */
9988 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9989 IStorage_Release(pTempStorage);
9991 /* Open Temp Storage as a file and copy to memory */
9992 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9993 if(hFile != INVALID_HANDLE_VALUE)
9995 nDataLength = GetFileSize(hFile, NULL);
9996 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9997 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9998 CloseHandle(hFile);
10000 DeleteFileW(wstrTempFile);
10002 return nDataLength;
10005 /*************************************************************************
10006 * OLECONVERT_CreateCompObjStream [Internal]
10008 * Creates a "\001CompObj" is the destination IStorage if necessary.
10010 * PARAMS
10011 * pStorage [I] The dest IStorage to create the CompObj Stream
10012 * if necessary.
10013 * strOleTypeName [I] The ProgID
10015 * RETURNS
10016 * Success: S_OK
10017 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10019 * NOTES
10020 * This function is used by OleConvertOLESTREAMToIStorage only.
10022 * The stream data is stored in the OLESTREAM and there should be
10023 * no need to recreate the stream. If the stream is manually
10024 * deleted it will attempt to create it by querying the registry.
10028 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
10030 IStream *pStream;
10031 HRESULT hStorageRes, hRes = S_OK;
10032 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
10033 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10034 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
10036 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10037 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
10039 /* Initialize the CompObj structure */
10040 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
10041 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
10042 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
10045 /* Create a CompObj stream if it doesn't exist */
10046 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
10047 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10048 if(hStorageRes == S_OK)
10050 /* copy the OleTypeName to the compobj struct */
10051 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10052 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10054 /* copy the OleTypeName to the compobj struct */
10055 /* Note: in the test made, these were Identical */
10056 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10057 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10059 /* Get the CLSID */
10060 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10061 bufferW, OLESTREAM_MAX_STR_LEN );
10062 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10064 if(hRes == S_OK)
10066 HKEY hKey;
10067 LONG hErr;
10068 /* Get the CLSID Default Name from the Registry */
10069 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10070 if(hErr == ERROR_SUCCESS)
10072 char strTemp[OLESTREAM_MAX_STR_LEN];
10073 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10074 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10075 if(hErr == ERROR_SUCCESS)
10077 strcpy(IStorageCompObj.strCLSIDName, strTemp);
10079 RegCloseKey(hKey);
10083 /* Write CompObj Structure to stream */
10084 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10086 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10088 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10089 if(IStorageCompObj.dwCLSIDNameLength > 0)
10091 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10093 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10094 if(IStorageCompObj.dwOleTypeNameLength > 0)
10096 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10098 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10099 if(IStorageCompObj.dwProgIDNameLength > 0)
10101 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10103 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10104 IStream_Release(pStream);
10106 return hRes;
10110 /*************************************************************************
10111 * OLECONVERT_CreateOlePresStream[Internal]
10113 * Creates the "\002OlePres000" Stream with the Metafile data
10115 * PARAMS
10116 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10117 * dwExtentX [I] Width of the Metafile
10118 * dwExtentY [I] Height of the Metafile
10119 * pData [I] Metafile data
10120 * dwDataLength [I] Size of the Metafile data
10122 * RETURNS
10123 * Success: S_OK
10124 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10126 * NOTES
10127 * This function is used by OleConvertOLESTREAMToIStorage only.
10130 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10132 HRESULT hRes;
10133 IStream *pStream;
10134 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10135 BYTE pOlePresStreamHeader [] =
10137 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10138 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10139 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10140 0x00, 0x00, 0x00, 0x00
10143 BYTE pOlePresStreamHeaderEmpty [] =
10145 0x00, 0x00, 0x00, 0x00,
10146 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10147 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10148 0x00, 0x00, 0x00, 0x00
10151 /* Create the OlePres000 Stream */
10152 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10153 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10155 if(hRes == S_OK)
10157 DWORD nHeaderSize;
10158 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10160 memset(&OlePres, 0, sizeof(OlePres));
10161 /* Do we have any metafile data to save */
10162 if(dwDataLength > 0)
10164 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10165 nHeaderSize = sizeof(pOlePresStreamHeader);
10167 else
10169 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10170 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10172 /* Set width and height of the metafile */
10173 OlePres.dwExtentX = dwExtentX;
10174 OlePres.dwExtentY = -dwExtentY;
10176 /* Set Data and Length */
10177 if(dwDataLength > sizeof(METAFILEPICT16))
10179 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10180 OlePres.pData = &(pData[8]);
10182 /* Save OlePres000 Data to Stream */
10183 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10184 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10185 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10186 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10187 if(OlePres.dwSize > 0)
10189 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10191 IStream_Release(pStream);
10195 /*************************************************************************
10196 * OLECONVERT_CreateOle10NativeStream [Internal]
10198 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10200 * PARAMS
10201 * pStorage [I] Dest storage to create the stream in
10202 * pData [I] Ole10 Native Data (ex. bmp)
10203 * dwDataLength [I] Size of the Ole10 Native Data
10205 * RETURNS
10206 * Nothing
10208 * NOTES
10209 * This function is used by OleConvertOLESTREAMToIStorage only.
10211 * Might need to verify the data and return appropriate error message
10214 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10216 HRESULT hRes;
10217 IStream *pStream;
10218 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10220 /* Create the Ole10Native Stream */
10221 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10222 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10224 if(hRes == S_OK)
10226 /* Write info to stream */
10227 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10228 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10229 IStream_Release(pStream);
10234 /*************************************************************************
10235 * OLECONVERT_GetOLE10ProgID [Internal]
10237 * Finds the ProgID (or OleTypeID) from the IStorage
10239 * PARAMS
10240 * pStorage [I] The Src IStorage to get the ProgID
10241 * strProgID [I] the ProgID string to get
10242 * dwSize [I] the size of the string
10244 * RETURNS
10245 * Success: S_OK
10246 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10248 * NOTES
10249 * This function is used by OleConvertIStorageToOLESTREAM only.
10253 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10255 HRESULT hRes;
10256 IStream *pStream;
10257 LARGE_INTEGER iSeekPos;
10258 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10259 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10261 /* Open the CompObj Stream */
10262 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10263 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10264 if(hRes == S_OK)
10267 /*Get the OleType from the CompObj Stream */
10268 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10269 iSeekPos.u.HighPart = 0;
10271 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10272 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10273 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10274 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10275 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10276 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10277 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10279 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10280 if(*dwSize > 0)
10282 IStream_Read(pStream, strProgID, *dwSize, NULL);
10284 IStream_Release(pStream);
10286 else
10288 STATSTG stat;
10289 LPOLESTR wstrProgID;
10291 /* Get the OleType from the registry */
10292 REFCLSID clsid = &(stat.clsid);
10293 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10294 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10295 if(hRes == S_OK)
10297 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10298 CoTaskMemFree(wstrProgID);
10302 return hRes;
10305 /*************************************************************************
10306 * OLECONVERT_GetOle10PresData [Internal]
10308 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10310 * PARAMS
10311 * pStorage [I] Src IStroage
10312 * pOleStream [I] Dest OleStream Mem Struct
10314 * RETURNS
10315 * Nothing
10317 * NOTES
10318 * This function is used by OleConvertIStorageToOLESTREAM only.
10320 * Memory allocated for pData must be freed by the caller
10324 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10327 HRESULT hRes;
10328 IStream *pStream;
10329 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10331 /* Initialize Default data for OLESTREAM */
10332 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10333 pOleStreamData[0].dwTypeID = 2;
10334 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10335 pOleStreamData[1].dwTypeID = 0;
10336 pOleStreamData[0].dwMetaFileWidth = 0;
10337 pOleStreamData[0].dwMetaFileHeight = 0;
10338 pOleStreamData[0].pData = NULL;
10339 pOleStreamData[1].pData = NULL;
10341 /* Open Ole10Native Stream */
10342 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10343 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10344 if(hRes == S_OK)
10347 /* Read Size and Data */
10348 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10349 if(pOleStreamData->dwDataLength > 0)
10351 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10352 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10354 IStream_Release(pStream);
10360 /*************************************************************************
10361 * OLECONVERT_GetOle20PresData[Internal]
10363 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10365 * PARAMS
10366 * pStorage [I] Src IStroage
10367 * pOleStreamData [I] Dest OleStream Mem Struct
10369 * RETURNS
10370 * Nothing
10372 * NOTES
10373 * This function is used by OleConvertIStorageToOLESTREAM only.
10375 * Memory allocated for pData must be freed by the caller
10377 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10379 HRESULT hRes;
10380 IStream *pStream;
10381 OLECONVERT_ISTORAGE_OLEPRES olePress;
10382 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10384 /* Initialize Default data for OLESTREAM */
10385 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10386 pOleStreamData[0].dwTypeID = 2;
10387 pOleStreamData[0].dwMetaFileWidth = 0;
10388 pOleStreamData[0].dwMetaFileHeight = 0;
10389 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10390 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10391 pOleStreamData[1].dwTypeID = 0;
10392 pOleStreamData[1].dwOleTypeNameLength = 0;
10393 pOleStreamData[1].strOleTypeName[0] = 0;
10394 pOleStreamData[1].dwMetaFileWidth = 0;
10395 pOleStreamData[1].dwMetaFileHeight = 0;
10396 pOleStreamData[1].pData = NULL;
10397 pOleStreamData[1].dwDataLength = 0;
10400 /* Open OlePress000 stream */
10401 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10402 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10403 if(hRes == S_OK)
10405 LARGE_INTEGER iSeekPos;
10406 METAFILEPICT16 MetaFilePict;
10407 static const char strMetafilePictName[] = "METAFILEPICT";
10409 /* Set the TypeID for a Metafile */
10410 pOleStreamData[1].dwTypeID = 5;
10412 /* Set the OleTypeName to Metafile */
10413 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10414 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10416 iSeekPos.u.HighPart = 0;
10417 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10419 /* Get Presentation Data */
10420 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10421 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10422 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10423 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10425 /*Set width and Height */
10426 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10427 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10428 if(olePress.dwSize > 0)
10430 /* Set Length */
10431 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10433 /* Set MetaFilePict struct */
10434 MetaFilePict.mm = 8;
10435 MetaFilePict.xExt = olePress.dwExtentX;
10436 MetaFilePict.yExt = olePress.dwExtentY;
10437 MetaFilePict.hMF = 0;
10439 /* Get Metafile Data */
10440 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10441 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10442 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10444 IStream_Release(pStream);
10448 /*************************************************************************
10449 * OleConvertOLESTREAMToIStorage [OLE32.@]
10451 * Read info on MSDN
10453 * TODO
10454 * DVTARGETDEVICE parameter is not handled
10455 * Still unsure of some mem fields for OLE 10 Stream
10456 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10457 * and "\001OLE" streams
10460 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10461 LPOLESTREAM pOleStream,
10462 LPSTORAGE pstg,
10463 const DVTARGETDEVICE* ptd)
10465 int i;
10466 HRESULT hRes=S_OK;
10467 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10469 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10471 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10473 if(ptd != NULL)
10475 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10478 if(pstg == NULL || pOleStream == NULL)
10480 hRes = E_INVALIDARG;
10483 if(hRes == S_OK)
10485 /* Load the OLESTREAM to Memory */
10486 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10489 if(hRes == S_OK)
10491 /* Load the OLESTREAM to Memory (part 2)*/
10492 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10495 if(hRes == S_OK)
10498 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10500 /* Do we have the IStorage Data in the OLESTREAM */
10501 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10503 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10504 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10506 else
10508 /* It must be an original OLE 1.0 source */
10509 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10512 else
10514 /* It must be an original OLE 1.0 source */
10515 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10518 /* Create CompObj Stream if necessary */
10519 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10520 if(hRes == S_OK)
10522 /*Create the Ole Stream if necessary */
10523 STORAGE_CreateOleStream(pstg, 0);
10528 /* Free allocated memory */
10529 for(i=0; i < 2; i++)
10531 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10532 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10533 pOleStreamData[i].pstrOleObjFileName = NULL;
10535 return hRes;
10538 /*************************************************************************
10539 * OleConvertIStorageToOLESTREAM [OLE32.@]
10541 * Read info on MSDN
10543 * Read info on MSDN
10545 * TODO
10546 * Still unsure of some mem fields for OLE 10 Stream
10547 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10548 * and "\001OLE" streams.
10551 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10552 LPSTORAGE pstg,
10553 LPOLESTREAM pOleStream)
10555 int i;
10556 HRESULT hRes = S_OK;
10557 IStream *pStream;
10558 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10559 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10561 TRACE("%p %p\n", pstg, pOleStream);
10563 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10565 if(pstg == NULL || pOleStream == NULL)
10567 hRes = E_INVALIDARG;
10569 if(hRes == S_OK)
10571 /* Get the ProgID */
10572 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10573 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10575 if(hRes == S_OK)
10577 /* Was it originally Ole10 */
10578 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10579 if(hRes == S_OK)
10581 IStream_Release(pStream);
10582 /* Get Presentation Data for Ole10Native */
10583 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10585 else
10587 /* Get Presentation Data (OLE20) */
10588 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10591 /* Save OLESTREAM */
10592 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10593 if(hRes == S_OK)
10595 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10600 /* Free allocated memory */
10601 for(i=0; i < 2; i++)
10603 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10606 return hRes;
10609 enum stream_1ole_flags {
10610 OleStream_LinkedObject = 0x00000001,
10611 OleStream_Convert = 0x00000004
10614 /***********************************************************************
10615 * GetConvertStg (OLE32.@)
10617 HRESULT WINAPI GetConvertStg(IStorage *stg)
10619 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10620 static const DWORD version_magic = 0x02000001;
10621 DWORD header[2];
10622 IStream *stream;
10623 HRESULT hr;
10625 TRACE("%p\n", stg);
10627 if (!stg) return E_INVALIDARG;
10629 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10630 if (FAILED(hr)) return hr;
10632 hr = IStream_Read(stream, header, sizeof(header), NULL);
10633 IStream_Release(stream);
10634 if (FAILED(hr)) return hr;
10636 if (header[0] != version_magic)
10638 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10639 return E_FAIL;
10642 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10645 /***********************************************************************
10646 * SetConvertStg (OLE32.@)
10648 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10650 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10651 DWORD flags = convert ? OleStream_Convert : 0;
10652 IStream *stream;
10653 DWORD header[2];
10654 HRESULT hr;
10656 TRACE("(%p, %d)\n", storage, convert);
10658 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10659 if (FAILED(hr))
10661 if (hr != STG_E_FILENOTFOUND)
10662 return hr;
10664 return STORAGE_CreateOleStream(storage, flags);
10667 hr = IStream_Read(stream, header, sizeof(header), NULL);
10668 if (FAILED(hr))
10670 IStream_Release(stream);
10671 return hr;
10674 /* update flag if differs */
10675 if ((header[1] ^ flags) & OleStream_Convert)
10677 LARGE_INTEGER pos = {{0}};
10679 if (header[1] & OleStream_Convert)
10680 flags = header[1] & ~OleStream_Convert;
10681 else
10682 flags = header[1] | OleStream_Convert;
10684 pos.QuadPart = sizeof(DWORD);
10685 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10686 if (FAILED(hr))
10688 IStream_Release(stream);
10689 return hr;
10692 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10695 IStream_Release(stream);
10696 return hr;