wined3d: Use return type to return result from read_dword.
[wine.git] / dlls / ole32 / storage32.c
blob0d3f9922d78a56c2663520a55d9b94a19a8b7035
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 #include "windef.h"
41 #include "winbase.h"
42 #include "winnls.h"
43 #include "winuser.h"
44 #include "wine/debug.h"
46 #include "storage32.h"
47 #include "ole2.h" /* For Write/ReadClassStm */
49 #include "winreg.h"
50 #include "wine/wingdi16.h"
51 #include "compobj_private.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(storage);
57 * These are signatures to detect the type of Document file.
59 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
60 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
62 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
65 /****************************************************************************
66 * StorageInternalImpl definitions.
68 * Definition of the implementation structure for the IStorage interface.
69 * This one implements the IStorage interface for storage that are
70 * inside another storage.
72 typedef struct StorageInternalImpl
74 struct StorageBaseImpl base;
77 * Entry in the parent's stream tracking list
79 struct list ParentListEntry;
81 StorageBaseImpl *parentStorage;
82 } StorageInternalImpl;
84 static const IStorageVtbl StorageInternalImpl_Vtbl;
85 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
87 typedef struct TransactedDirEntry
89 /* If applicable, a reference to the original DirEntry in the transacted
90 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
91 DirRef transactedParentEntry;
93 /* True if this entry is being used. */
94 BOOL inuse;
96 /* True if data is up to date. */
97 BOOL read;
99 /* True if this entry has been modified. */
100 BOOL dirty;
102 /* True if this entry's stream has been modified. */
103 BOOL stream_dirty;
105 /* True if this entry has been deleted in the transacted storage, but the
106 * delete has not yet been committed. */
107 BOOL deleted;
109 /* If this entry's stream has been modified, a reference to where the stream
110 * is stored in the snapshot file. */
111 DirRef stream_entry;
113 /* This directory entry's data, including any changes that have been made. */
114 DirEntry data;
116 /* A reference to the parent of this node. This is only valid while we are
117 * committing changes. */
118 DirRef parent;
120 /* A reference to a newly-created entry in the transacted parent. This is
121 * always equal to transactedParentEntry except when committing changes. */
122 DirRef newTransactedParentEntry;
123 } TransactedDirEntry;
126 /****************************************************************************
127 * Transacted storage object.
129 typedef struct TransactedSnapshotImpl
131 struct StorageBaseImpl base;
134 * Modified streams are temporarily saved to the scratch file.
136 StorageBaseImpl *scratch;
138 /* The directory structure is kept here, so that we can track how these
139 * entries relate to those in the parent storage. */
140 TransactedDirEntry *entries;
141 ULONG entries_size;
142 ULONG firstFreeEntry;
145 * Changes are committed to the transacted parent.
147 StorageBaseImpl *transactedParent;
149 /* The transaction signature from when we last committed */
150 ULONG lastTransactionSig;
151 } TransactedSnapshotImpl;
153 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
154 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
156 typedef struct TransactedSharedImpl
158 struct StorageBaseImpl base;
161 * Snapshot and uncommitted changes go here.
163 TransactedSnapshotImpl *scratch;
166 * Changes are committed to the transacted parent.
168 StorageBaseImpl *transactedParent;
170 /* The transaction signature from when we last committed */
171 ULONG lastTransactionSig;
172 } TransactedSharedImpl;
175 /****************************************************************************
176 * BlockChainStream definitions.
178 * The BlockChainStream class is a utility class that is used to create an
179 * abstraction of the big block chains in the storage file.
182 struct BlockChainRun
184 /* This represents a range of blocks that happen reside in consecutive sectors. */
185 ULONG firstSector;
186 ULONG firstOffset;
187 ULONG lastOffset;
190 typedef struct BlockChainBlock
192 ULONG index;
193 ULONG sector;
194 BOOL read;
195 BOOL dirty;
196 BYTE data[MAX_BIG_BLOCK_SIZE];
197 } BlockChainBlock;
199 struct BlockChainStream
201 StorageImpl* parentStorage;
202 ULONG* headOfStreamPlaceHolder;
203 DirRef ownerDirEntry;
204 struct BlockChainRun* indexCache;
205 ULONG indexCacheLen;
206 ULONG indexCacheSize;
207 BlockChainBlock cachedBlocks[2];
208 ULONG blockToEvict;
209 ULONG tailIndex;
210 ULONG numBlocks;
213 /* Returns the number of blocks that comprises this chain.
214 * This is not the size of the stream as the last block may not be full!
216 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
218 return This->numBlocks;
221 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
222 static void BlockChainStream_Destroy(BlockChainStream*);
223 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
224 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
225 static HRESULT BlockChainStream_Flush(BlockChainStream*);
226 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
227 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
230 /****************************************************************************
231 * SmallBlockChainStream definitions.
233 * The SmallBlockChainStream class is a utility class that is used to create an
234 * abstraction of the small block chains in the storage file.
237 struct SmallBlockChainStream
239 StorageImpl* parentStorage;
240 DirRef ownerDirEntry;
241 ULONG* headOfStreamPlaceHolder;
244 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
245 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
246 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
247 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
248 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
249 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
252 /************************************************************************
253 * STGM Functions
254 ***********************************************************************/
256 /************************************************************************
257 * This method validates an STGM parameter that can contain the values below
259 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
260 * The stgm values contained in 0xffff0000 are bitmasks.
262 * STGM_DIRECT 0x00000000
263 * STGM_TRANSACTED 0x00010000
264 * STGM_SIMPLE 0x08000000
266 * STGM_READ 0x00000000
267 * STGM_WRITE 0x00000001
268 * STGM_READWRITE 0x00000002
270 * STGM_SHARE_DENY_NONE 0x00000040
271 * STGM_SHARE_DENY_READ 0x00000030
272 * STGM_SHARE_DENY_WRITE 0x00000020
273 * STGM_SHARE_EXCLUSIVE 0x00000010
275 * STGM_PRIORITY 0x00040000
276 * STGM_DELETEONRELEASE 0x04000000
278 * STGM_CREATE 0x00001000
279 * STGM_CONVERT 0x00020000
280 * STGM_FAILIFTHERE 0x00000000
282 * STGM_NOSCRATCH 0x00100000
283 * STGM_NOSNAPSHOT 0x00200000
285 static HRESULT validateSTGM(DWORD stgm)
287 DWORD access = STGM_ACCESS_MODE(stgm);
288 DWORD share = STGM_SHARE_MODE(stgm);
289 DWORD create = STGM_CREATE_MODE(stgm);
291 if (stgm&~STGM_KNOWN_FLAGS)
293 ERR("unknown flags %#lx\n", stgm);
294 return E_FAIL;
297 switch (access)
299 case STGM_READ:
300 case STGM_WRITE:
301 case STGM_READWRITE:
302 break;
303 default:
304 return E_FAIL;
307 switch (share)
309 case STGM_SHARE_DENY_NONE:
310 case STGM_SHARE_DENY_READ:
311 case STGM_SHARE_DENY_WRITE:
312 case STGM_SHARE_EXCLUSIVE:
313 break;
314 case 0:
315 if (!(stgm & STGM_TRANSACTED))
316 return E_FAIL;
317 break;
318 default:
319 return E_FAIL;
322 switch (create)
324 case STGM_CREATE:
325 case STGM_FAILIFTHERE:
326 break;
327 default:
328 return E_FAIL;
332 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
334 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
335 return E_FAIL;
338 * STGM_CREATE | STGM_CONVERT
339 * if both are false, STGM_FAILIFTHERE is set to TRUE
341 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
342 return E_FAIL;
345 * STGM_NOSCRATCH requires STGM_TRANSACTED
347 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
348 return E_FAIL;
351 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
352 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
354 if ( (stgm & STGM_NOSNAPSHOT) &&
355 (!(stgm & STGM_TRANSACTED) ||
356 share == STGM_SHARE_EXCLUSIVE ||
357 share == STGM_SHARE_DENY_WRITE) )
358 return E_FAIL;
360 return S_OK;
363 /************************************************************************
364 * GetShareModeFromSTGM
366 * This method will return a share mode flag from a STGM value.
367 * The STGM value is assumed valid.
369 static DWORD GetShareModeFromSTGM(DWORD stgm)
371 switch (STGM_SHARE_MODE(stgm))
373 case 0:
374 assert(stgm & STGM_TRANSACTED);
375 /* fall-through */
376 case STGM_SHARE_DENY_NONE:
377 return FILE_SHARE_READ | FILE_SHARE_WRITE;
378 case STGM_SHARE_DENY_READ:
379 return FILE_SHARE_WRITE;
380 case STGM_SHARE_DENY_WRITE:
381 case STGM_SHARE_EXCLUSIVE:
382 return FILE_SHARE_READ;
384 ERR("Invalid share mode!\n");
385 assert(0);
386 return 0;
389 /************************************************************************
390 * GetAccessModeFromSTGM
392 * This method will return an access mode flag from a STGM value.
393 * The STGM value is assumed valid.
395 static DWORD GetAccessModeFromSTGM(DWORD stgm)
397 switch (STGM_ACCESS_MODE(stgm))
399 case STGM_READ:
400 return GENERIC_READ;
401 case STGM_WRITE:
402 case STGM_READWRITE:
403 return GENERIC_READ | GENERIC_WRITE;
405 ERR("Invalid access mode!\n");
406 assert(0);
407 return 0;
410 /************************************************************************
411 * GetCreationModeFromSTGM
413 * This method will return a creation mode flag from a STGM value.
414 * The STGM value is assumed valid.
416 static DWORD GetCreationModeFromSTGM(DWORD stgm)
418 switch(STGM_CREATE_MODE(stgm))
420 case STGM_CREATE:
421 return CREATE_ALWAYS;
422 case STGM_CONVERT:
423 FIXME("STGM_CONVERT not implemented!\n");
424 return CREATE_NEW;
425 case STGM_FAILIFTHERE:
426 return CREATE_NEW;
428 ERR("Invalid create mode!\n");
429 assert(0);
430 return 0;
434 /************************************************************************
435 * IDirectWriterLock implementation
436 ***********************************************************************/
438 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
440 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
443 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
445 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
446 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
449 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
451 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
452 return IStorage_AddRef(&This->IStorage_iface);
455 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
457 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
458 return IStorage_Release(&This->IStorage_iface);
461 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
463 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
464 FIXME("%p, %ld: stub\n", This, timeout);
465 return E_NOTIMPL;
468 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
470 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
471 FIXME("(%p): stub\n", This);
472 return E_NOTIMPL;
475 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
477 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
478 FIXME("(%p): stub\n", This);
479 return E_NOTIMPL;
482 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
484 directwriterlock_QueryInterface,
485 directwriterlock_AddRef,
486 directwriterlock_Release,
487 directwriterlock_WaitForWriteAccess,
488 directwriterlock_ReleaseWriteAccess,
489 directwriterlock_HaveWriteAccess
493 /************************************************************************
494 * StorageBaseImpl implementation : Tree helper functions
495 ***********************************************************************/
497 /****************************************************************************
499 * Internal Method
501 * Case insensitive comparison of DirEntry.name by first considering
502 * their size.
504 * Returns <0 when name1 < name2
505 * >0 when name1 > name2
506 * 0 when name1 == name2
508 static LONG entryNameCmp(
509 const OLECHAR *name1,
510 const OLECHAR *name2)
512 LONG diff = lstrlenW(name1) - lstrlenW(name2);
514 while (diff == 0 && *name1 != 0)
517 * We compare the string themselves only when they are of the same length
519 diff = towupper(*name1++) - towupper(*name2++);
522 return diff;
525 /****************************************************************************
527 * Internal Method
529 * Find and read the element of a storage with the given name.
531 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
532 const OLECHAR *name, DirEntry *data)
534 DirRef currentEntry;
536 /* Read the storage entry to find the root of the tree. */
537 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
539 currentEntry = data->dirRootEntry;
541 while (currentEntry != DIRENTRY_NULL)
543 LONG cmp;
545 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
547 cmp = entryNameCmp(name, data->name);
549 if (cmp == 0)
550 /* found it */
551 break;
553 else if (cmp < 0)
554 currentEntry = data->leftChild;
556 else if (cmp > 0)
557 currentEntry = data->rightChild;
560 return currentEntry;
563 /****************************************************************************
565 * Internal Method
567 * Find and read the binary tree parent of the element with the given name.
569 * If there is no such element, find a place where it could be inserted and
570 * return STG_E_FILENOTFOUND.
572 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
573 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
574 ULONG *relation)
576 DirRef childEntry;
577 DirEntry childData;
579 /* Read the storage entry to find the root of the tree. */
580 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
582 *parentEntry = storageEntry;
583 *relation = DIRENTRY_RELATION_DIR;
585 childEntry = parentData->dirRootEntry;
587 while (childEntry != DIRENTRY_NULL)
589 LONG cmp;
591 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
593 cmp = entryNameCmp(childName, childData.name);
595 if (cmp == 0)
596 /* found it */
597 break;
599 else if (cmp < 0)
601 *parentData = childData;
602 *parentEntry = childEntry;
603 *relation = DIRENTRY_RELATION_PREVIOUS;
605 childEntry = parentData->leftChild;
608 else if (cmp > 0)
610 *parentData = childData;
611 *parentEntry = childEntry;
612 *relation = DIRENTRY_RELATION_NEXT;
614 childEntry = parentData->rightChild;
618 if (childEntry == DIRENTRY_NULL)
619 return STG_E_FILENOTFOUND;
620 else
621 return S_OK;
624 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
626 switch (relation)
628 case DIRENTRY_RELATION_PREVIOUS:
629 entry->leftChild = new_target;
630 break;
631 case DIRENTRY_RELATION_NEXT:
632 entry->rightChild = new_target;
633 break;
634 case DIRENTRY_RELATION_DIR:
635 entry->dirRootEntry = new_target;
636 break;
637 default:
638 assert(0);
642 /****************************************************************************
644 * Internal Method
646 * Add a directory entry to a storage
648 static HRESULT insertIntoTree(
649 StorageBaseImpl *This,
650 DirRef parentStorageIndex,
651 DirRef newEntryIndex)
653 DirEntry currentEntry;
654 DirEntry newEntry;
657 * Read the inserted entry
659 StorageBaseImpl_ReadDirEntry(This,
660 newEntryIndex,
661 &newEntry);
664 * Read the storage entry
666 StorageBaseImpl_ReadDirEntry(This,
667 parentStorageIndex,
668 &currentEntry);
670 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
673 * The root storage contains some element, therefore, start the research
674 * for the appropriate location.
676 BOOL found = FALSE;
677 DirRef current, next, previous, currentEntryId;
680 * Keep a reference to the root of the storage's element tree
682 currentEntryId = currentEntry.dirRootEntry;
685 * Read
687 StorageBaseImpl_ReadDirEntry(This,
688 currentEntry.dirRootEntry,
689 &currentEntry);
691 previous = currentEntry.leftChild;
692 next = currentEntry.rightChild;
693 current = currentEntryId;
695 while (!found)
697 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
699 if (diff < 0)
701 if (previous != DIRENTRY_NULL)
703 StorageBaseImpl_ReadDirEntry(This,
704 previous,
705 &currentEntry);
706 current = previous;
708 else
710 currentEntry.leftChild = newEntryIndex;
711 StorageBaseImpl_WriteDirEntry(This,
712 current,
713 &currentEntry);
714 found = TRUE;
717 else if (diff > 0)
719 if (next != DIRENTRY_NULL)
721 StorageBaseImpl_ReadDirEntry(This,
722 next,
723 &currentEntry);
724 current = next;
726 else
728 currentEntry.rightChild = newEntryIndex;
729 StorageBaseImpl_WriteDirEntry(This,
730 current,
731 &currentEntry);
732 found = TRUE;
735 else
738 * Trying to insert an item with the same name in the
739 * subtree structure.
741 return STG_E_FILEALREADYEXISTS;
744 previous = currentEntry.leftChild;
745 next = currentEntry.rightChild;
748 else
751 * The storage is empty, make the new entry the root of its element tree
753 currentEntry.dirRootEntry = newEntryIndex;
754 StorageBaseImpl_WriteDirEntry(This,
755 parentStorageIndex,
756 &currentEntry);
759 return S_OK;
762 /*************************************************************************
764 * Internal Method
766 * This method removes a directory entry from its parent storage tree without
767 * freeing any resources attached to it.
769 static HRESULT removeFromTree(
770 StorageBaseImpl *This,
771 DirRef parentStorageIndex,
772 DirRef deletedIndex)
774 DirEntry entryToDelete;
775 DirEntry parentEntry;
776 DirRef parentEntryRef;
777 ULONG typeOfRelation;
778 HRESULT hr;
780 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
782 if (hr != S_OK)
783 return hr;
786 * Find the element that links to the one we want to delete.
788 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
789 &parentEntry, &parentEntryRef, &typeOfRelation);
791 if (hr != S_OK)
792 return hr;
794 if (entryToDelete.leftChild != DIRENTRY_NULL)
797 * Replace the deleted entry with its left child
799 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
801 hr = StorageBaseImpl_WriteDirEntry(
802 This,
803 parentEntryRef,
804 &parentEntry);
805 if(FAILED(hr))
807 return hr;
810 if (entryToDelete.rightChild != DIRENTRY_NULL)
813 * We need to reinsert the right child somewhere. We already know it and
814 * its children are greater than everything in the left tree, so we
815 * insert it at the rightmost point in the left tree.
817 DirRef newRightChildParent = entryToDelete.leftChild;
818 DirEntry newRightChildParentEntry;
822 hr = StorageBaseImpl_ReadDirEntry(
823 This,
824 newRightChildParent,
825 &newRightChildParentEntry);
826 if (FAILED(hr))
828 return hr;
831 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
832 newRightChildParent = newRightChildParentEntry.rightChild;
833 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
835 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
837 hr = StorageBaseImpl_WriteDirEntry(
838 This,
839 newRightChildParent,
840 &newRightChildParentEntry);
841 if (FAILED(hr))
843 return hr;
847 else
850 * Replace the deleted entry with its right child
852 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
854 hr = StorageBaseImpl_WriteDirEntry(
855 This,
856 parentEntryRef,
857 &parentEntry);
858 if(FAILED(hr))
860 return hr;
864 return hr;
868 /************************************************************************
869 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
870 ***********************************************************************/
873 * IEnumSTATSTGImpl definitions.
875 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
876 * This class allows iterating through the content of a storage and finding
877 * specific items inside it.
879 struct IEnumSTATSTGImpl
881 IEnumSTATSTG IEnumSTATSTG_iface;
883 LONG ref; /* Reference count */
884 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
885 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
887 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
890 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
892 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
895 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
897 IStorage_Release(&This->parentStorage->IStorage_iface);
898 HeapFree(GetProcessHeap(), 0, This);
901 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
902 IEnumSTATSTG* iface,
903 REFIID riid,
904 void** ppvObject)
906 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
908 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
910 if (ppvObject==0)
911 return E_INVALIDARG;
913 *ppvObject = 0;
915 if (IsEqualGUID(&IID_IUnknown, riid) ||
916 IsEqualGUID(&IID_IEnumSTATSTG, riid))
918 *ppvObject = &This->IEnumSTATSTG_iface;
919 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
920 TRACE("<-- %p\n", *ppvObject);
921 return S_OK;
924 TRACE("<-- E_NOINTERFACE\n");
925 return E_NOINTERFACE;
928 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
929 IEnumSTATSTG* iface)
931 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
932 return InterlockedIncrement(&This->ref);
935 static ULONG WINAPI IEnumSTATSTGImpl_Release(
936 IEnumSTATSTG* iface)
938 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
940 ULONG newRef;
942 newRef = InterlockedDecrement(&This->ref);
944 if (newRef==0)
946 IEnumSTATSTGImpl_Destroy(This);
949 return newRef;
952 static HRESULT IEnumSTATSTGImpl_GetNextRef(
953 IEnumSTATSTGImpl* This,
954 DirRef *ref)
956 DirRef result = DIRENTRY_NULL;
957 DirRef searchNode;
958 DirEntry entry;
959 HRESULT hr;
960 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
962 TRACE("%p,%p\n", This, ref);
964 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
965 This->parentStorage->storageDirEntry, &entry);
966 searchNode = entry.dirRootEntry;
968 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
970 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
972 if (SUCCEEDED(hr))
974 LONG diff = entryNameCmp( entry.name, This->name);
976 if (diff <= 0)
978 searchNode = entry.rightChild;
980 else
982 result = searchNode;
983 memcpy(result_name, entry.name, sizeof(result_name));
984 searchNode = entry.leftChild;
989 if (SUCCEEDED(hr))
991 *ref = result;
992 if (result != DIRENTRY_NULL)
993 memcpy(This->name, result_name, sizeof(result_name));
996 TRACE("<-- %#lx\n", hr);
997 return hr;
1000 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
1001 IEnumSTATSTG* iface,
1002 ULONG celt,
1003 STATSTG* rgelt,
1004 ULONG* pceltFetched)
1006 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1008 DirEntry currentEntry;
1009 STATSTG* currentReturnStruct = rgelt;
1010 ULONG objectFetched = 0;
1011 DirRef currentSearchNode;
1012 HRESULT hr=S_OK;
1014 TRACE("%p, %lu, %p, %p.\n", iface, celt, rgelt, pceltFetched);
1016 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
1017 return E_INVALIDARG;
1019 if (This->parentStorage->reverted)
1021 TRACE("<-- STG_E_REVERTED\n");
1022 return STG_E_REVERTED;
1026 * To avoid the special case, get another pointer to a ULONG value if
1027 * the caller didn't supply one.
1029 if (pceltFetched==0)
1030 pceltFetched = &objectFetched;
1033 * Start the iteration, we will iterate until we hit the end of the
1034 * linked list or until we hit the number of items to iterate through
1036 *pceltFetched = 0;
1038 while ( *pceltFetched < celt )
1040 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1042 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1044 memset(currentReturnStruct, 0, sizeof(*currentReturnStruct));
1045 break;
1049 * Read the entry from the storage.
1051 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
1052 currentSearchNode,
1053 &currentEntry);
1054 if (FAILED(hr)) break;
1057 * Copy the information to the return buffer.
1059 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1060 currentReturnStruct,
1061 &currentEntry,
1062 STATFLAG_DEFAULT);
1065 * Step to the next item in the iteration
1067 (*pceltFetched)++;
1068 currentReturnStruct++;
1071 if (SUCCEEDED(hr) && *pceltFetched != celt)
1072 hr = S_FALSE;
1074 TRACE("<-- %#lx (asked %lu, got %lu)\n", hr, celt, *pceltFetched);
1075 return hr;
1079 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1080 IEnumSTATSTG* iface,
1081 ULONG celt)
1083 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1085 ULONG objectFetched = 0;
1086 DirRef currentSearchNode;
1087 HRESULT hr=S_OK;
1089 TRACE("%p, %lu.\n", iface, celt);
1091 if (This->parentStorage->reverted)
1093 TRACE("<-- STG_E_REVERTED\n");
1094 return STG_E_REVERTED;
1097 while ( (objectFetched < celt) )
1099 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1101 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1102 break;
1104 objectFetched++;
1107 if (SUCCEEDED(hr) && objectFetched != celt)
1108 return S_FALSE;
1110 TRACE("<-- %#lx\n", hr);
1111 return hr;
1114 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
1115 IEnumSTATSTG* iface)
1117 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1119 TRACE("%p\n", iface);
1121 if (This->parentStorage->reverted)
1123 TRACE("<-- STG_E_REVERTED\n");
1124 return STG_E_REVERTED;
1127 This->name[0] = 0;
1129 return S_OK;
1132 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1134 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
1135 IEnumSTATSTG* iface,
1136 IEnumSTATSTG** ppenum)
1138 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1139 IEnumSTATSTGImpl* newClone;
1141 TRACE("%p,%p\n", iface, ppenum);
1143 if (This->parentStorage->reverted)
1145 TRACE("<-- STG_E_REVERTED\n");
1146 return STG_E_REVERTED;
1149 if (ppenum==0)
1150 return E_INVALIDARG;
1152 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1153 This->storageDirEntry);
1154 if (!newClone)
1156 *ppenum = NULL;
1157 return E_OUTOFMEMORY;
1161 * The new clone enumeration must point to the same current node as
1162 * the old one.
1164 memcpy(newClone->name, This->name, sizeof(newClone->name));
1166 *ppenum = &newClone->IEnumSTATSTG_iface;
1168 return S_OK;
1172 * Virtual function table for the IEnumSTATSTGImpl class.
1174 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1176 IEnumSTATSTGImpl_QueryInterface,
1177 IEnumSTATSTGImpl_AddRef,
1178 IEnumSTATSTGImpl_Release,
1179 IEnumSTATSTGImpl_Next,
1180 IEnumSTATSTGImpl_Skip,
1181 IEnumSTATSTGImpl_Reset,
1182 IEnumSTATSTGImpl_Clone
1185 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
1186 StorageBaseImpl* parentStorage,
1187 DirRef storageDirEntry)
1189 IEnumSTATSTGImpl* newEnumeration;
1191 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1193 if (newEnumeration)
1195 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1196 newEnumeration->ref = 1;
1197 newEnumeration->name[0] = 0;
1200 * We want to nail-down the reference to the storage in case the
1201 * enumeration out-lives the storage in the client application.
1203 newEnumeration->parentStorage = parentStorage;
1204 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1206 newEnumeration->storageDirEntry = storageDirEntry;
1209 return newEnumeration;
1213 /************************************************************************
1214 * StorageBaseImpl implementation
1215 ***********************************************************************/
1217 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
1219 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1222 /************************************************************************
1223 * StorageBaseImpl_QueryInterface (IUnknown)
1225 * This method implements the common QueryInterface for all IStorage
1226 * implementations contained in this file.
1228 * See Windows documentation for more details on IUnknown methods.
1230 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
1231 IStorage* iface,
1232 REFIID riid,
1233 void** ppvObject)
1235 StorageBaseImpl *This = impl_from_IStorage(iface);
1237 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
1239 if (!ppvObject)
1240 return E_INVALIDARG;
1242 *ppvObject = 0;
1244 if (IsEqualGUID(&IID_IUnknown, riid) ||
1245 IsEqualGUID(&IID_IStorage, riid))
1247 *ppvObject = &This->IStorage_iface;
1249 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1251 *ppvObject = &This->IPropertySetStorage_iface;
1253 /* locking interface is reported for writer only */
1254 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1256 *ppvObject = &This->IDirectWriterLock_iface;
1258 else
1260 TRACE("<-- E_NOINTERFACE\n");
1261 return E_NOINTERFACE;
1264 IStorage_AddRef(iface);
1265 TRACE("<-- %p\n", *ppvObject);
1266 return S_OK;
1269 /************************************************************************
1270 * StorageBaseImpl_AddRef (IUnknown)
1272 * This method implements the common AddRef for all IStorage
1273 * implementations contained in this file.
1275 * See Windows documentation for more details on IUnknown methods.
1277 static ULONG WINAPI StorageBaseImpl_AddRef(
1278 IStorage* iface)
1280 StorageBaseImpl *This = impl_from_IStorage(iface);
1281 ULONG ref = InterlockedIncrement(&This->ref);
1283 TRACE("%p, refcount %lu.\n", iface, ref);
1285 return ref;
1288 /************************************************************************
1289 * StorageBaseImpl_Release (IUnknown)
1291 * This method implements the common Release for all IStorage
1292 * implementations contained in this file.
1294 * See Windows documentation for more details on IUnknown methods.
1296 static ULONG WINAPI StorageBaseImpl_Release(
1297 IStorage* iface)
1299 StorageBaseImpl *This = impl_from_IStorage(iface);
1301 ULONG ref = InterlockedDecrement(&This->ref);
1303 TRACE("%p, refcount %lu.\n", iface, ref);
1305 if (ref == 0)
1308 * Since we are using a system of base-classes, we want to call the
1309 * destructor of the appropriate derived class. To do this, we are
1310 * using virtual functions to implement the destructor.
1312 StorageBaseImpl_Destroy(This);
1315 return ref;
1318 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1319 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1320 SNB snbExclude, IStorage *pstgDest);
1322 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1323 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1324 SNB snbExclude, IStorage *pstgDest)
1326 DirEntry data;
1327 HRESULT hr;
1328 BOOL skip = FALSE;
1329 IStorage *pstgTmp;
1330 IStream *pstrChild, *pstrTmp;
1331 STATSTG strStat;
1333 if (srcEntry == DIRENTRY_NULL)
1334 return S_OK;
1336 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1338 if (FAILED(hr))
1339 return hr;
1341 if ( snbExclude )
1343 WCHAR **snb = snbExclude;
1345 while ( *snb != NULL && !skip )
1347 if ( wcscmp(data.name, *snb) == 0 )
1348 skip = TRUE;
1349 ++snb;
1353 if (!skip)
1355 if (data.stgType == STGTY_STORAGE && !skip_storage)
1358 * create a new storage in destination storage
1360 hr = IStorage_CreateStorage( pstgDest, data.name,
1361 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1362 0, 0,
1363 &pstgTmp );
1366 * if it already exist, don't create a new one use this one
1368 if (hr == STG_E_FILEALREADYEXISTS)
1370 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1371 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1372 NULL, 0, &pstgTmp );
1375 if (SUCCEEDED(hr))
1377 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1378 skip_stream, NULL, pstgTmp );
1380 IStorage_Release(pstgTmp);
1383 else if (data.stgType == STGTY_STREAM && !skip_stream)
1386 * create a new stream in destination storage. If the stream already
1387 * exist, it will be deleted and a new one will be created.
1389 hr = IStorage_CreateStream( pstgDest, data.name,
1390 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1391 0, 0, &pstrTmp );
1394 * open child stream storage. This operation must succeed even if the
1395 * stream is already open, so we use internal functions to do it.
1397 if (hr == S_OK)
1399 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1401 if (streamimpl)
1403 pstrChild = &streamimpl->IStream_iface;
1404 if (pstrChild)
1405 IStream_AddRef(pstrChild);
1407 else
1409 pstrChild = NULL;
1410 hr = E_OUTOFMEMORY;
1414 if (hr == S_OK)
1417 * Get the size of the source stream
1419 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1422 * Set the size of the destination stream.
1424 IStream_SetSize(pstrTmp, strStat.cbSize);
1427 * do the copy
1429 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1430 NULL, NULL );
1432 IStream_Release( pstrChild );
1435 IStream_Release( pstrTmp );
1439 /* copy siblings */
1440 if (SUCCEEDED(hr))
1441 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1442 skip_stream, snbExclude, pstgDest );
1444 if (SUCCEEDED(hr))
1445 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1446 skip_stream, snbExclude, pstgDest );
1448 TRACE("<-- %#lx\n", hr);
1449 return hr;
1452 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1454 StgStreamImpl *strm;
1456 TRACE("%p, %ld.\n", stg, streamEntry);
1458 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1460 if (strm->dirEntry == streamEntry)
1462 return TRUE;
1466 return FALSE;
1469 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1471 StorageInternalImpl *childstg;
1473 TRACE("%p, %ld.\n", stg, storageEntry);
1475 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1477 if (childstg->base.storageDirEntry == storageEntry)
1479 return TRUE;
1483 return FALSE;
1486 /************************************************************************
1487 * StorageBaseImpl_OpenStream (IStorage)
1489 * This method will open the specified stream object from the current storage.
1491 * See Windows documentation for more details on IStorage methods.
1493 static HRESULT WINAPI StorageBaseImpl_OpenStream(
1494 IStorage* iface,
1495 const OLECHAR* pwcsName, /* [string][in] */
1496 void* reserved1, /* [unique][in] */
1497 DWORD grfMode, /* [in] */
1498 DWORD reserved2, /* [in] */
1499 IStream** ppstm) /* [out] */
1501 StorageBaseImpl *This = impl_from_IStorage(iface);
1502 StgStreamImpl* newStream;
1503 DirEntry currentEntry;
1504 DirRef streamEntryRef;
1505 HRESULT res = STG_E_UNKNOWN;
1507 TRACE("%p, %s, %p, %#lx, %ld, %p.\n", iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1509 if ( (pwcsName==NULL) || (ppstm==0) )
1511 res = E_INVALIDARG;
1512 goto end;
1515 *ppstm = NULL;
1517 if ( FAILED( validateSTGM(grfMode) ) ||
1518 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1520 res = STG_E_INVALIDFLAG;
1521 goto end;
1525 * As documented.
1527 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1529 res = STG_E_INVALIDFUNCTION;
1530 goto end;
1533 if (This->reverted)
1535 res = STG_E_REVERTED;
1536 goto end;
1540 * Check that we're compatible with the parent's storage mode, but
1541 * only if we are not in transacted mode
1543 if(!(This->openFlags & STGM_TRANSACTED)) {
1544 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1546 res = STG_E_INVALIDFLAG;
1547 goto end;
1552 * Search for the element with the given name
1554 streamEntryRef = findElement(
1555 This,
1556 This->storageDirEntry,
1557 pwcsName,
1558 &currentEntry);
1561 * If it was found, construct the stream object and return a pointer to it.
1563 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1564 (currentEntry.stgType==STGTY_STREAM) )
1566 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1568 /* A single stream cannot be opened a second time. */
1569 res = STG_E_ACCESSDENIED;
1570 goto end;
1573 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1575 if (newStream)
1577 newStream->grfMode = grfMode;
1578 *ppstm = &newStream->IStream_iface;
1580 IStream_AddRef(*ppstm);
1582 res = S_OK;
1583 goto end;
1586 res = E_OUTOFMEMORY;
1587 goto end;
1590 res = STG_E_FILENOTFOUND;
1592 end:
1593 if (res == S_OK)
1594 TRACE("<-- IStream %p\n", *ppstm);
1595 TRACE("<-- %#lx\n", res);
1596 return res;
1599 /************************************************************************
1600 * StorageBaseImpl_OpenStorage (IStorage)
1602 * This method will open a new storage object from the current storage.
1604 * See Windows documentation for more details on IStorage methods.
1606 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
1607 IStorage* iface,
1608 const OLECHAR* pwcsName, /* [string][unique][in] */
1609 IStorage* pstgPriority, /* [unique][in] */
1610 DWORD grfMode, /* [in] */
1611 SNB snbExclude, /* [unique][in] */
1612 DWORD reserved, /* [in] */
1613 IStorage** ppstg) /* [out] */
1615 StorageBaseImpl *This = impl_from_IStorage(iface);
1616 StorageInternalImpl* newStorage;
1617 StorageBaseImpl* newTransactedStorage;
1618 DirEntry currentEntry;
1619 DirRef storageEntryRef;
1620 HRESULT res = STG_E_UNKNOWN;
1622 TRACE("%p, %s, %p, %#lx, %p, %ld, %p.\n", iface, debugstr_w(pwcsName), pstgPriority,
1623 grfMode, snbExclude, reserved, ppstg);
1625 if ((pwcsName==NULL) || (ppstg==0) )
1627 res = E_INVALIDARG;
1628 goto end;
1631 if (This->openFlags & STGM_SIMPLE)
1633 res = STG_E_INVALIDFUNCTION;
1634 goto end;
1637 /* as documented */
1638 if (snbExclude != NULL)
1640 res = STG_E_INVALIDPARAMETER;
1641 goto end;
1644 if ( FAILED( validateSTGM(grfMode) ))
1646 res = STG_E_INVALIDFLAG;
1647 goto end;
1651 * As documented.
1653 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1654 (grfMode & STGM_DELETEONRELEASE) ||
1655 (grfMode & STGM_PRIORITY) )
1657 res = STG_E_INVALIDFUNCTION;
1658 goto end;
1661 if (This->reverted)
1662 return STG_E_REVERTED;
1665 * Check that we're compatible with the parent's storage mode,
1666 * but only if we are not transacted
1668 if(!(This->openFlags & STGM_TRANSACTED)) {
1669 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1671 res = STG_E_ACCESSDENIED;
1672 goto end;
1676 *ppstg = NULL;
1678 storageEntryRef = findElement(
1679 This,
1680 This->storageDirEntry,
1681 pwcsName,
1682 &currentEntry);
1684 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1685 (currentEntry.stgType==STGTY_STORAGE) )
1687 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1689 /* A single storage cannot be opened a second time. */
1690 res = STG_E_ACCESSDENIED;
1691 goto end;
1694 newStorage = StorageInternalImpl_Construct(
1695 This,
1696 grfMode,
1697 storageEntryRef);
1699 if (newStorage != 0)
1701 if (grfMode & STGM_TRANSACTED)
1703 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1705 if (FAILED(res))
1707 HeapFree(GetProcessHeap(), 0, newStorage);
1708 goto end;
1711 *ppstg = &newTransactedStorage->IStorage_iface;
1713 else
1715 *ppstg = &newStorage->base.IStorage_iface;
1718 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1720 res = S_OK;
1721 goto end;
1724 res = STG_E_INSUFFICIENTMEMORY;
1725 goto end;
1728 res = STG_E_FILENOTFOUND;
1730 end:
1731 TRACE("<-- %#lx\n", res);
1732 return res;
1735 /************************************************************************
1736 * StorageBaseImpl_EnumElements (IStorage)
1738 * This method will create an enumerator object that can be used to
1739 * retrieve information about all the elements in the storage object.
1741 * See Windows documentation for more details on IStorage methods.
1743 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1744 IStorage* iface,
1745 DWORD reserved1, /* [in] */
1746 void* reserved2, /* [size_is][unique][in] */
1747 DWORD reserved3, /* [in] */
1748 IEnumSTATSTG** ppenum) /* [out] */
1750 StorageBaseImpl *This = impl_from_IStorage(iface);
1751 IEnumSTATSTGImpl* newEnum;
1753 TRACE("%p, %ld, %p, %ld, %p.\n", iface, reserved1, reserved2, reserved3, ppenum);
1755 if (!ppenum)
1756 return E_INVALIDARG;
1758 if (This->reverted)
1759 return STG_E_REVERTED;
1761 newEnum = IEnumSTATSTGImpl_Construct(
1762 This,
1763 This->storageDirEntry);
1765 if (newEnum)
1767 *ppenum = &newEnum->IEnumSTATSTG_iface;
1768 return S_OK;
1771 return E_OUTOFMEMORY;
1774 /************************************************************************
1775 * StorageBaseImpl_Stat (IStorage)
1777 * This method will retrieve information about this storage object.
1779 * See Windows documentation for more details on IStorage methods.
1781 static HRESULT WINAPI StorageBaseImpl_Stat(
1782 IStorage* iface,
1783 STATSTG* pstatstg, /* [out] */
1784 DWORD grfStatFlag) /* [in] */
1786 StorageBaseImpl *This = impl_from_IStorage(iface);
1787 DirEntry currentEntry;
1788 HRESULT res = STG_E_UNKNOWN;
1790 TRACE("%p, %p, %#lx.\n", iface, pstatstg, grfStatFlag);
1792 if (!pstatstg)
1794 res = E_INVALIDARG;
1795 goto end;
1798 if (This->reverted)
1800 res = STG_E_REVERTED;
1801 goto end;
1804 res = StorageBaseImpl_ReadDirEntry(
1805 This,
1806 This->storageDirEntry,
1807 &currentEntry);
1809 if (SUCCEEDED(res))
1811 StorageUtl_CopyDirEntryToSTATSTG(
1812 This,
1813 pstatstg,
1814 &currentEntry,
1815 grfStatFlag);
1817 pstatstg->grfMode = This->openFlags;
1818 pstatstg->grfStateBits = This->stateBits;
1821 end:
1822 if (res == S_OK)
1824 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.LowPart, pstatstg->cbSize.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
1826 TRACE("<-- %#lx\n", res);
1827 return res;
1830 /************************************************************************
1831 * StorageBaseImpl_RenameElement (IStorage)
1833 * This method will rename the specified element.
1835 * See Windows documentation for more details on IStorage methods.
1837 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1838 IStorage* iface,
1839 const OLECHAR* pwcsOldName, /* [in] */
1840 const OLECHAR* pwcsNewName) /* [in] */
1842 StorageBaseImpl *This = impl_from_IStorage(iface);
1843 DirEntry currentEntry;
1844 DirRef currentEntryRef;
1846 TRACE("(%p, %s, %s)\n",
1847 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1849 if (This->reverted)
1850 return STG_E_REVERTED;
1852 currentEntryRef = findElement(This,
1853 This->storageDirEntry,
1854 pwcsNewName,
1855 &currentEntry);
1857 if (currentEntryRef != DIRENTRY_NULL)
1860 * There is already an element with the new name
1862 return STG_E_FILEALREADYEXISTS;
1866 * Search for the old element name
1868 currentEntryRef = findElement(This,
1869 This->storageDirEntry,
1870 pwcsOldName,
1871 &currentEntry);
1873 if (currentEntryRef != DIRENTRY_NULL)
1875 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1876 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1878 WARN("Element is already open; cannot rename.\n");
1879 return STG_E_ACCESSDENIED;
1882 /* Remove the element from its current position in the tree */
1883 removeFromTree(This, This->storageDirEntry,
1884 currentEntryRef);
1886 /* Change the name of the element */
1887 lstrcpyW(currentEntry.name, pwcsNewName);
1889 /* Delete any sibling links */
1890 currentEntry.leftChild = DIRENTRY_NULL;
1891 currentEntry.rightChild = DIRENTRY_NULL;
1893 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1894 &currentEntry);
1896 /* Insert the element in a new position in the tree */
1897 insertIntoTree(This, This->storageDirEntry,
1898 currentEntryRef);
1900 else
1903 * There is no element with the old name
1905 return STG_E_FILENOTFOUND;
1908 return StorageBaseImpl_Flush(This);
1911 /************************************************************************
1912 * StorageBaseImpl_CreateStream (IStorage)
1914 * This method will create a stream object within this storage
1916 * See Windows documentation for more details on IStorage methods.
1918 static HRESULT WINAPI StorageBaseImpl_CreateStream(
1919 IStorage* iface,
1920 const OLECHAR* pwcsName, /* [string][in] */
1921 DWORD grfMode, /* [in] */
1922 DWORD reserved1, /* [in] */
1923 DWORD reserved2, /* [in] */
1924 IStream** ppstm) /* [out] */
1926 StorageBaseImpl *This = impl_from_IStorage(iface);
1927 StgStreamImpl* newStream;
1928 DirEntry currentEntry, newStreamEntry;
1929 DirRef currentEntryRef, newStreamEntryRef;
1930 HRESULT hr;
1932 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode, reserved1, reserved2, ppstm);
1934 if (ppstm == 0)
1935 return STG_E_INVALIDPOINTER;
1937 if (pwcsName == 0)
1938 return STG_E_INVALIDNAME;
1940 if (reserved1 || reserved2)
1941 return STG_E_INVALIDPARAMETER;
1943 if ( FAILED( validateSTGM(grfMode) ))
1944 return STG_E_INVALIDFLAG;
1946 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1947 return STG_E_INVALIDFLAG;
1949 if (This->reverted)
1950 return STG_E_REVERTED;
1953 * As documented.
1955 if ((grfMode & STGM_DELETEONRELEASE) ||
1956 (grfMode & STGM_TRANSACTED))
1957 return STG_E_INVALIDFUNCTION;
1960 * Don't worry about permissions in transacted mode, as we can always write
1961 * changes; we just can't always commit them.
1963 if(!(This->openFlags & STGM_TRANSACTED)) {
1964 /* Can't create a stream on read-only storage */
1965 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1966 return STG_E_ACCESSDENIED;
1968 /* Can't create a stream with greater access than the parent. */
1969 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1970 return STG_E_ACCESSDENIED;
1973 if(This->openFlags & STGM_SIMPLE)
1974 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1976 *ppstm = 0;
1978 currentEntryRef = findElement(This,
1979 This->storageDirEntry,
1980 pwcsName,
1981 &currentEntry);
1983 if (currentEntryRef != DIRENTRY_NULL)
1986 * An element with this name already exists
1988 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1990 IStorage_DestroyElement(iface, pwcsName);
1992 else
1993 return STG_E_FILEALREADYEXISTS;
1997 * memset the empty entry
1999 memset(&newStreamEntry, 0, sizeof(DirEntry));
2001 newStreamEntry.sizeOfNameString =
2002 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2004 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2005 return STG_E_INVALIDNAME;
2007 lstrcpyW(newStreamEntry.name, pwcsName);
2009 newStreamEntry.stgType = STGTY_STREAM;
2010 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2011 newStreamEntry.size.LowPart = 0;
2012 newStreamEntry.size.HighPart = 0;
2014 newStreamEntry.leftChild = DIRENTRY_NULL;
2015 newStreamEntry.rightChild = DIRENTRY_NULL;
2016 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2018 /* call CoFileTime to get the current time
2019 newStreamEntry.ctime
2020 newStreamEntry.mtime
2023 /* newStreamEntry.clsid */
2026 * Create an entry with the new data
2028 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2029 if (FAILED(hr))
2030 return hr;
2033 * Insert the new entry in the parent storage's tree.
2035 hr = insertIntoTree(
2036 This,
2037 This->storageDirEntry,
2038 newStreamEntryRef);
2039 if (FAILED(hr))
2041 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2042 return hr;
2046 * Open the stream to return it.
2048 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2050 if (newStream)
2052 *ppstm = &newStream->IStream_iface;
2053 IStream_AddRef(*ppstm);
2055 else
2057 return STG_E_INSUFFICIENTMEMORY;
2060 return StorageBaseImpl_Flush(This);
2063 /************************************************************************
2064 * StorageBaseImpl_SetClass (IStorage)
2066 * This method will write the specified CLSID in the directory entry of this
2067 * storage.
2069 * See Windows documentation for more details on IStorage methods.
2071 static HRESULT WINAPI StorageBaseImpl_SetClass(
2072 IStorage* iface,
2073 REFCLSID clsid) /* [in] */
2075 StorageBaseImpl *This = impl_from_IStorage(iface);
2076 HRESULT hRes;
2077 DirEntry currentEntry;
2079 TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid));
2081 if (This->reverted)
2082 return STG_E_REVERTED;
2084 hRes = StorageBaseImpl_ReadDirEntry(This,
2085 This->storageDirEntry,
2086 &currentEntry);
2087 if (SUCCEEDED(hRes))
2089 currentEntry.clsid = *clsid;
2091 hRes = StorageBaseImpl_WriteDirEntry(This,
2092 This->storageDirEntry,
2093 &currentEntry);
2096 if (SUCCEEDED(hRes))
2097 hRes = StorageBaseImpl_Flush(This);
2099 return hRes;
2102 /************************************************************************
2103 * StorageBaseImpl_CreateStorage (IStorage)
2105 * This method will create the storage object within the provided storage.
2107 * See Windows documentation for more details on IStorage methods.
2109 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
2110 IStorage* iface,
2111 const OLECHAR *pwcsName, /* [string][in] */
2112 DWORD grfMode, /* [in] */
2113 DWORD reserved1, /* [in] */
2114 DWORD reserved2, /* [in] */
2115 IStorage **ppstg) /* [out] */
2117 StorageBaseImpl* This = impl_from_IStorage(iface);
2119 DirEntry currentEntry;
2120 DirEntry newEntry;
2121 DirRef currentEntryRef;
2122 DirRef newEntryRef;
2123 HRESULT hr;
2125 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode,
2126 reserved1, reserved2, ppstg);
2128 if (ppstg == 0)
2129 return STG_E_INVALIDPOINTER;
2131 if (This->openFlags & STGM_SIMPLE)
2133 return STG_E_INVALIDFUNCTION;
2136 if (pwcsName == 0)
2137 return STG_E_INVALIDNAME;
2139 *ppstg = NULL;
2141 if ( FAILED( validateSTGM(grfMode) ) ||
2142 (grfMode & STGM_DELETEONRELEASE) )
2144 WARN("bad grfMode: %#lx\n", grfMode);
2145 return STG_E_INVALIDFLAG;
2148 if (This->reverted)
2149 return STG_E_REVERTED;
2152 * Check that we're compatible with the parent's storage mode
2154 if ( !(This->openFlags & STGM_TRANSACTED) &&
2155 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2157 WARN("access denied\n");
2158 return STG_E_ACCESSDENIED;
2161 currentEntryRef = findElement(This,
2162 This->storageDirEntry,
2163 pwcsName,
2164 &currentEntry);
2166 if (currentEntryRef != DIRENTRY_NULL)
2169 * An element with this name already exists
2171 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2172 ((This->openFlags & STGM_TRANSACTED) ||
2173 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2175 hr = IStorage_DestroyElement(iface, pwcsName);
2176 if (FAILED(hr))
2177 return hr;
2179 else
2181 WARN("file already exists\n");
2182 return STG_E_FILEALREADYEXISTS;
2185 else if (!(This->openFlags & STGM_TRANSACTED) &&
2186 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2188 WARN("read-only storage\n");
2189 return STG_E_ACCESSDENIED;
2192 memset(&newEntry, 0, sizeof(DirEntry));
2194 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2196 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2198 FIXME("name too long\n");
2199 return STG_E_INVALIDNAME;
2202 lstrcpyW(newEntry.name, pwcsName);
2204 newEntry.stgType = STGTY_STORAGE;
2205 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2206 newEntry.size.LowPart = 0;
2207 newEntry.size.HighPart = 0;
2209 newEntry.leftChild = DIRENTRY_NULL;
2210 newEntry.rightChild = DIRENTRY_NULL;
2211 newEntry.dirRootEntry = DIRENTRY_NULL;
2213 /* call CoFileTime to get the current time
2214 newEntry.ctime
2215 newEntry.mtime
2218 /* newEntry.clsid */
2221 * Create a new directory entry for the storage
2223 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2224 if (FAILED(hr))
2225 return hr;
2228 * Insert the new directory entry into the parent storage's tree
2230 hr = insertIntoTree(
2231 This,
2232 This->storageDirEntry,
2233 newEntryRef);
2234 if (FAILED(hr))
2236 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2237 return hr;
2241 * Open it to get a pointer to return.
2243 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2245 if( (hr != S_OK) || (*ppstg == NULL))
2247 return hr;
2250 if (SUCCEEDED(hr))
2251 hr = StorageBaseImpl_Flush(This);
2253 return S_OK;
2256 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2257 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2258 SNB snbExclude, IStorage *pstgDest)
2260 DirEntry data;
2261 HRESULT hr;
2263 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2265 if (SUCCEEDED(hr))
2266 hr = IStorage_SetClass( pstgDest, &data.clsid );
2268 if (SUCCEEDED(hr))
2269 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2270 skip_stream, snbExclude, pstgDest );
2272 TRACE("<-- %#lx\n", hr);
2273 return hr;
2276 /*************************************************************************
2277 * CopyTo (IStorage)
2279 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2280 IStorage* iface,
2281 DWORD ciidExclude, /* [in] */
2282 const IID* rgiidExclude, /* [size_is][unique][in] */
2283 SNB snbExclude, /* [unique][in] */
2284 IStorage* pstgDest) /* [unique][in] */
2286 StorageBaseImpl *This = impl_from_IStorage(iface);
2288 BOOL skip_storage = FALSE, skip_stream = FALSE;
2289 DWORD i;
2291 TRACE("%p, %ld, %p, %p, %p.\n", iface, ciidExclude, rgiidExclude, snbExclude, pstgDest);
2293 if ( pstgDest == 0 )
2294 return STG_E_INVALIDPOINTER;
2296 for(i = 0; i < ciidExclude; ++i)
2298 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2299 skip_storage = TRUE;
2300 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2301 skip_stream = TRUE;
2302 else
2303 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2306 if (!skip_storage)
2308 /* Give up early if it looks like this would be infinitely recursive.
2309 * Oddly enough, this includes some cases that aren't really recursive, like
2310 * copying to a transacted child. */
2311 IStorage *pstgDestAncestor = pstgDest;
2312 IStorage *pstgDestAncestorChild = NULL;
2314 /* Go up the chain from the destination until we find the source storage. */
2315 while (pstgDestAncestor != iface) {
2316 pstgDestAncestorChild = pstgDest;
2318 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2320 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2322 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2324 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2326 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2328 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2330 else
2331 break;
2334 if (pstgDestAncestor == iface)
2336 BOOL fail = TRUE;
2338 if (pstgDestAncestorChild && snbExclude)
2340 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2341 DirEntry data;
2342 WCHAR **snb = snbExclude;
2344 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2346 while ( *snb != NULL && fail )
2348 if ( wcscmp(data.name, *snb) == 0 )
2349 fail = FALSE;
2350 ++snb;
2354 if (fail)
2355 return STG_E_ACCESSDENIED;
2359 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2360 skip_storage, skip_stream, snbExclude, pstgDest );
2363 /*************************************************************************
2364 * MoveElementTo (IStorage)
2366 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2367 IStorage* iface,
2368 const OLECHAR *pwcsName, /* [string][in] */
2369 IStorage *pstgDest, /* [unique][in] */
2370 const OLECHAR *pwcsNewName,/* [string][in] */
2371 DWORD grfFlags) /* [in] */
2373 FIXME("%p, %s, %p, %s, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest,
2374 debugstr_w(pwcsNewName), grfFlags);
2375 return E_NOTIMPL;
2378 /*************************************************************************
2379 * Commit (IStorage)
2381 * Ensures that any changes made to a storage object open in transacted mode
2382 * are reflected in the parent storage
2384 * In a non-transacted mode, this ensures all cached writes are completed.
2386 static HRESULT WINAPI StorageBaseImpl_Commit(
2387 IStorage* iface,
2388 DWORD grfCommitFlags)/* [in] */
2390 StorageBaseImpl* This = impl_from_IStorage(iface);
2391 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
2392 return StorageBaseImpl_Flush(This);
2395 /*************************************************************************
2396 * Revert (IStorage)
2398 * Discard all changes that have been made since the last commit operation
2400 static HRESULT WINAPI StorageBaseImpl_Revert(
2401 IStorage* iface)
2403 TRACE("(%p)\n", iface);
2404 return S_OK;
2407 /*********************************************************************
2409 * Internal helper function for StorageBaseImpl_DestroyElement()
2411 * Delete the contents of a storage entry.
2414 static HRESULT deleteStorageContents(
2415 StorageBaseImpl *parentStorage,
2416 DirRef indexToDelete,
2417 DirEntry entryDataToDelete)
2419 IEnumSTATSTG *elements = 0;
2420 IStorage *childStorage = 0;
2421 STATSTG currentElement;
2422 HRESULT hr;
2423 HRESULT destroyHr = S_OK;
2424 StorageInternalImpl *stg, *stg2;
2426 TRACE("%p, %ld.\n", parentStorage, indexToDelete);
2428 /* Invalidate any open storage objects. */
2429 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2431 if (stg->base.storageDirEntry == indexToDelete)
2433 StorageBaseImpl_Invalidate(&stg->base);
2438 * Open the storage and enumerate it
2440 hr = IStorage_OpenStorage(
2441 &parentStorage->IStorage_iface,
2442 entryDataToDelete.name,
2444 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2447 &childStorage);
2449 if (hr != S_OK)
2451 TRACE("<-- %#lx\n", hr);
2452 return hr;
2456 * Enumerate the elements
2458 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2459 if (FAILED(hr))
2461 IStorage_Release(childStorage);
2462 TRACE("<-- %#lx\n", hr);
2463 return hr;
2469 * Obtain the next element
2471 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2472 if (hr==S_OK)
2474 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2476 CoTaskMemFree(currentElement.pwcsName);
2480 * We need to Reset the enumeration every time because we delete elements
2481 * and the enumeration could be invalid
2483 IEnumSTATSTG_Reset(elements);
2485 } while ((hr == S_OK) && (destroyHr == S_OK));
2487 IStorage_Release(childStorage);
2488 IEnumSTATSTG_Release(elements);
2490 TRACE("%#lx\n", hr);
2491 return destroyHr;
2494 /*********************************************************************
2496 * Internal helper function for StorageBaseImpl_DestroyElement()
2498 * Perform the deletion of a stream's data
2501 static HRESULT deleteStreamContents(
2502 StorageBaseImpl *parentStorage,
2503 DirRef indexToDelete,
2504 DirEntry entryDataToDelete)
2506 IStream *pis;
2507 HRESULT hr;
2508 ULARGE_INTEGER size;
2509 StgStreamImpl *strm, *strm2;
2511 /* Invalidate any open stream objects. */
2512 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2514 if (strm->dirEntry == indexToDelete)
2516 TRACE("Stream deleted %p\n", strm);
2517 strm->parentStorage = NULL;
2518 list_remove(&strm->StrmListEntry);
2522 size.HighPart = 0;
2523 size.LowPart = 0;
2525 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2526 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2528 if (hr!=S_OK)
2530 TRACE("<-- %#lx\n", hr);
2531 return(hr);
2535 * Zap the stream
2537 hr = IStream_SetSize(pis, size);
2539 if(hr != S_OK)
2541 TRACE("<-- %#lx\n", hr);
2542 return hr;
2546 * Release the stream object.
2548 IStream_Release(pis);
2549 TRACE("<-- %#lx\n", hr);
2550 return S_OK;
2553 /*************************************************************************
2554 * DestroyElement (IStorage)
2556 * Strategy: This implementation is built this way for simplicity not for speed.
2557 * I always delete the topmost element of the enumeration and adjust
2558 * the deleted element pointer all the time. This takes longer to
2559 * do but allows reinvoking DestroyElement whenever we encounter a
2560 * storage object. The optimisation resides in the usage of another
2561 * enumeration strategy that would give all the leaves of a storage
2562 * first. (postfix order)
2564 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2565 IStorage* iface,
2566 const OLECHAR *pwcsName)/* [string][in] */
2568 StorageBaseImpl *This = impl_from_IStorage(iface);
2570 HRESULT hr = S_OK;
2571 DirEntry entryToDelete;
2572 DirRef entryToDeleteRef;
2574 TRACE("(%p, %s)\n",
2575 iface, debugstr_w(pwcsName));
2577 if (pwcsName==NULL)
2578 return STG_E_INVALIDPOINTER;
2580 if (This->reverted)
2581 return STG_E_REVERTED;
2583 if ( !(This->openFlags & STGM_TRANSACTED) &&
2584 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2585 return STG_E_ACCESSDENIED;
2587 entryToDeleteRef = findElement(
2588 This,
2589 This->storageDirEntry,
2590 pwcsName,
2591 &entryToDelete);
2593 if ( entryToDeleteRef == DIRENTRY_NULL )
2595 TRACE("<-- STG_E_FILENOTFOUND\n");
2596 return STG_E_FILENOTFOUND;
2599 if ( entryToDelete.stgType == STGTY_STORAGE )
2601 hr = deleteStorageContents(
2602 This,
2603 entryToDeleteRef,
2604 entryToDelete);
2606 else if ( entryToDelete.stgType == STGTY_STREAM )
2608 hr = deleteStreamContents(
2609 This,
2610 entryToDeleteRef,
2611 entryToDelete);
2614 if (hr!=S_OK)
2616 TRACE("<-- %#lx\n", hr);
2617 return hr;
2621 * Remove the entry from its parent storage
2623 hr = removeFromTree(
2624 This,
2625 This->storageDirEntry,
2626 entryToDeleteRef);
2629 * Invalidate the entry
2631 if (SUCCEEDED(hr))
2632 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2634 if (SUCCEEDED(hr))
2635 hr = StorageBaseImpl_Flush(This);
2637 TRACE("<-- %#lx\n", hr);
2638 return hr;
2641 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2643 struct list *cur, *cur2;
2644 StgStreamImpl *strm=NULL;
2645 StorageInternalImpl *childstg=NULL;
2647 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2648 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2649 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2650 strm->parentStorage = NULL;
2651 list_remove(cur);
2654 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2655 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2656 StorageBaseImpl_Invalidate( &childstg->base );
2659 if (stg->transactedChild)
2661 StorageBaseImpl_Invalidate(stg->transactedChild);
2663 stg->transactedChild = NULL;
2667 /******************************************************************************
2668 * SetElementTimes (IStorage)
2670 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2671 IStorage* iface,
2672 const OLECHAR *pwcsName,/* [string][in] */
2673 const FILETIME *pctime, /* [in] */
2674 const FILETIME *patime, /* [in] */
2675 const FILETIME *pmtime) /* [in] */
2677 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2678 return S_OK;
2681 /******************************************************************************
2682 * SetStateBits (IStorage)
2684 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2685 IStorage* iface,
2686 DWORD grfStateBits,/* [in] */
2687 DWORD grfMask) /* [in] */
2689 StorageBaseImpl *This = impl_from_IStorage(iface);
2691 if (This->reverted)
2692 return STG_E_REVERTED;
2694 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2695 return S_OK;
2698 /******************************************************************************
2699 * Internal stream list handlers
2702 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2704 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2705 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2708 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2710 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2711 list_remove(&(strm->StrmListEntry));
2714 static HRESULT StorageBaseImpl_CopyStream(
2715 StorageBaseImpl *dst, DirRef dst_entry,
2716 StorageBaseImpl *src, DirRef src_entry)
2718 HRESULT hr;
2719 BYTE data[4096];
2720 DirEntry srcdata;
2721 ULARGE_INTEGER bytes_copied;
2722 ULONG bytestocopy, bytesread, byteswritten;
2724 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2726 if (SUCCEEDED(hr))
2728 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2730 bytes_copied.QuadPart = 0;
2731 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2733 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2735 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2736 data, &bytesread);
2737 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2739 if (SUCCEEDED(hr))
2740 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2741 data, &byteswritten);
2742 if (SUCCEEDED(hr))
2744 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2745 bytes_copied.QuadPart += byteswritten;
2750 return hr;
2753 static HRESULT StorageBaseImpl_DupStorageTree(
2754 StorageBaseImpl *dst, DirRef *dst_entry,
2755 StorageBaseImpl *src, DirRef src_entry)
2757 HRESULT hr;
2758 DirEntry data;
2759 BOOL has_stream=FALSE;
2761 if (src_entry == DIRENTRY_NULL)
2763 *dst_entry = DIRENTRY_NULL;
2764 return S_OK;
2767 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2768 if (SUCCEEDED(hr))
2770 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2771 data.startingBlock = BLOCK_END_OF_CHAIN;
2772 data.size.QuadPart = 0;
2774 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2777 if (SUCCEEDED(hr))
2778 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2780 if (SUCCEEDED(hr))
2781 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2783 if (SUCCEEDED(hr))
2784 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2786 if (SUCCEEDED(hr) && has_stream)
2787 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2789 return hr;
2792 static HRESULT StorageBaseImpl_CopyStorageTree(
2793 StorageBaseImpl *dst, DirRef dst_entry,
2794 StorageBaseImpl *src, DirRef src_entry)
2796 HRESULT hr;
2797 DirEntry src_data, dst_data;
2798 DirRef new_root_entry;
2800 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2802 if (SUCCEEDED(hr))
2804 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2807 if (SUCCEEDED(hr))
2809 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
2810 dst_data.clsid = src_data.clsid;
2811 dst_data.ctime = src_data.ctime;
2812 dst_data.mtime = src_data.mtime;
2813 dst_data.dirRootEntry = new_root_entry;
2816 if (SUCCEEDED(hr))
2817 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2819 return hr;
2822 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2824 HRESULT hr;
2825 DirEntry data;
2826 ULARGE_INTEGER zero;
2828 if (entry == DIRENTRY_NULL)
2829 return S_OK;
2831 zero.QuadPart = 0;
2833 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2835 if (SUCCEEDED(hr) && include_siblings)
2836 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2838 if (SUCCEEDED(hr) && include_siblings)
2839 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2841 if (SUCCEEDED(hr))
2842 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2844 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2845 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2847 if (SUCCEEDED(hr))
2848 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2850 return hr;
2854 /************************************************************************
2855 * StorageImpl implementation
2856 ***********************************************************************/
2858 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2859 ULARGE_INTEGER offset,
2860 void* buffer,
2861 ULONG size,
2862 ULONG* bytesRead)
2864 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2867 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2868 ULARGE_INTEGER offset,
2869 const void* buffer,
2870 const ULONG size,
2871 ULONG* bytesWritten)
2873 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2876 /******************************************************************************
2877 * StorageImpl_LoadFileHeader
2879 * This method will read in the file header
2881 static HRESULT StorageImpl_LoadFileHeader(
2882 StorageImpl* This)
2884 HRESULT hr;
2885 BYTE headerBigBlock[HEADER_SIZE];
2886 int index;
2887 ULARGE_INTEGER offset;
2888 DWORD bytes_read;
2890 TRACE("\n");
2892 * Get a pointer to the big block of data containing the header.
2894 offset.HighPart = 0;
2895 offset.LowPart = 0;
2896 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2897 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2898 hr = STG_E_FILENOTFOUND;
2901 * Extract the information from the header.
2903 if (SUCCEEDED(hr))
2906 * Check for the "magic number" signature and return an error if it is not
2907 * found.
2909 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2911 return STG_E_OLDFORMAT;
2914 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2916 return STG_E_INVALIDHEADER;
2919 StorageUtl_ReadWord(
2920 headerBigBlock,
2921 OFFSET_BIGBLOCKSIZEBITS,
2922 &This->bigBlockSizeBits);
2924 StorageUtl_ReadWord(
2925 headerBigBlock,
2926 OFFSET_SMALLBLOCKSIZEBITS,
2927 &This->smallBlockSizeBits);
2929 StorageUtl_ReadDWord(
2930 headerBigBlock,
2931 OFFSET_BBDEPOTCOUNT,
2932 &This->bigBlockDepotCount);
2934 StorageUtl_ReadDWord(
2935 headerBigBlock,
2936 OFFSET_ROOTSTARTBLOCK,
2937 &This->rootStartBlock);
2939 StorageUtl_ReadDWord(
2940 headerBigBlock,
2941 OFFSET_TRANSACTIONSIG,
2942 &This->transactionSig);
2944 StorageUtl_ReadDWord(
2945 headerBigBlock,
2946 OFFSET_SMALLBLOCKLIMIT,
2947 &This->smallBlockLimit);
2949 StorageUtl_ReadDWord(
2950 headerBigBlock,
2951 OFFSET_SBDEPOTSTART,
2952 &This->smallBlockDepotStart);
2954 StorageUtl_ReadDWord(
2955 headerBigBlock,
2956 OFFSET_EXTBBDEPOTSTART,
2957 &This->extBigBlockDepotStart);
2959 StorageUtl_ReadDWord(
2960 headerBigBlock,
2961 OFFSET_EXTBBDEPOTCOUNT,
2962 &This->extBigBlockDepotCount);
2964 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2966 StorageUtl_ReadDWord(
2967 headerBigBlock,
2968 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2969 &(This->bigBlockDepotStart[index]));
2973 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2975 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2976 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2979 * Right now, the code is making some assumptions about the size of the
2980 * blocks, just make sure they are what we're expecting.
2982 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2983 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2984 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2986 FIXME("Broken OLE storage file? bigblock=%#lx, smallblock=%#lx, sblimit=%#lx\n",
2987 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
2988 hr = STG_E_INVALIDHEADER;
2990 else
2991 hr = S_OK;
2994 return hr;
2997 /******************************************************************************
2998 * StorageImpl_SaveFileHeader
3000 * This method will save to the file the header
3002 static void StorageImpl_SaveFileHeader(
3003 StorageImpl* This)
3005 BYTE headerBigBlock[HEADER_SIZE];
3006 int index;
3007 ULARGE_INTEGER offset;
3008 DWORD bytes_written;
3009 DWORD major_version, dirsectorcount;
3011 if (This->bigBlockSizeBits == 0x9)
3012 major_version = 3;
3013 else if (This->bigBlockSizeBits == 0xc)
3014 major_version = 4;
3015 else
3017 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3018 major_version = 4;
3021 memset(headerBigBlock, 0, HEADER_SIZE);
3022 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3025 * Write the information to the header.
3027 StorageUtl_WriteWord(
3028 headerBigBlock,
3029 OFFSET_MINORVERSION,
3030 0x3e);
3032 StorageUtl_WriteWord(
3033 headerBigBlock,
3034 OFFSET_MAJORVERSION,
3035 major_version);
3037 StorageUtl_WriteWord(
3038 headerBigBlock,
3039 OFFSET_BYTEORDERMARKER,
3040 (WORD)-2);
3042 StorageUtl_WriteWord(
3043 headerBigBlock,
3044 OFFSET_BIGBLOCKSIZEBITS,
3045 This->bigBlockSizeBits);
3047 StorageUtl_WriteWord(
3048 headerBigBlock,
3049 OFFSET_SMALLBLOCKSIZEBITS,
3050 This->smallBlockSizeBits);
3052 if (major_version >= 4)
3054 if (This->rootBlockChain)
3055 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3056 else
3057 /* This file is being created, and it will start out with one block. */
3058 dirsectorcount = 1;
3060 else
3061 /* This field must be 0 in versions older than 4 */
3062 dirsectorcount = 0;
3064 StorageUtl_WriteDWord(
3065 headerBigBlock,
3066 OFFSET_DIRSECTORCOUNT,
3067 dirsectorcount);
3069 StorageUtl_WriteDWord(
3070 headerBigBlock,
3071 OFFSET_BBDEPOTCOUNT,
3072 This->bigBlockDepotCount);
3074 StorageUtl_WriteDWord(
3075 headerBigBlock,
3076 OFFSET_ROOTSTARTBLOCK,
3077 This->rootStartBlock);
3079 StorageUtl_WriteDWord(
3080 headerBigBlock,
3081 OFFSET_TRANSACTIONSIG,
3082 This->transactionSig);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_SMALLBLOCKLIMIT,
3087 This->smallBlockLimit);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_SBDEPOTSTART,
3092 This->smallBlockDepotStart);
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_SBDEPOTCOUNT,
3097 This->smallBlockDepotChain ?
3098 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3100 StorageUtl_WriteDWord(
3101 headerBigBlock,
3102 OFFSET_EXTBBDEPOTSTART,
3103 This->extBigBlockDepotStart);
3105 StorageUtl_WriteDWord(
3106 headerBigBlock,
3107 OFFSET_EXTBBDEPOTCOUNT,
3108 This->extBigBlockDepotCount);
3110 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3112 StorageUtl_WriteDWord(
3113 headerBigBlock,
3114 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3115 (This->bigBlockDepotStart[index]));
3118 offset.QuadPart = 0;
3119 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3123 /************************************************************************
3124 * StorageImpl implementation : DirEntry methods
3125 ***********************************************************************/
3127 /******************************************************************************
3128 * StorageImpl_ReadRawDirEntry
3130 * This method will read the raw data from a directory entry in the file.
3132 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3134 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3136 ULARGE_INTEGER offset;
3137 HRESULT hr;
3138 ULONG bytesRead;
3140 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3142 hr = BlockChainStream_ReadAt(
3143 This->rootBlockChain,
3144 offset,
3145 RAW_DIRENTRY_SIZE,
3146 buffer,
3147 &bytesRead);
3149 if (bytesRead != RAW_DIRENTRY_SIZE)
3150 return STG_E_READFAULT;
3152 return hr;
3155 /******************************************************************************
3156 * StorageImpl_WriteRawDirEntry
3158 * This method will write the raw data from a directory entry in the file.
3160 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3162 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3164 ULARGE_INTEGER offset;
3165 ULONG bytesRead;
3167 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3169 return BlockChainStream_WriteAt(
3170 This->rootBlockChain,
3171 offset,
3172 RAW_DIRENTRY_SIZE,
3173 buffer,
3174 &bytesRead);
3177 /***************************************************************************
3179 * Internal Method
3181 * Mark a directory entry in the file as free.
3183 static HRESULT StorageImpl_DestroyDirEntry(
3184 StorageBaseImpl *base,
3185 DirRef index)
3187 BYTE emptyData[RAW_DIRENTRY_SIZE];
3188 StorageImpl *storage = (StorageImpl*)base;
3190 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3192 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3195 /******************************************************************************
3196 * UpdateRawDirEntry
3198 * Update raw directory entry data from the fields in newData.
3200 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3202 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3204 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3206 memcpy(
3207 buffer + OFFSET_PS_NAME,
3208 newData->name,
3209 DIRENTRY_NAME_BUFFER_LEN );
3211 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3213 StorageUtl_WriteWord(
3214 buffer,
3215 OFFSET_PS_NAMELENGTH,
3216 newData->sizeOfNameString);
3218 StorageUtl_WriteDWord(
3219 buffer,
3220 OFFSET_PS_LEFTCHILD,
3221 newData->leftChild);
3223 StorageUtl_WriteDWord(
3224 buffer,
3225 OFFSET_PS_RIGHTCHILD,
3226 newData->rightChild);
3228 StorageUtl_WriteDWord(
3229 buffer,
3230 OFFSET_PS_DIRROOT,
3231 newData->dirRootEntry);
3233 StorageUtl_WriteGUID(
3234 buffer,
3235 OFFSET_PS_GUID,
3236 &newData->clsid);
3238 StorageUtl_WriteDWord(
3239 buffer,
3240 OFFSET_PS_CTIMELOW,
3241 newData->ctime.dwLowDateTime);
3243 StorageUtl_WriteDWord(
3244 buffer,
3245 OFFSET_PS_CTIMEHIGH,
3246 newData->ctime.dwHighDateTime);
3248 StorageUtl_WriteDWord(
3249 buffer,
3250 OFFSET_PS_MTIMELOW,
3251 newData->mtime.dwLowDateTime);
3253 StorageUtl_WriteDWord(
3254 buffer,
3255 OFFSET_PS_MTIMEHIGH,
3256 newData->ctime.dwHighDateTime);
3258 StorageUtl_WriteDWord(
3259 buffer,
3260 OFFSET_PS_STARTBLOCK,
3261 newData->startingBlock);
3263 StorageUtl_WriteDWord(
3264 buffer,
3265 OFFSET_PS_SIZE,
3266 newData->size.LowPart);
3268 StorageUtl_WriteDWord(
3269 buffer,
3270 OFFSET_PS_SIZE_HIGH,
3271 newData->size.HighPart);
3274 /***************************************************************************
3276 * Internal Method
3278 * Reserve a directory entry in the file and initialize it.
3280 static HRESULT StorageImpl_CreateDirEntry(
3281 StorageBaseImpl *base,
3282 const DirEntry *newData,
3283 DirRef *index)
3285 StorageImpl *storage = (StorageImpl*)base;
3286 ULONG currentEntryIndex = 0;
3287 ULONG newEntryIndex = DIRENTRY_NULL;
3288 HRESULT hr = S_OK;
3289 BYTE currentData[RAW_DIRENTRY_SIZE];
3290 WORD sizeOfNameString;
3294 hr = StorageImpl_ReadRawDirEntry(storage,
3295 currentEntryIndex,
3296 currentData);
3298 if (SUCCEEDED(hr))
3300 StorageUtl_ReadWord(
3301 currentData,
3302 OFFSET_PS_NAMELENGTH,
3303 &sizeOfNameString);
3305 if (sizeOfNameString == 0)
3308 * The entry exists and is available, we found it.
3310 newEntryIndex = currentEntryIndex;
3313 else
3316 * We exhausted the directory entries, we will create more space below
3318 newEntryIndex = currentEntryIndex;
3320 currentEntryIndex++;
3322 } while (newEntryIndex == DIRENTRY_NULL);
3325 * grow the directory stream
3327 if (FAILED(hr))
3329 BYTE emptyData[RAW_DIRENTRY_SIZE];
3330 ULARGE_INTEGER newSize;
3331 ULONG entryIndex;
3332 ULONG lastEntry = 0;
3333 ULONG blockCount = 0;
3336 * obtain the new count of blocks in the directory stream
3338 blockCount = BlockChainStream_GetCount(
3339 storage->rootBlockChain)+1;
3342 * initialize the size used by the directory stream
3344 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3347 * add a block to the directory stream
3349 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3352 * memset the empty entry in order to initialize the unused newly
3353 * created entries
3355 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3358 * initialize them
3360 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3362 for(
3363 entryIndex = newEntryIndex + 1;
3364 entryIndex < lastEntry;
3365 entryIndex++)
3367 StorageImpl_WriteRawDirEntry(
3368 storage,
3369 entryIndex,
3370 emptyData);
3373 StorageImpl_SaveFileHeader(storage);
3376 UpdateRawDirEntry(currentData, newData);
3378 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3380 if (SUCCEEDED(hr))
3381 *index = newEntryIndex;
3383 return hr;
3386 /******************************************************************************
3387 * StorageImpl_ReadDirEntry
3389 * This method will read the specified directory entry.
3391 static HRESULT StorageImpl_ReadDirEntry(
3392 StorageImpl* This,
3393 DirRef index,
3394 DirEntry* buffer)
3396 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3397 HRESULT readRes;
3399 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3401 if (SUCCEEDED(readRes))
3403 memset(buffer->name, 0, sizeof(buffer->name));
3404 memcpy(
3405 buffer->name,
3406 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3407 DIRENTRY_NAME_BUFFER_LEN );
3408 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3410 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3412 StorageUtl_ReadWord(
3413 currentEntry,
3414 OFFSET_PS_NAMELENGTH,
3415 &buffer->sizeOfNameString);
3417 StorageUtl_ReadDWord(
3418 currentEntry,
3419 OFFSET_PS_LEFTCHILD,
3420 &buffer->leftChild);
3422 StorageUtl_ReadDWord(
3423 currentEntry,
3424 OFFSET_PS_RIGHTCHILD,
3425 &buffer->rightChild);
3427 StorageUtl_ReadDWord(
3428 currentEntry,
3429 OFFSET_PS_DIRROOT,
3430 &buffer->dirRootEntry);
3432 StorageUtl_ReadGUID(
3433 currentEntry,
3434 OFFSET_PS_GUID,
3435 &buffer->clsid);
3437 StorageUtl_ReadDWord(
3438 currentEntry,
3439 OFFSET_PS_CTIMELOW,
3440 &buffer->ctime.dwLowDateTime);
3442 StorageUtl_ReadDWord(
3443 currentEntry,
3444 OFFSET_PS_CTIMEHIGH,
3445 &buffer->ctime.dwHighDateTime);
3447 StorageUtl_ReadDWord(
3448 currentEntry,
3449 OFFSET_PS_MTIMELOW,
3450 &buffer->mtime.dwLowDateTime);
3452 StorageUtl_ReadDWord(
3453 currentEntry,
3454 OFFSET_PS_MTIMEHIGH,
3455 &buffer->mtime.dwHighDateTime);
3457 StorageUtl_ReadDWord(
3458 currentEntry,
3459 OFFSET_PS_STARTBLOCK,
3460 &buffer->startingBlock);
3462 StorageUtl_ReadDWord(
3463 currentEntry,
3464 OFFSET_PS_SIZE,
3465 &buffer->size.LowPart);
3467 if (This->bigBlockSize < 4096)
3469 /* Version 3 files may have junk in the high part of size. */
3470 buffer->size.HighPart = 0;
3472 else
3474 StorageUtl_ReadDWord(
3475 currentEntry,
3476 OFFSET_PS_SIZE_HIGH,
3477 &buffer->size.HighPart);
3481 return readRes;
3484 /*********************************************************************
3485 * Write the specified directory entry to the file
3487 static HRESULT StorageImpl_WriteDirEntry(
3488 StorageImpl* This,
3489 DirRef index,
3490 const DirEntry* buffer)
3492 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3494 UpdateRawDirEntry(currentEntry, buffer);
3496 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3500 /************************************************************************
3501 * StorageImpl implementation : Block methods
3502 ***********************************************************************/
3504 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3506 return (ULONGLONG)(index+1) * This->bigBlockSize;
3509 static HRESULT StorageImpl_ReadBigBlock(
3510 StorageImpl* This,
3511 ULONG blockIndex,
3512 void* buffer,
3513 ULONG* out_read)
3515 ULARGE_INTEGER ulOffset;
3516 DWORD read=0;
3517 HRESULT hr;
3519 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3521 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3523 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3525 /* File ends during this block; fill the rest with 0's. */
3526 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3529 if (out_read) *out_read = read;
3531 return hr;
3534 static BOOL StorageImpl_ReadDWordFromBigBlock(
3535 StorageImpl* This,
3536 ULONG blockIndex,
3537 ULONG offset,
3538 DWORD* value)
3540 ULARGE_INTEGER ulOffset;
3541 DWORD read;
3542 DWORD tmp;
3544 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3545 ulOffset.QuadPart += offset;
3547 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3548 *value = lendian32toh(tmp);
3549 return (read == sizeof(DWORD));
3552 static BOOL StorageImpl_WriteBigBlock(
3553 StorageImpl* This,
3554 ULONG blockIndex,
3555 const void* buffer)
3557 ULARGE_INTEGER ulOffset;
3558 DWORD wrote;
3560 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3562 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3563 return (wrote == This->bigBlockSize);
3566 static BOOL StorageImpl_WriteDWordToBigBlock(
3567 StorageImpl* This,
3568 ULONG blockIndex,
3569 ULONG offset,
3570 DWORD value)
3572 ULARGE_INTEGER ulOffset;
3573 DWORD wrote;
3575 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3576 ulOffset.QuadPart += offset;
3578 value = htole32(value);
3579 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3580 return (wrote == sizeof(DWORD));
3583 /******************************************************************************
3584 * Storage32Impl_SmallBlocksToBigBlocks
3586 * This method will convert a small block chain to a big block chain.
3587 * The small block chain will be destroyed.
3589 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3590 StorageImpl* This,
3591 SmallBlockChainStream** ppsbChain)
3593 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3594 ULARGE_INTEGER size, offset;
3595 ULONG cbRead, cbWritten;
3596 ULARGE_INTEGER cbTotalRead;
3597 DirRef streamEntryRef;
3598 HRESULT resWrite = S_OK;
3599 HRESULT resRead;
3600 DirEntry streamEntry;
3601 BYTE *buffer;
3602 BlockChainStream *bbTempChain = NULL;
3603 BlockChainStream *bigBlockChain = NULL;
3606 * Create a temporary big block chain that doesn't have
3607 * an associated directory entry. This temporary chain will be
3608 * used to copy data from small blocks to big blocks.
3610 bbTempChain = BlockChainStream_Construct(This,
3611 &bbHeadOfChain,
3612 DIRENTRY_NULL);
3613 if(!bbTempChain) return NULL;
3615 * Grow the big block chain.
3617 size = SmallBlockChainStream_GetSize(*ppsbChain);
3618 BlockChainStream_SetSize(bbTempChain, size);
3621 * Copy the contents of the small block chain to the big block chain
3622 * by small block size increments.
3624 offset.LowPart = 0;
3625 offset.HighPart = 0;
3626 cbTotalRead.QuadPart = 0;
3628 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3631 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3632 offset,
3633 min(This->smallBlockSize, size.LowPart - offset.LowPart),
3634 buffer,
3635 &cbRead);
3636 if (FAILED(resRead))
3637 break;
3639 if (cbRead > 0)
3641 cbTotalRead.QuadPart += cbRead;
3643 resWrite = BlockChainStream_WriteAt(bbTempChain,
3644 offset,
3645 cbRead,
3646 buffer,
3647 &cbWritten);
3649 if (FAILED(resWrite))
3650 break;
3652 offset.LowPart += cbRead;
3654 else
3656 resRead = STG_E_READFAULT;
3657 break;
3659 } while (cbTotalRead.QuadPart < size.QuadPart);
3660 HeapFree(GetProcessHeap(),0,buffer);
3662 size.HighPart = 0;
3663 size.LowPart = 0;
3665 if (FAILED(resRead) || FAILED(resWrite))
3667 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite);
3668 BlockChainStream_SetSize(bbTempChain, size);
3669 BlockChainStream_Destroy(bbTempChain);
3670 return NULL;
3674 * Destroy the small block chain.
3676 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3677 SmallBlockChainStream_SetSize(*ppsbChain, size);
3678 SmallBlockChainStream_Destroy(*ppsbChain);
3679 *ppsbChain = 0;
3682 * Change the directory entry. This chain is now a big block chain
3683 * and it doesn't reside in the small blocks chain anymore.
3685 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3687 streamEntry.startingBlock = bbHeadOfChain;
3689 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3692 * Destroy the temporary entryless big block chain.
3693 * Create a new big block chain associated with this entry.
3695 BlockChainStream_Destroy(bbTempChain);
3696 bigBlockChain = BlockChainStream_Construct(This,
3697 NULL,
3698 streamEntryRef);
3700 return bigBlockChain;
3703 /******************************************************************************
3704 * Storage32Impl_BigBlocksToSmallBlocks
3706 * This method will convert a big block chain to a small block chain.
3707 * The big block chain will be destroyed on success.
3709 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3710 StorageImpl* This,
3711 BlockChainStream** ppbbChain,
3712 ULARGE_INTEGER newSize)
3714 ULARGE_INTEGER size, offset, cbTotalRead;
3715 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3716 DirRef streamEntryRef;
3717 HRESULT resWrite = S_OK, resRead = S_OK;
3718 DirEntry streamEntry;
3719 BYTE* buffer;
3720 SmallBlockChainStream* sbTempChain;
3722 TRACE("%p %p\n", This, ppbbChain);
3724 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3725 DIRENTRY_NULL);
3727 if(!sbTempChain)
3728 return NULL;
3730 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3731 size = BlockChainStream_GetSize(*ppbbChain);
3732 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3734 offset.HighPart = 0;
3735 offset.LowPart = 0;
3736 cbTotalRead.QuadPart = 0;
3737 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3738 while(cbTotalRead.QuadPart < size.QuadPart)
3740 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3741 min(This->bigBlockSize, size.LowPart - offset.LowPart),
3742 buffer, &cbRead);
3744 if(FAILED(resRead))
3745 break;
3747 if(cbRead > 0)
3749 cbTotalRead.QuadPart += cbRead;
3751 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3752 cbRead, buffer, &cbWritten);
3754 if(FAILED(resWrite))
3755 break;
3757 offset.LowPart += cbRead;
3759 else
3761 resRead = STG_E_READFAULT;
3762 break;
3765 HeapFree(GetProcessHeap(), 0, buffer);
3767 size.HighPart = 0;
3768 size.LowPart = 0;
3770 if(FAILED(resRead) || FAILED(resWrite))
3772 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite);
3773 SmallBlockChainStream_SetSize(sbTempChain, size);
3774 SmallBlockChainStream_Destroy(sbTempChain);
3775 return NULL;
3778 /* destroy the original big block chain */
3779 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3780 BlockChainStream_SetSize(*ppbbChain, size);
3781 BlockChainStream_Destroy(*ppbbChain);
3782 *ppbbChain = NULL;
3784 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3785 streamEntry.startingBlock = sbHeadOfChain;
3786 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3788 SmallBlockChainStream_Destroy(sbTempChain);
3789 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3792 /******************************************************************************
3793 * Storage32Impl_AddBlockDepot
3795 * This will create a depot block, essentially it is a block initialized
3796 * to BLOCK_UNUSEDs.
3798 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3800 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3801 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3802 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3803 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3806 * Initialize blocks as free
3808 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3810 /* Reserve the range lock sector */
3811 if (depotIndex == rangeLockDepot)
3813 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3816 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3819 /******************************************************************************
3820 * Storage32Impl_GetExtDepotBlock
3822 * Returns the index of the block that corresponds to the specified depot
3823 * index. This method is only for depot indexes equal or greater than
3824 * COUNT_BBDEPOTINHEADER.
3826 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3828 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3829 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3830 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3831 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3832 ULONG blockIndex = BLOCK_UNUSED;
3833 ULONG extBlockIndex;
3834 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3835 int index, num_blocks;
3837 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3839 if (extBlockCount >= This->extBigBlockDepotCount)
3840 return BLOCK_UNUSED;
3842 if (This->indexExtBlockDepotCached != extBlockCount)
3844 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3846 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3848 num_blocks = This->bigBlockSize / 4;
3850 for (index = 0; index < num_blocks; index++)
3852 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3853 This->extBlockDepotCached[index] = blockIndex;
3856 This->indexExtBlockDepotCached = extBlockCount;
3859 blockIndex = This->extBlockDepotCached[extBlockOffset];
3861 return blockIndex;
3864 /******************************************************************************
3865 * Storage32Impl_SetExtDepotBlock
3867 * Associates the specified block index to the specified depot index.
3868 * This method is only for depot indexes equal or greater than
3869 * COUNT_BBDEPOTINHEADER.
3871 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3873 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3874 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3875 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3876 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3877 ULONG extBlockIndex;
3879 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3881 assert(extBlockCount < This->extBigBlockDepotCount);
3883 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3885 if (extBlockIndex != BLOCK_UNUSED)
3887 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3888 extBlockOffset * sizeof(ULONG),
3889 blockIndex);
3892 if (This->indexExtBlockDepotCached == extBlockCount)
3894 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3898 /******************************************************************************
3899 * Storage32Impl_AddExtBlockDepot
3901 * Creates an extended depot block.
3903 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3905 ULONG numExtBlocks = This->extBigBlockDepotCount;
3906 ULONG nextExtBlock = This->extBigBlockDepotStart;
3907 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3908 ULONG index = BLOCK_UNUSED;
3909 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3910 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3911 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3913 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3914 blocksPerDepotBlock;
3916 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3919 * The first extended block.
3921 This->extBigBlockDepotStart = index;
3923 else
3926 * Find the last existing extended block.
3928 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3931 * Add the new extended block to the chain.
3933 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3934 index);
3938 * Initialize this block.
3940 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3941 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3943 /* Add the block to our cache. */
3944 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3946 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3947 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3949 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3950 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3952 This->extBigBlockDepotLocations = new_cache;
3953 This->extBigBlockDepotLocationsSize = new_cache_size;
3955 This->extBigBlockDepotLocations[numExtBlocks] = index;
3957 return index;
3960 /************************************************************************
3961 * StorageImpl_GetNextBlockInChain
3963 * This method will retrieve the block index of the next big block in
3964 * in the chain.
3966 * Params: This - Pointer to the Storage object.
3967 * blockIndex - Index of the block to retrieve the chain
3968 * for.
3969 * nextBlockIndex - receives the return value.
3971 * Returns: This method returns the index of the next block in the chain.
3972 * It will return the constants:
3973 * BLOCK_SPECIAL - If the block given was not part of a
3974 * chain.
3975 * BLOCK_END_OF_CHAIN - If the block given was the last in
3976 * a chain.
3977 * BLOCK_UNUSED - If the block given was not past of a chain
3978 * and is available.
3979 * BLOCK_EXTBBDEPOT - This block is part of the extended
3980 * big block depot.
3982 * See Windows documentation for more details on IStorage methods.
3984 static HRESULT StorageImpl_GetNextBlockInChain(
3985 StorageImpl* This,
3986 ULONG blockIndex,
3987 ULONG* nextBlockIndex)
3989 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3990 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3991 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3992 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3993 ULONG read;
3994 ULONG depotBlockIndexPos;
3995 int index, num_blocks;
3997 *nextBlockIndex = BLOCK_SPECIAL;
3999 if(depotBlockCount >= This->bigBlockDepotCount)
4001 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount, This->bigBlockDepotCount);
4002 return STG_E_READFAULT;
4006 * Cache the currently accessed depot block.
4008 if (depotBlockCount != This->indexBlockDepotCached)
4010 This->indexBlockDepotCached = depotBlockCount;
4012 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4014 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4016 else
4019 * We have to look in the extended depot.
4021 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4024 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4026 if (!read)
4027 return STG_E_READFAULT;
4029 num_blocks = This->bigBlockSize / 4;
4031 for (index = 0; index < num_blocks; index++)
4033 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4034 This->blockDepotCached[index] = *nextBlockIndex;
4038 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4040 return S_OK;
4043 /******************************************************************************
4044 * Storage32Impl_GetNextExtendedBlock
4046 * Given an extended block this method will return the next extended block.
4048 * NOTES:
4049 * The last ULONG of an extended block is the block index of the next
4050 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4051 * depot.
4053 * Return values:
4054 * - The index of the next extended block
4055 * - BLOCK_UNUSED: there is no next extended block.
4056 * - Any other return values denotes failure.
4058 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4060 ULONG nextBlockIndex = BLOCK_SPECIAL;
4061 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4063 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4064 &nextBlockIndex);
4066 return nextBlockIndex;
4069 /******************************************************************************
4070 * StorageImpl_SetNextBlockInChain
4072 * This method will write the index of the specified block's next block
4073 * in the big block depot.
4075 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4076 * do the following
4078 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4079 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4080 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4083 static void StorageImpl_SetNextBlockInChain(
4084 StorageImpl* This,
4085 ULONG blockIndex,
4086 ULONG nextBlock)
4088 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4089 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4090 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4091 ULONG depotBlockIndexPos;
4093 assert(depotBlockCount < This->bigBlockDepotCount);
4094 assert(blockIndex != nextBlock);
4096 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4097 /* This should never happen (storage file format spec forbids it), but
4098 * older versions of Wine may have generated broken files. We don't want to
4099 * assert and potentially lose data, but we do want to know if this ever
4100 * happens in a newly-created file. */
4101 ERR("Using range lock page\n");
4103 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4105 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4107 else
4110 * We have to look in the extended depot.
4112 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4115 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4116 nextBlock);
4118 * Update the cached block depot, if necessary.
4120 if (depotBlockCount == This->indexBlockDepotCached)
4122 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4126 /******************************************************************************
4127 * StorageImpl_GetNextFreeBigBlock
4129 * Returns the index of the next free big block.
4130 * If the big block depot is filled, this method will enlarge it.
4133 static ULONG StorageImpl_GetNextFreeBigBlock(
4134 StorageImpl* This, ULONG neededAddNumBlocks)
4136 ULONG depotBlockIndexPos;
4137 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4138 ULONG depotBlockOffset;
4139 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4140 ULONG nextBlockIndex = BLOCK_SPECIAL;
4141 int depotIndex = 0;
4142 ULONG freeBlock = BLOCK_UNUSED;
4143 ULONG read;
4144 ULARGE_INTEGER neededSize;
4145 STATSTG statstg;
4147 depotIndex = This->prevFreeBlock / blocksPerDepot;
4148 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4151 * Scan the entire big block depot until we find a block marked free
4153 while (nextBlockIndex != BLOCK_UNUSED)
4155 if (depotIndex < COUNT_BBDEPOTINHEADER)
4157 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4160 * Grow the primary depot.
4162 if (depotBlockIndexPos == BLOCK_UNUSED)
4164 depotBlockIndexPos = depotIndex*blocksPerDepot;
4167 * Add a block depot.
4169 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4170 This->bigBlockDepotCount++;
4171 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4174 * Flag it as a block depot.
4176 StorageImpl_SetNextBlockInChain(This,
4177 depotBlockIndexPos,
4178 BLOCK_SPECIAL);
4180 /* Save new header information.
4182 StorageImpl_SaveFileHeader(This);
4185 else
4187 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4189 if (depotBlockIndexPos == BLOCK_UNUSED)
4192 * Grow the extended depot.
4194 ULONG extIndex = BLOCK_UNUSED;
4195 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4196 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4198 if (extBlockOffset == 0)
4200 /* We need an extended block.
4202 extIndex = Storage32Impl_AddExtBlockDepot(This);
4203 This->extBigBlockDepotCount++;
4204 depotBlockIndexPos = extIndex + 1;
4206 else
4207 depotBlockIndexPos = depotIndex * blocksPerDepot;
4210 * Add a block depot and mark it in the extended block.
4212 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4213 This->bigBlockDepotCount++;
4214 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4216 /* Flag the block depot.
4218 StorageImpl_SetNextBlockInChain(This,
4219 depotBlockIndexPos,
4220 BLOCK_SPECIAL);
4222 /* If necessary, flag the extended depot block.
4224 if (extIndex != BLOCK_UNUSED)
4225 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4227 /* Save header information.
4229 StorageImpl_SaveFileHeader(This);
4233 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4235 if (read)
4237 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4238 ( nextBlockIndex != BLOCK_UNUSED))
4240 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4242 if (nextBlockIndex == BLOCK_UNUSED)
4244 freeBlock = (depotIndex * blocksPerDepot) +
4245 (depotBlockOffset/sizeof(ULONG));
4248 depotBlockOffset += sizeof(ULONG);
4252 depotIndex++;
4253 depotBlockOffset = 0;
4257 * make sure that the block physically exists before using it
4259 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize * neededAddNumBlocks;
4261 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4263 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4264 ILockBytes_SetSize(This->lockBytes, neededSize);
4266 This->prevFreeBlock = freeBlock;
4268 return freeBlock;
4271 /******************************************************************************
4272 * StorageImpl_FreeBigBlock
4274 * This method will flag the specified block as free in the big block depot.
4276 static void StorageImpl_FreeBigBlock(
4277 StorageImpl* This,
4278 ULONG blockIndex)
4280 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4282 if (blockIndex < This->prevFreeBlock)
4283 This->prevFreeBlock = blockIndex;
4287 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4288 DirRef index, const DirEntry *data)
4290 StorageImpl *This = (StorageImpl*)base;
4291 return StorageImpl_WriteDirEntry(This, index, data);
4294 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4295 DirRef index, DirEntry *data)
4297 StorageImpl *This = (StorageImpl*)base;
4298 return StorageImpl_ReadDirEntry(This, index, data);
4301 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4303 int i;
4305 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4307 if (!This->blockChainCache[i])
4309 return &This->blockChainCache[i];
4313 i = This->blockChainToEvict;
4315 BlockChainStream_Destroy(This->blockChainCache[i]);
4316 This->blockChainCache[i] = NULL;
4318 This->blockChainToEvict++;
4319 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4320 This->blockChainToEvict = 0;
4322 return &This->blockChainCache[i];
4325 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4326 DirRef index)
4328 int i, free_index=-1;
4330 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4332 if (!This->blockChainCache[i])
4334 if (free_index == -1) free_index = i;
4336 else if (This->blockChainCache[i]->ownerDirEntry == index)
4338 return &This->blockChainCache[i];
4342 if (free_index == -1)
4344 free_index = This->blockChainToEvict;
4346 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4347 This->blockChainCache[free_index] = NULL;
4349 This->blockChainToEvict++;
4350 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4351 This->blockChainToEvict = 0;
4354 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4355 return &This->blockChainCache[free_index];
4358 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4360 int i;
4362 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4364 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4366 BlockChainStream_Destroy(This->blockChainCache[i]);
4367 This->blockChainCache[i] = NULL;
4368 return;
4373 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4374 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4376 StorageImpl *This = (StorageImpl*)base;
4377 DirEntry data;
4378 HRESULT hr;
4379 ULONG bytesToRead;
4381 hr = StorageImpl_ReadDirEntry(This, index, &data);
4382 if (FAILED(hr)) return hr;
4384 if (data.size.QuadPart == 0)
4386 *bytesRead = 0;
4387 return S_OK;
4390 if (offset.QuadPart + size > data.size.QuadPart)
4392 bytesToRead = data.size.QuadPart - offset.QuadPart;
4394 else
4396 bytesToRead = size;
4399 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4401 SmallBlockChainStream *stream;
4403 stream = SmallBlockChainStream_Construct(This, NULL, index);
4404 if (!stream) return E_OUTOFMEMORY;
4406 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4408 SmallBlockChainStream_Destroy(stream);
4410 return hr;
4412 else
4414 BlockChainStream *stream = NULL;
4416 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4417 if (!stream) return E_OUTOFMEMORY;
4419 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4421 return hr;
4425 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4426 ULARGE_INTEGER newsize)
4428 StorageImpl *This = (StorageImpl*)base;
4429 DirEntry data;
4430 HRESULT hr;
4431 SmallBlockChainStream *smallblock=NULL;
4432 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4434 hr = StorageImpl_ReadDirEntry(This, index, &data);
4435 if (FAILED(hr)) return hr;
4437 /* In simple mode keep the stream size above the small block limit */
4438 if (This->base.openFlags & STGM_SIMPLE)
4439 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4441 if (data.size.QuadPart == newsize.QuadPart)
4442 return S_OK;
4444 /* Create a block chain object of the appropriate type */
4445 if (data.size.QuadPart == 0)
4447 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4449 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4450 if (!smallblock) return E_OUTOFMEMORY;
4452 else
4454 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4455 bigblock = *pbigblock;
4456 if (!bigblock) return E_OUTOFMEMORY;
4459 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4461 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4462 if (!smallblock) return E_OUTOFMEMORY;
4464 else
4466 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4467 bigblock = *pbigblock;
4468 if (!bigblock) return E_OUTOFMEMORY;
4471 /* Change the block chain type if necessary. */
4472 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4474 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4475 if (!bigblock)
4477 SmallBlockChainStream_Destroy(smallblock);
4478 return E_FAIL;
4481 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4482 *pbigblock = bigblock;
4484 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4486 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4487 if (!smallblock)
4488 return E_FAIL;
4491 /* Set the size of the block chain. */
4492 if (smallblock)
4494 SmallBlockChainStream_SetSize(smallblock, newsize);
4495 SmallBlockChainStream_Destroy(smallblock);
4497 else
4499 BlockChainStream_SetSize(bigblock, newsize);
4502 /* Set the size in the directory entry. */
4503 hr = StorageImpl_ReadDirEntry(This, index, &data);
4504 if (SUCCEEDED(hr))
4506 data.size = newsize;
4508 hr = StorageImpl_WriteDirEntry(This, index, &data);
4510 return hr;
4513 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4514 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4516 StorageImpl *This = (StorageImpl*)base;
4517 DirEntry data;
4518 HRESULT hr;
4519 ULARGE_INTEGER newSize;
4521 hr = StorageImpl_ReadDirEntry(This, index, &data);
4522 if (FAILED(hr)) return hr;
4524 /* Grow the stream if necessary */
4525 newSize.QuadPart = offset.QuadPart + size;
4527 if (newSize.QuadPart > data.size.QuadPart)
4529 hr = StorageImpl_StreamSetSize(base, index, newSize);
4530 if (FAILED(hr))
4531 return hr;
4533 hr = StorageImpl_ReadDirEntry(This, index, &data);
4534 if (FAILED(hr)) return hr;
4537 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4539 SmallBlockChainStream *stream;
4541 stream = SmallBlockChainStream_Construct(This, NULL, index);
4542 if (!stream) return E_OUTOFMEMORY;
4544 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4546 SmallBlockChainStream_Destroy(stream);
4548 return hr;
4550 else
4552 BlockChainStream *stream;
4554 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4555 if (!stream) return E_OUTOFMEMORY;
4557 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4561 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4562 DirRef src)
4564 StorageImpl *This = (StorageImpl*)base;
4565 DirEntry dst_data, src_data;
4566 HRESULT hr;
4568 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4570 if (SUCCEEDED(hr))
4571 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4573 if (SUCCEEDED(hr))
4575 StorageImpl_DeleteCachedBlockChainStream(This, src);
4576 dst_data.startingBlock = src_data.startingBlock;
4577 dst_data.size = src_data.size;
4579 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4582 return hr;
4585 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4587 HRESULT hr=S_OK;
4588 DirEntry currentEntry;
4589 DirRef currentEntryRef;
4590 BlockChainStream *blockChainStream;
4592 if (create)
4594 ULARGE_INTEGER size;
4595 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4597 /* Discard any existing data. */
4598 size.QuadPart = 0;
4599 ILockBytes_SetSize(This->lockBytes, size);
4602 * Initialize all header variables:
4603 * - The big block depot consists of one block and it is at block 0
4604 * - The directory table starts at block 1
4605 * - There is no small block depot
4607 memset( This->bigBlockDepotStart,
4608 BLOCK_UNUSED,
4609 sizeof(This->bigBlockDepotStart));
4611 This->bigBlockDepotCount = 1;
4612 This->bigBlockDepotStart[0] = 0;
4613 This->rootStartBlock = 1;
4614 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4615 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4616 if (This->bigBlockSize == 4096)
4617 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4618 else
4619 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4620 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4621 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4622 This->extBigBlockDepotCount = 0;
4624 StorageImpl_SaveFileHeader(This);
4627 * Add one block for the big block depot and one block for the directory table
4629 size.HighPart = 0;
4630 size.LowPart = This->bigBlockSize * 3;
4631 ILockBytes_SetSize(This->lockBytes, size);
4634 * Initialize the big block depot
4636 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4637 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4638 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4639 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4641 else
4644 * Load the header for the file.
4646 hr = StorageImpl_LoadFileHeader(This);
4648 if (FAILED(hr))
4650 return hr;
4655 * There is no block depot cached yet.
4657 This->indexBlockDepotCached = 0xFFFFFFFF;
4658 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4661 * Start searching for free blocks with block 0.
4663 This->prevFreeBlock = 0;
4665 This->firstFreeSmallBlock = 0;
4667 /* Read the extended big block depot locations. */
4668 if (This->extBigBlockDepotCount != 0)
4670 ULONG current_block = This->extBigBlockDepotStart;
4671 ULONG cache_size = This->extBigBlockDepotCount * 2;
4672 ULONG i;
4674 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4675 if (!This->extBigBlockDepotLocations)
4677 return E_OUTOFMEMORY;
4680 This->extBigBlockDepotLocationsSize = cache_size;
4682 for (i=0; i<This->extBigBlockDepotCount; i++)
4684 if (current_block == BLOCK_END_OF_CHAIN)
4686 WARN("File has too few extended big block depot blocks.\n");
4687 return STG_E_DOCFILECORRUPT;
4689 This->extBigBlockDepotLocations[i] = current_block;
4690 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4693 else
4695 This->extBigBlockDepotLocations = NULL;
4696 This->extBigBlockDepotLocationsSize = 0;
4700 * Create the block chain abstractions.
4702 if(!(blockChainStream =
4703 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4705 return STG_E_READFAULT;
4707 if (!new_object)
4708 BlockChainStream_Destroy(This->rootBlockChain);
4709 This->rootBlockChain = blockChainStream;
4711 if(!(blockChainStream =
4712 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4713 DIRENTRY_NULL)))
4715 return STG_E_READFAULT;
4717 if (!new_object)
4718 BlockChainStream_Destroy(This->smallBlockDepotChain);
4719 This->smallBlockDepotChain = blockChainStream;
4722 * Write the root storage entry (memory only)
4724 if (create)
4726 DirEntry rootEntry;
4728 * Initialize the directory table
4730 memset(&rootEntry, 0, sizeof(rootEntry));
4731 lstrcpyW(rootEntry.name, L"Root Entry");
4732 rootEntry.sizeOfNameString = sizeof(L"Root Entry");
4733 rootEntry.stgType = STGTY_ROOT;
4734 rootEntry.leftChild = DIRENTRY_NULL;
4735 rootEntry.rightChild = DIRENTRY_NULL;
4736 rootEntry.dirRootEntry = DIRENTRY_NULL;
4737 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4738 rootEntry.size.HighPart = 0;
4739 rootEntry.size.LowPart = 0;
4741 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4745 * Find the ID of the root storage.
4747 currentEntryRef = 0;
4751 hr = StorageImpl_ReadDirEntry(
4752 This,
4753 currentEntryRef,
4754 &currentEntry);
4756 if (SUCCEEDED(hr))
4758 if ( (currentEntry.sizeOfNameString != 0 ) &&
4759 (currentEntry.stgType == STGTY_ROOT) )
4761 This->base.storageDirEntry = currentEntryRef;
4765 currentEntryRef++;
4767 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4769 if (FAILED(hr))
4771 return STG_E_READFAULT;
4775 * Create the block chain abstraction for the small block root chain.
4777 if(!(blockChainStream =
4778 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4780 return STG_E_READFAULT;
4782 if (!new_object)
4783 BlockChainStream_Destroy(This->smallBlockRootChain);
4784 This->smallBlockRootChain = blockChainStream;
4786 if (!new_object)
4788 int i;
4789 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4791 BlockChainStream_Destroy(This->blockChainCache[i]);
4792 This->blockChainCache[i] = NULL;
4796 return hr;
4799 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4800 ULONG* result, BOOL refresh)
4802 StorageImpl *This = (StorageImpl*)base;
4803 HRESULT hr=S_OK;
4804 DWORD oldTransactionSig = This->transactionSig;
4806 if (refresh)
4808 ULARGE_INTEGER offset;
4809 ULONG bytes_read;
4810 BYTE data[4];
4812 offset.HighPart = 0;
4813 offset.LowPart = OFFSET_TRANSACTIONSIG;
4814 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4816 if (SUCCEEDED(hr))
4818 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4820 if (oldTransactionSig != This->transactionSig)
4822 /* Someone else wrote to this, so toss all cached information. */
4823 TRACE("signature changed\n");
4825 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4828 if (FAILED(hr))
4829 This->transactionSig = oldTransactionSig;
4833 *result = This->transactionSig;
4835 return hr;
4838 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4839 ULONG value)
4841 StorageImpl *This = (StorageImpl*)base;
4843 This->transactionSig = value;
4844 StorageImpl_SaveFileHeader(This);
4846 return S_OK;
4849 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4850 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4852 if ((dwLockType & This->locks_supported) == 0)
4854 if (supported) *supported = FALSE;
4855 return S_OK;
4858 if (supported) *supported = TRUE;
4859 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4862 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4863 ULARGE_INTEGER cb, DWORD dwLockType)
4865 if ((dwLockType & This->locks_supported) == 0)
4866 return S_OK;
4868 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4871 /* Internal function */
4872 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4873 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4875 HRESULT hr;
4876 int delay = 0;
4877 DWORD start_time = GetTickCount();
4878 DWORD last_sanity_check = start_time;
4879 ULARGE_INTEGER sanity_offset, sanity_cb;
4881 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4882 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4886 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4888 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4890 DWORD current_time = GetTickCount();
4891 if (current_time - start_time >= 20000)
4893 /* timeout */
4894 break;
4896 if (current_time - last_sanity_check >= 500)
4898 /* Any storage implementation with the file open in a
4899 * shared mode should not lock these bytes for writing. However,
4900 * some programs (LibreOffice Writer) will keep ALL bytes locked
4901 * when opening in exclusive mode. We can use a read lock to
4902 * detect this case early, and not hang a full 20 seconds.
4904 * This can collide with another attempt to open the file in
4905 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4906 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4907 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4908 break;
4909 if (SUCCEEDED(hr))
4911 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4912 hr = STG_E_ACCESSDENIED;
4915 last_sanity_check = current_time;
4917 Sleep(delay);
4918 if (delay < 150) delay++;
4920 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4922 return hr;
4925 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4927 StorageImpl *This = (StorageImpl*)base;
4928 HRESULT hr;
4929 ULARGE_INTEGER offset, cb;
4931 if (write)
4933 /* Synchronous grab of second priority range, the commit lock, and the
4934 * lock-checking lock. */
4935 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4936 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4938 else
4940 offset.QuadPart = RANGELOCK_COMMIT;
4941 cb.QuadPart = 1;
4944 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4946 return hr;
4949 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4951 StorageImpl *This = (StorageImpl*)base;
4952 HRESULT hr;
4953 ULARGE_INTEGER offset, cb;
4955 if (write)
4957 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4958 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4960 else
4962 offset.QuadPart = RANGELOCK_COMMIT;
4963 cb.QuadPart = 1;
4966 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4968 return hr;
4971 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4973 StorageImpl *This = (StorageImpl*) iface;
4974 STATSTG statstg;
4975 HRESULT hr;
4977 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4979 *result = statstg.pwcsName;
4981 return hr;
4984 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
4985 ULONG end, HRESULT fail_hr)
4987 HRESULT hr;
4988 ULARGE_INTEGER offset, cb;
4990 offset.QuadPart = start;
4991 cb.QuadPart = 1 + end - start;
4993 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4994 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4996 if (FAILED(hr))
4997 return fail_hr;
4998 else
4999 return S_OK;
5002 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
5004 HRESULT hr=S_OK;
5005 int i, j;
5006 ULARGE_INTEGER offset, cb;
5008 cb.QuadPart = 1;
5010 for (i=start; i<=end; i++)
5012 offset.QuadPart = i;
5013 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5014 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
5015 break;
5018 if (SUCCEEDED(hr))
5020 for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++)
5022 if (This->locked_bytes[j] == 0)
5024 This->locked_bytes[j] = i;
5025 break;
5030 return hr;
5033 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5035 HRESULT hr;
5036 ULARGE_INTEGER offset;
5037 ULARGE_INTEGER cb;
5038 DWORD share_mode = STGM_SHARE_MODE(openFlags);
5039 BOOL supported;
5041 if (openFlags & STGM_NOSNAPSHOT)
5043 /* STGM_NOSNAPSHOT implies deny write */
5044 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5045 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5048 /* Wrap all other locking inside a single lock so we can check ranges safely */
5049 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5050 cb.QuadPart = 1;
5051 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5053 /* If the ILockBytes doesn't support locking that's ok. */
5054 if (!supported) return S_OK;
5055 else if (FAILED(hr)) return hr;
5057 hr = S_OK;
5059 /* First check for any conflicting locks. */
5060 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5061 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5063 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5064 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5066 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5067 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5069 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5070 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5072 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5073 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5075 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5077 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5079 if (SUCCEEDED(hr))
5080 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5083 /* Then grab our locks. */
5084 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5086 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5087 if (SUCCEEDED(hr))
5088 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5091 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5092 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5094 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5095 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5097 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5098 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5100 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5101 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5103 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5104 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5106 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5107 cb.QuadPart = 1;
5108 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5110 return hr;
5113 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5115 StorageImpl *This = (StorageImpl*)storage;
5116 int i;
5117 HRESULT hr;
5118 TRACE("(%p)\n", This);
5120 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5122 if (SUCCEEDED(hr))
5123 hr = BlockChainStream_Flush(This->rootBlockChain);
5125 if (SUCCEEDED(hr))
5126 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5128 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5129 if (This->blockChainCache[i])
5130 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5132 if (SUCCEEDED(hr))
5133 hr = ILockBytes_Flush(This->lockBytes);
5135 return hr;
5138 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5140 StorageImpl *This = (StorageImpl*) iface;
5142 StorageBaseImpl_DeleteAll(&This->base);
5144 This->base.reverted = TRUE;
5147 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5149 StorageImpl *This = (StorageImpl*) iface;
5150 int i;
5151 TRACE("(%p)\n", This);
5153 StorageImpl_Flush(iface);
5155 StorageImpl_Invalidate(iface);
5157 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5159 BlockChainStream_Destroy(This->smallBlockRootChain);
5160 BlockChainStream_Destroy(This->rootBlockChain);
5161 BlockChainStream_Destroy(This->smallBlockDepotChain);
5163 for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++)
5164 BlockChainStream_Destroy(This->blockChainCache[i]);
5166 for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++)
5168 ULARGE_INTEGER offset, cb;
5169 cb.QuadPart = 1;
5170 if (This->locked_bytes[i] != 0)
5172 offset.QuadPart = This->locked_bytes[i];
5173 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5177 if (This->lockBytes)
5178 ILockBytes_Release(This->lockBytes);
5179 HeapFree(GetProcessHeap(), 0, This);
5183 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5185 StorageImpl_Destroy,
5186 StorageImpl_Invalidate,
5187 StorageImpl_Flush,
5188 StorageImpl_GetFilename,
5189 StorageImpl_CreateDirEntry,
5190 StorageImpl_BaseWriteDirEntry,
5191 StorageImpl_BaseReadDirEntry,
5192 StorageImpl_DestroyDirEntry,
5193 StorageImpl_StreamReadAt,
5194 StorageImpl_StreamWriteAt,
5195 StorageImpl_StreamSetSize,
5196 StorageImpl_StreamLink,
5197 StorageImpl_GetTransactionSig,
5198 StorageImpl_SetTransactionSig,
5199 StorageImpl_LockTransaction,
5200 StorageImpl_UnlockTransaction
5205 * Virtual function table for the IStorageBaseImpl class.
5207 static const IStorageVtbl StorageImpl_Vtbl =
5209 StorageBaseImpl_QueryInterface,
5210 StorageBaseImpl_AddRef,
5211 StorageBaseImpl_Release,
5212 StorageBaseImpl_CreateStream,
5213 StorageBaseImpl_OpenStream,
5214 StorageBaseImpl_CreateStorage,
5215 StorageBaseImpl_OpenStorage,
5216 StorageBaseImpl_CopyTo,
5217 StorageBaseImpl_MoveElementTo,
5218 StorageBaseImpl_Commit,
5219 StorageBaseImpl_Revert,
5220 StorageBaseImpl_EnumElements,
5221 StorageBaseImpl_DestroyElement,
5222 StorageBaseImpl_RenameElement,
5223 StorageBaseImpl_SetElementTimes,
5224 StorageBaseImpl_SetClass,
5225 StorageBaseImpl_SetStateBits,
5226 StorageBaseImpl_Stat
5229 static HRESULT StorageImpl_Construct(
5230 HANDLE hFile,
5231 LPCOLESTR pwcsName,
5232 ILockBytes* pLkbyt,
5233 DWORD openFlags,
5234 BOOL fileBased,
5235 BOOL create,
5236 ULONG sector_size,
5237 StorageImpl** result)
5239 StorageImpl* This;
5240 HRESULT hr = S_OK;
5241 STATSTG stat;
5243 if ( FAILED( validateSTGM(openFlags) ))
5244 return STG_E_INVALIDFLAG;
5246 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5247 if (!This)
5248 return E_OUTOFMEMORY;
5250 memset(This, 0, sizeof(StorageImpl));
5252 list_init(&This->base.strmHead);
5254 list_init(&This->base.storageHead);
5256 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5257 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5258 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5259 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5260 This->base.openFlags = (openFlags & ~STGM_CREATE);
5261 This->base.ref = 1;
5262 This->base.create = create;
5264 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5265 This->base.lockingrole = SWMR_Writer;
5266 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5267 This->base.lockingrole = SWMR_Reader;
5268 else
5269 This->base.lockingrole = SWMR_None;
5271 This->base.reverted = FALSE;
5274 * Initialize the big block cache.
5276 This->bigBlockSize = sector_size;
5277 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5278 if (hFile)
5279 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5280 else
5282 This->lockBytes = pLkbyt;
5283 ILockBytes_AddRef(pLkbyt);
5286 if (SUCCEEDED(hr))
5287 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5289 if (SUCCEEDED(hr))
5291 This->locks_supported = stat.grfLocksSupported;
5292 if (!hFile)
5293 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5294 This->locks_supported &= ~WINE_LOCK_READ;
5296 hr = StorageImpl_GrabLocks(This, openFlags);
5299 if (SUCCEEDED(hr))
5300 hr = StorageImpl_Refresh(This, TRUE, create);
5302 if (FAILED(hr))
5304 IStorage_Release(&This->base.IStorage_iface);
5305 *result = NULL;
5307 else
5309 StorageImpl_Flush(&This->base);
5310 *result = This;
5313 return hr;
5317 /************************************************************************
5318 * StorageInternalImpl implementation
5319 ***********************************************************************/
5321 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5323 StorageInternalImpl* This = (StorageInternalImpl*) base;
5325 if (!This->base.reverted)
5327 TRACE("Storage invalidated (stg=%p)\n", This);
5329 This->base.reverted = TRUE;
5331 This->parentStorage = NULL;
5333 StorageBaseImpl_DeleteAll(&This->base);
5335 list_remove(&This->ParentListEntry);
5339 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5341 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5343 StorageInternalImpl_Invalidate(&This->base);
5345 HeapFree(GetProcessHeap(), 0, This);
5348 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5350 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5352 return StorageBaseImpl_Flush(This->parentStorage);
5355 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5357 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5359 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5362 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5363 const DirEntry *newData, DirRef *index)
5365 StorageInternalImpl* This = (StorageInternalImpl*) base;
5367 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5368 newData, index);
5371 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5372 DirRef index, const DirEntry *data)
5374 StorageInternalImpl* This = (StorageInternalImpl*) base;
5376 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5377 index, data);
5380 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5381 DirRef index, DirEntry *data)
5383 StorageInternalImpl* This = (StorageInternalImpl*) base;
5385 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5386 index, data);
5389 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5390 DirRef index)
5392 StorageInternalImpl* This = (StorageInternalImpl*) base;
5394 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5395 index);
5398 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5399 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5401 StorageInternalImpl* This = (StorageInternalImpl*) base;
5403 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5404 index, offset, size, buffer, bytesRead);
5407 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5408 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5410 StorageInternalImpl* This = (StorageInternalImpl*) base;
5412 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5413 index, offset, size, buffer, bytesWritten);
5416 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5417 DirRef index, ULARGE_INTEGER newsize)
5419 StorageInternalImpl* This = (StorageInternalImpl*) base;
5421 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5422 index, newsize);
5425 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5426 DirRef dst, DirRef src)
5428 StorageInternalImpl* This = (StorageInternalImpl*) base;
5430 return StorageBaseImpl_StreamLink(This->parentStorage,
5431 dst, src);
5434 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5435 ULONG* result, BOOL refresh)
5437 return E_NOTIMPL;
5440 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5441 ULONG value)
5443 return E_NOTIMPL;
5446 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5448 return E_NOTIMPL;
5451 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5453 return E_NOTIMPL;
5456 /******************************************************************************
5458 ** StorageInternalImpl_Commit
5461 static HRESULT WINAPI StorageInternalImpl_Commit(
5462 IStorage* iface,
5463 DWORD grfCommitFlags) /* [in] */
5465 StorageBaseImpl* This = impl_from_IStorage(iface);
5466 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
5467 return StorageBaseImpl_Flush(This);
5470 /******************************************************************************
5472 ** StorageInternalImpl_Revert
5475 static HRESULT WINAPI StorageInternalImpl_Revert(
5476 IStorage* iface)
5478 FIXME("(%p): stub\n", iface);
5479 return S_OK;
5483 * Virtual function table for the StorageInternalImpl class.
5485 static const IStorageVtbl StorageInternalImpl_Vtbl =
5487 StorageBaseImpl_QueryInterface,
5488 StorageBaseImpl_AddRef,
5489 StorageBaseImpl_Release,
5490 StorageBaseImpl_CreateStream,
5491 StorageBaseImpl_OpenStream,
5492 StorageBaseImpl_CreateStorage,
5493 StorageBaseImpl_OpenStorage,
5494 StorageBaseImpl_CopyTo,
5495 StorageBaseImpl_MoveElementTo,
5496 StorageInternalImpl_Commit,
5497 StorageInternalImpl_Revert,
5498 StorageBaseImpl_EnumElements,
5499 StorageBaseImpl_DestroyElement,
5500 StorageBaseImpl_RenameElement,
5501 StorageBaseImpl_SetElementTimes,
5502 StorageBaseImpl_SetClass,
5503 StorageBaseImpl_SetStateBits,
5504 StorageBaseImpl_Stat
5507 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5509 StorageInternalImpl_Destroy,
5510 StorageInternalImpl_Invalidate,
5511 StorageInternalImpl_Flush,
5512 StorageInternalImpl_GetFilename,
5513 StorageInternalImpl_CreateDirEntry,
5514 StorageInternalImpl_WriteDirEntry,
5515 StorageInternalImpl_ReadDirEntry,
5516 StorageInternalImpl_DestroyDirEntry,
5517 StorageInternalImpl_StreamReadAt,
5518 StorageInternalImpl_StreamWriteAt,
5519 StorageInternalImpl_StreamSetSize,
5520 StorageInternalImpl_StreamLink,
5521 StorageInternalImpl_GetTransactionSig,
5522 StorageInternalImpl_SetTransactionSig,
5523 StorageInternalImpl_LockTransaction,
5524 StorageInternalImpl_UnlockTransaction
5527 static StorageInternalImpl* StorageInternalImpl_Construct(
5528 StorageBaseImpl* parentStorage,
5529 DWORD openFlags,
5530 DirRef storageDirEntry)
5532 StorageInternalImpl* newStorage;
5534 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5536 if (newStorage!=0)
5538 list_init(&newStorage->base.strmHead);
5540 list_init(&newStorage->base.storageHead);
5543 * Initialize the virtual function table.
5545 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5546 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5547 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5548 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5550 newStorage->base.reverted = FALSE;
5552 newStorage->base.ref = 1;
5554 newStorage->parentStorage = parentStorage;
5557 * Keep a reference to the directory entry of this storage
5559 newStorage->base.storageDirEntry = storageDirEntry;
5561 newStorage->base.create = FALSE;
5563 return newStorage;
5566 return 0;
5570 /************************************************************************
5571 * TransactedSnapshotImpl implementation
5572 ***********************************************************************/
5574 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5576 DirRef result=This->firstFreeEntry;
5578 while (result < This->entries_size && This->entries[result].inuse)
5579 result++;
5581 if (result == This->entries_size)
5583 ULONG new_size = This->entries_size * 2;
5584 TransactedDirEntry *new_entries;
5586 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5587 if (!new_entries) return DIRENTRY_NULL;
5589 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5590 HeapFree(GetProcessHeap(), 0, This->entries);
5592 This->entries = new_entries;
5593 This->entries_size = new_size;
5596 This->entries[result].inuse = TRUE;
5598 This->firstFreeEntry = result+1;
5600 return result;
5603 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5604 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5606 DirRef stubEntryRef;
5607 TransactedDirEntry *entry;
5609 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5611 if (stubEntryRef != DIRENTRY_NULL)
5613 entry = &This->entries[stubEntryRef];
5615 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5617 entry->read = FALSE;
5620 return stubEntryRef;
5623 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5624 TransactedSnapshotImpl *This, DirRef entry)
5626 HRESULT hr=S_OK;
5627 DirEntry data;
5629 if (!This->entries[entry].read)
5631 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5632 This->entries[entry].transactedParentEntry,
5633 &data);
5635 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5637 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5639 if (data.leftChild == DIRENTRY_NULL)
5640 hr = E_OUTOFMEMORY;
5643 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5645 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5647 if (data.rightChild == DIRENTRY_NULL)
5648 hr = E_OUTOFMEMORY;
5651 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5653 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5655 if (data.dirRootEntry == DIRENTRY_NULL)
5656 hr = E_OUTOFMEMORY;
5659 if (SUCCEEDED(hr))
5661 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5662 This->entries[entry].read = TRUE;
5666 return hr;
5669 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5670 TransactedSnapshotImpl *This, DirRef entry)
5672 HRESULT hr = S_OK;
5674 if (!This->entries[entry].stream_dirty)
5676 DirEntry new_entrydata;
5678 memset(&new_entrydata, 0, sizeof(DirEntry));
5679 new_entrydata.name[0] = 'S';
5680 new_entrydata.sizeOfNameString = 1;
5681 new_entrydata.stgType = STGTY_STREAM;
5682 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5683 new_entrydata.leftChild = DIRENTRY_NULL;
5684 new_entrydata.rightChild = DIRENTRY_NULL;
5685 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5687 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5688 &This->entries[entry].stream_entry);
5690 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5692 hr = StorageBaseImpl_CopyStream(
5693 This->scratch, This->entries[entry].stream_entry,
5694 This->transactedParent, This->entries[entry].transactedParentEntry);
5696 if (FAILED(hr))
5697 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5700 if (SUCCEEDED(hr))
5701 This->entries[entry].stream_dirty = TRUE;
5703 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5705 /* Since this entry is modified, and we aren't using its stream data, we
5706 * no longer care about the original entry. */
5707 DirRef delete_ref;
5708 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5710 if (delete_ref != DIRENTRY_NULL)
5711 This->entries[delete_ref].deleted = TRUE;
5713 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5717 return hr;
5720 /* Find the first entry in a depth-first traversal. */
5721 static DirRef TransactedSnapshotImpl_FindFirstChild(
5722 TransactedSnapshotImpl* This, DirRef parent)
5724 DirRef cursor, prev;
5725 TransactedDirEntry *entry;
5727 cursor = parent;
5728 entry = &This->entries[cursor];
5729 while (entry->read)
5731 if (entry->data.leftChild != DIRENTRY_NULL)
5733 prev = cursor;
5734 cursor = entry->data.leftChild;
5735 entry = &This->entries[cursor];
5736 entry->parent = prev;
5738 else if (entry->data.rightChild != DIRENTRY_NULL)
5740 prev = cursor;
5741 cursor = entry->data.rightChild;
5742 entry = &This->entries[cursor];
5743 entry->parent = prev;
5745 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5747 prev = cursor;
5748 cursor = entry->data.dirRootEntry;
5749 entry = &This->entries[cursor];
5750 entry->parent = prev;
5752 else
5753 break;
5756 return cursor;
5759 /* Find the next entry in a depth-first traversal. */
5760 static DirRef TransactedSnapshotImpl_FindNextChild(
5761 TransactedSnapshotImpl* This, DirRef current)
5763 DirRef parent;
5764 TransactedDirEntry *parent_entry;
5766 parent = This->entries[current].parent;
5767 parent_entry = &This->entries[parent];
5769 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5771 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5773 This->entries[parent_entry->data.rightChild].parent = parent;
5774 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5777 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5779 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5780 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5784 return parent;
5787 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5788 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5789 TransactedSnapshotImpl* This, DirRef entry)
5791 return entry != DIRENTRY_NULL &&
5792 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5795 /* Destroy the entries created by CopyTree. */
5796 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5797 TransactedSnapshotImpl* This, DirRef stop)
5799 DirRef cursor;
5800 TransactedDirEntry *entry;
5801 ULARGE_INTEGER zero;
5803 zero.QuadPart = 0;
5805 if (!This->entries[This->base.storageDirEntry].read)
5806 return;
5808 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5810 if (cursor == DIRENTRY_NULL)
5811 return;
5813 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5815 while (cursor != DIRENTRY_NULL && cursor != stop)
5817 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5819 entry = &This->entries[cursor];
5821 if (entry->stream_dirty)
5822 StorageBaseImpl_StreamSetSize(This->transactedParent,
5823 entry->newTransactedParentEntry, zero);
5825 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5826 entry->newTransactedParentEntry);
5828 entry->newTransactedParentEntry = entry->transactedParentEntry;
5831 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5835 /* Make a copy of our edited tree that we can use in the parent. */
5836 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5838 DirRef cursor;
5839 TransactedDirEntry *entry;
5840 HRESULT hr = S_OK;
5842 cursor = This->base.storageDirEntry;
5843 entry = &This->entries[cursor];
5844 entry->parent = DIRENTRY_NULL;
5845 entry->newTransactedParentEntry = entry->transactedParentEntry;
5847 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5848 return S_OK;
5850 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5852 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5853 entry = &This->entries[cursor];
5855 while (cursor != DIRENTRY_NULL)
5857 /* Make a copy of this entry in the transacted parent. */
5858 if (!entry->read ||
5859 (!entry->dirty && !entry->stream_dirty &&
5860 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5861 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5862 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5863 entry->newTransactedParentEntry = entry->transactedParentEntry;
5864 else
5866 DirEntry newData;
5868 memcpy(&newData, &entry->data, sizeof(DirEntry));
5870 newData.size.QuadPart = 0;
5871 newData.startingBlock = BLOCK_END_OF_CHAIN;
5873 if (newData.leftChild != DIRENTRY_NULL)
5874 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5876 if (newData.rightChild != DIRENTRY_NULL)
5877 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5879 if (newData.dirRootEntry != DIRENTRY_NULL)
5880 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5882 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5883 &entry->newTransactedParentEntry);
5884 if (FAILED(hr))
5886 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5887 return hr;
5890 if (entry->stream_dirty)
5892 hr = StorageBaseImpl_CopyStream(
5893 This->transactedParent, entry->newTransactedParentEntry,
5894 This->scratch, entry->stream_entry);
5896 else if (entry->data.size.QuadPart)
5898 hr = StorageBaseImpl_StreamLink(
5899 This->transactedParent, entry->newTransactedParentEntry,
5900 entry->transactedParentEntry);
5903 if (FAILED(hr))
5905 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5906 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5907 return hr;
5911 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5912 entry = &This->entries[cursor];
5915 return hr;
5918 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5919 IStorage* iface,
5920 DWORD grfCommitFlags) /* [in] */
5922 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5923 TransactedDirEntry *root_entry;
5924 DirRef i, dir_root_ref;
5925 DirEntry data;
5926 ULARGE_INTEGER zero;
5927 HRESULT hr;
5928 ULONG transactionSig;
5930 zero.QuadPart = 0;
5932 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
5934 /* Cannot commit a read-only transacted storage */
5935 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5936 return STG_E_ACCESSDENIED;
5938 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5939 if (hr == E_NOTIMPL) hr = S_OK;
5940 if (SUCCEEDED(hr))
5942 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5943 if (SUCCEEDED(hr))
5945 if (transactionSig != This->lastTransactionSig)
5947 ERR("file was externally modified\n");
5948 hr = STG_E_NOTCURRENT;
5951 if (SUCCEEDED(hr))
5953 This->lastTransactionSig = transactionSig+1;
5954 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5957 else if (hr == E_NOTIMPL)
5958 hr = S_OK;
5960 if (FAILED(hr)) goto end;
5962 /* To prevent data loss, we create the new structure in the file before we
5963 * delete the old one, so that in case of errors the old data is intact. We
5964 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5965 * needed in the rare situation where we have just enough free disk space to
5966 * overwrite the existing data. */
5968 root_entry = &This->entries[This->base.storageDirEntry];
5970 if (!root_entry->read)
5971 goto end;
5973 hr = TransactedSnapshotImpl_CopyTree(This);
5974 if (FAILED(hr)) goto end;
5976 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5977 dir_root_ref = DIRENTRY_NULL;
5978 else
5979 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5981 hr = StorageBaseImpl_Flush(This->transactedParent);
5983 /* Update the storage to use the new data in one step. */
5984 if (SUCCEEDED(hr))
5985 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5986 root_entry->transactedParentEntry, &data);
5988 if (SUCCEEDED(hr))
5990 data.dirRootEntry = dir_root_ref;
5991 data.clsid = root_entry->data.clsid;
5992 data.ctime = root_entry->data.ctime;
5993 data.mtime = root_entry->data.mtime;
5995 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5996 root_entry->transactedParentEntry, &data);
5999 /* Try to flush after updating the root storage, but if the flush fails, keep
6000 * going, on the theory that it'll either succeed later or the subsequent
6001 * writes will fail. */
6002 StorageBaseImpl_Flush(This->transactedParent);
6004 if (SUCCEEDED(hr))
6006 /* Destroy the old now-orphaned data. */
6007 for (i=0; i<This->entries_size; i++)
6009 TransactedDirEntry *entry = &This->entries[i];
6010 if (entry->inuse)
6012 if (entry->deleted)
6014 StorageBaseImpl_StreamSetSize(This->transactedParent,
6015 entry->transactedParentEntry, zero);
6016 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6017 entry->transactedParentEntry);
6018 memset(entry, 0, sizeof(TransactedDirEntry));
6019 This->firstFreeEntry = min(i, This->firstFreeEntry);
6021 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6023 if (entry->transactedParentEntry != DIRENTRY_NULL)
6024 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6025 entry->transactedParentEntry);
6026 if (entry->stream_dirty)
6028 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6029 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6030 entry->stream_dirty = FALSE;
6032 entry->dirty = FALSE;
6033 entry->transactedParentEntry = entry->newTransactedParentEntry;
6038 else
6040 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6043 if (SUCCEEDED(hr))
6044 hr = StorageBaseImpl_Flush(This->transactedParent);
6045 end:
6046 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6049 TRACE("<-- %#lx\n", hr);
6050 return hr;
6053 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6054 IStorage* iface)
6056 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6057 ULARGE_INTEGER zero;
6058 ULONG i;
6060 TRACE("(%p)\n", iface);
6062 /* Destroy the open objects. */
6063 StorageBaseImpl_DeleteAll(&This->base);
6065 /* Clear out the scratch file. */
6066 zero.QuadPart = 0;
6067 for (i=0; i<This->entries_size; i++)
6069 if (This->entries[i].stream_dirty)
6071 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6072 zero);
6074 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6078 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6080 This->firstFreeEntry = 0;
6081 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6083 return S_OK;
6086 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6088 if (!This->reverted)
6090 TRACE("Storage invalidated (stg=%p)\n", This);
6092 This->reverted = TRUE;
6094 StorageBaseImpl_DeleteAll(This);
6098 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6100 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6102 IStorage_Revert(&This->base.IStorage_iface);
6103 IStorage_Release(&This->transactedParent->IStorage_iface);
6104 IStorage_Release(&This->scratch->IStorage_iface);
6105 HeapFree(GetProcessHeap(), 0, This->entries);
6106 HeapFree(GetProcessHeap(), 0, This);
6109 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6111 /* We only need to flush when committing. */
6112 return S_OK;
6115 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6117 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6119 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6122 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6123 const DirEntry *newData, DirRef *index)
6125 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6126 DirRef new_ref;
6127 TransactedDirEntry *new_entry;
6129 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6130 if (new_ref == DIRENTRY_NULL)
6131 return E_OUTOFMEMORY;
6133 new_entry = &This->entries[new_ref];
6135 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6136 new_entry->read = TRUE;
6137 new_entry->dirty = TRUE;
6138 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6140 *index = new_ref;
6142 TRACE("%s l=%lx r=%lx d=%lx <-- %lx\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6144 return S_OK;
6147 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6148 DirRef index, const DirEntry *data)
6150 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6151 HRESULT hr;
6153 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6155 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6156 if (FAILED(hr))
6158 TRACE("<-- %#lx\n", hr);
6159 return hr;
6162 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6164 if (index != This->base.storageDirEntry)
6166 This->entries[index].dirty = TRUE;
6168 if (data->size.QuadPart == 0 &&
6169 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6171 /* Since this entry is modified, and we aren't using its stream data, we
6172 * no longer care about the original entry. */
6173 DirRef delete_ref;
6174 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6176 if (delete_ref != DIRENTRY_NULL)
6177 This->entries[delete_ref].deleted = TRUE;
6179 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6182 TRACE("<-- S_OK\n");
6183 return S_OK;
6186 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6187 DirRef index, DirEntry *data)
6189 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6190 HRESULT hr;
6192 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6193 if (FAILED(hr))
6195 TRACE("<-- %#lx\n", hr);
6196 return hr;
6199 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6201 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6203 return S_OK;
6206 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6207 DirRef index)
6209 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6211 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6212 This->entries[index].data.size.QuadPart != 0)
6214 /* If we deleted this entry while it has stream data. We must have left the
6215 * data because some other entry is using it, and we need to leave the
6216 * original entry alone. */
6217 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6218 This->firstFreeEntry = min(index, This->firstFreeEntry);
6220 else
6222 This->entries[index].deleted = TRUE;
6225 return S_OK;
6228 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6229 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6231 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6233 if (This->entries[index].stream_dirty)
6235 return StorageBaseImpl_StreamReadAt(This->scratch,
6236 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6238 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6240 /* This stream doesn't live in the parent, and we haven't allocated storage
6241 * for it yet */
6242 *bytesRead = 0;
6243 return S_OK;
6245 else
6247 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6248 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6252 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6253 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6255 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6256 HRESULT hr;
6258 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6259 if (FAILED(hr))
6261 TRACE("<-- %#lx\n", hr);
6262 return hr;
6265 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6266 if (FAILED(hr))
6268 TRACE("<-- %#lx\n", hr);
6269 return hr;
6272 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6273 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6275 if (SUCCEEDED(hr) && size != 0)
6276 This->entries[index].data.size.QuadPart = max(
6277 This->entries[index].data.size.QuadPart,
6278 offset.QuadPart + size);
6280 TRACE("<-- %#lx\n", hr);
6281 return hr;
6284 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6285 DirRef index, ULARGE_INTEGER newsize)
6287 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6288 HRESULT hr;
6290 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6291 if (FAILED(hr))
6293 TRACE("<-- %#lx\n", hr);
6294 return hr;
6297 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6298 return S_OK;
6300 if (newsize.QuadPart == 0)
6302 /* Destroy any parent references or entries in the scratch file. */
6303 if (This->entries[index].stream_dirty)
6305 ULARGE_INTEGER zero;
6306 zero.QuadPart = 0;
6307 StorageBaseImpl_StreamSetSize(This->scratch,
6308 This->entries[index].stream_entry, zero);
6309 StorageBaseImpl_DestroyDirEntry(This->scratch,
6310 This->entries[index].stream_entry);
6311 This->entries[index].stream_dirty = FALSE;
6313 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6315 DirRef delete_ref;
6316 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6318 if (delete_ref != DIRENTRY_NULL)
6319 This->entries[delete_ref].deleted = TRUE;
6321 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6324 else
6326 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6327 if (FAILED(hr)) return hr;
6329 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6330 This->entries[index].stream_entry, newsize);
6333 if (SUCCEEDED(hr))
6334 This->entries[index].data.size = newsize;
6336 TRACE("<-- %#lx\n", hr);
6337 return hr;
6340 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6341 DirRef dst, DirRef src)
6343 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6344 HRESULT hr;
6345 TransactedDirEntry *dst_entry, *src_entry;
6347 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6348 if (FAILED(hr))
6350 TRACE("<-- %#lx\n", hr);
6351 return hr;
6354 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6355 if (FAILED(hr))
6357 TRACE("<-- %#lx\n", hr);
6358 return hr;
6361 dst_entry = &This->entries[dst];
6362 src_entry = &This->entries[src];
6364 dst_entry->stream_dirty = src_entry->stream_dirty;
6365 dst_entry->stream_entry = src_entry->stream_entry;
6366 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6367 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6368 dst_entry->data.size = src_entry->data.size;
6370 return S_OK;
6373 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6374 ULONG* result, BOOL refresh)
6376 return E_NOTIMPL;
6379 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6380 ULONG value)
6382 return E_NOTIMPL;
6385 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6387 return E_NOTIMPL;
6390 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6392 return E_NOTIMPL;
6395 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6397 StorageBaseImpl_QueryInterface,
6398 StorageBaseImpl_AddRef,
6399 StorageBaseImpl_Release,
6400 StorageBaseImpl_CreateStream,
6401 StorageBaseImpl_OpenStream,
6402 StorageBaseImpl_CreateStorage,
6403 StorageBaseImpl_OpenStorage,
6404 StorageBaseImpl_CopyTo,
6405 StorageBaseImpl_MoveElementTo,
6406 TransactedSnapshotImpl_Commit,
6407 TransactedSnapshotImpl_Revert,
6408 StorageBaseImpl_EnumElements,
6409 StorageBaseImpl_DestroyElement,
6410 StorageBaseImpl_RenameElement,
6411 StorageBaseImpl_SetElementTimes,
6412 StorageBaseImpl_SetClass,
6413 StorageBaseImpl_SetStateBits,
6414 StorageBaseImpl_Stat
6417 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6419 TransactedSnapshotImpl_Destroy,
6420 TransactedSnapshotImpl_Invalidate,
6421 TransactedSnapshotImpl_Flush,
6422 TransactedSnapshotImpl_GetFilename,
6423 TransactedSnapshotImpl_CreateDirEntry,
6424 TransactedSnapshotImpl_WriteDirEntry,
6425 TransactedSnapshotImpl_ReadDirEntry,
6426 TransactedSnapshotImpl_DestroyDirEntry,
6427 TransactedSnapshotImpl_StreamReadAt,
6428 TransactedSnapshotImpl_StreamWriteAt,
6429 TransactedSnapshotImpl_StreamSetSize,
6430 TransactedSnapshotImpl_StreamLink,
6431 TransactedSnapshotImpl_GetTransactionSig,
6432 TransactedSnapshotImpl_SetTransactionSig,
6433 TransactedSnapshotImpl_LockTransaction,
6434 TransactedSnapshotImpl_UnlockTransaction
6437 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6438 TransactedSnapshotImpl** result)
6440 HRESULT hr;
6442 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6443 if (*result)
6445 IStorage *scratch;
6447 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6449 /* This is OK because the property set storage functions use the IStorage functions. */
6450 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6451 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6453 list_init(&(*result)->base.strmHead);
6455 list_init(&(*result)->base.storageHead);
6457 (*result)->base.ref = 1;
6459 (*result)->base.openFlags = parentStorage->openFlags;
6461 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6462 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6464 /* Create a new temporary storage to act as the scratch file. */
6465 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6466 0, &scratch);
6467 (*result)->scratch = impl_from_IStorage(scratch);
6469 if (SUCCEEDED(hr))
6471 ULONG num_entries = 20;
6473 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6474 (*result)->entries_size = num_entries;
6475 (*result)->firstFreeEntry = 0;
6477 if ((*result)->entries)
6479 /* parentStorage already has 1 reference, which we take over here. */
6480 (*result)->transactedParent = parentStorage;
6482 parentStorage->transactedChild = &(*result)->base;
6484 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6486 else
6488 IStorage_Release(scratch);
6490 hr = E_OUTOFMEMORY;
6494 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6496 return hr;
6498 else
6499 return E_OUTOFMEMORY;
6503 /************************************************************************
6504 * TransactedSharedImpl implementation
6505 ***********************************************************************/
6507 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6509 if (!This->reverted)
6511 TRACE("Storage invalidated (stg=%p)\n", This);
6513 This->reverted = TRUE;
6515 StorageBaseImpl_DeleteAll(This);
6519 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6521 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6523 TransactedSharedImpl_Invalidate(&This->base);
6524 IStorage_Release(&This->transactedParent->IStorage_iface);
6525 IStorage_Release(&This->scratch->base.IStorage_iface);
6526 HeapFree(GetProcessHeap(), 0, This);
6529 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6531 /* We only need to flush when committing. */
6532 return S_OK;
6535 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6537 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6539 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6542 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6543 const DirEntry *newData, DirRef *index)
6545 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6547 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6548 newData, index);
6551 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6552 DirRef index, const DirEntry *data)
6554 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6556 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6557 index, data);
6560 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6561 DirRef index, DirEntry *data)
6563 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6565 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6566 index, data);
6569 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6570 DirRef index)
6572 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6574 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6575 index);
6578 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6579 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6581 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6583 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6584 index, offset, size, buffer, bytesRead);
6587 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6588 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6590 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6592 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6593 index, offset, size, buffer, bytesWritten);
6596 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6597 DirRef index, ULARGE_INTEGER newsize)
6599 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6601 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6602 index, newsize);
6605 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6606 DirRef dst, DirRef src)
6608 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6610 return StorageBaseImpl_StreamLink(&This->scratch->base,
6611 dst, src);
6614 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6615 ULONG* result, BOOL refresh)
6617 return E_NOTIMPL;
6620 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6621 ULONG value)
6623 return E_NOTIMPL;
6626 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6628 return E_NOTIMPL;
6631 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6633 return E_NOTIMPL;
6636 static HRESULT WINAPI TransactedSharedImpl_Commit(
6637 IStorage* iface,
6638 DWORD grfCommitFlags) /* [in] */
6640 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6641 DirRef new_storage_ref, prev_storage_ref;
6642 DirEntry src_data, dst_data;
6643 HRESULT hr;
6644 ULONG transactionSig;
6646 TRACE("%p, %#lx\n", iface, grfCommitFlags);
6648 /* Cannot commit a read-only transacted storage */
6649 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6650 return STG_E_ACCESSDENIED;
6652 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6653 if (hr == E_NOTIMPL) hr = S_OK;
6654 if (SUCCEEDED(hr))
6656 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6657 if (SUCCEEDED(hr))
6659 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6660 hr = STG_E_NOTCURRENT;
6662 if (SUCCEEDED(hr))
6663 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6665 else if (hr == E_NOTIMPL)
6666 hr = S_OK;
6668 if (SUCCEEDED(hr))
6669 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6671 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6672 if (SUCCEEDED(hr))
6673 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6675 if (SUCCEEDED(hr))
6676 hr = StorageBaseImpl_Flush(This->transactedParent);
6678 if (SUCCEEDED(hr))
6679 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6681 if (SUCCEEDED(hr))
6683 prev_storage_ref = dst_data.dirRootEntry;
6684 dst_data.dirRootEntry = new_storage_ref;
6685 dst_data.clsid = src_data.clsid;
6686 dst_data.ctime = src_data.ctime;
6687 dst_data.mtime = src_data.mtime;
6688 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6691 if (SUCCEEDED(hr))
6693 /* Try to flush after updating the root storage, but if the flush fails, keep
6694 * going, on the theory that it'll either succeed later or the subsequent
6695 * writes will fail. */
6696 StorageBaseImpl_Flush(This->transactedParent);
6698 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6701 if (SUCCEEDED(hr))
6702 hr = StorageBaseImpl_Flush(This->transactedParent);
6704 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6706 if (SUCCEEDED(hr))
6707 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6709 if (SUCCEEDED(hr))
6711 This->lastTransactionSig = transactionSig+1;
6714 TRACE("<-- %#lx\n", hr);
6715 return hr;
6718 static HRESULT WINAPI TransactedSharedImpl_Revert(
6719 IStorage* iface)
6721 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6723 TRACE("(%p)\n", iface);
6725 /* Destroy the open objects. */
6726 StorageBaseImpl_DeleteAll(&This->base);
6728 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6731 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6733 StorageBaseImpl_QueryInterface,
6734 StorageBaseImpl_AddRef,
6735 StorageBaseImpl_Release,
6736 StorageBaseImpl_CreateStream,
6737 StorageBaseImpl_OpenStream,
6738 StorageBaseImpl_CreateStorage,
6739 StorageBaseImpl_OpenStorage,
6740 StorageBaseImpl_CopyTo,
6741 StorageBaseImpl_MoveElementTo,
6742 TransactedSharedImpl_Commit,
6743 TransactedSharedImpl_Revert,
6744 StorageBaseImpl_EnumElements,
6745 StorageBaseImpl_DestroyElement,
6746 StorageBaseImpl_RenameElement,
6747 StorageBaseImpl_SetElementTimes,
6748 StorageBaseImpl_SetClass,
6749 StorageBaseImpl_SetStateBits,
6750 StorageBaseImpl_Stat
6753 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6755 TransactedSharedImpl_Destroy,
6756 TransactedSharedImpl_Invalidate,
6757 TransactedSharedImpl_Flush,
6758 TransactedSharedImpl_GetFilename,
6759 TransactedSharedImpl_CreateDirEntry,
6760 TransactedSharedImpl_WriteDirEntry,
6761 TransactedSharedImpl_ReadDirEntry,
6762 TransactedSharedImpl_DestroyDirEntry,
6763 TransactedSharedImpl_StreamReadAt,
6764 TransactedSharedImpl_StreamWriteAt,
6765 TransactedSharedImpl_StreamSetSize,
6766 TransactedSharedImpl_StreamLink,
6767 TransactedSharedImpl_GetTransactionSig,
6768 TransactedSharedImpl_SetTransactionSig,
6769 TransactedSharedImpl_LockTransaction,
6770 TransactedSharedImpl_UnlockTransaction
6773 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6774 TransactedSharedImpl** result)
6776 HRESULT hr;
6778 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6779 if (*result)
6781 IStorage *scratch;
6783 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6785 /* This is OK because the property set storage functions use the IStorage functions. */
6786 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6787 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6789 list_init(&(*result)->base.strmHead);
6791 list_init(&(*result)->base.storageHead);
6793 (*result)->base.ref = 1;
6795 (*result)->base.openFlags = parentStorage->openFlags;
6797 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6799 if (SUCCEEDED(hr))
6801 STGOPTIONS stgo;
6803 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6804 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6806 stgo.usVersion = 1;
6807 stgo.reserved = 0;
6808 stgo.ulSectorSize = 4096;
6809 stgo.pwcsTemplateFile = NULL;
6811 /* Create a new temporary storage to act as the scratch file. */
6812 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6813 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6814 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6816 if (SUCCEEDED(hr))
6818 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6819 parentStorage, parentStorage->storageDirEntry);
6821 if (SUCCEEDED(hr))
6823 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6825 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6826 (*result)->transactedParent = parentStorage;
6829 if (FAILED(hr))
6830 IStorage_Release(scratch);
6833 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6836 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6838 return hr;
6840 else
6841 return E_OUTOFMEMORY;
6844 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6845 BOOL toplevel, StorageBaseImpl** result)
6847 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6849 if (parentStorage->openFlags & fixme_flags)
6851 fixme_flags &= ~parentStorage->openFlags;
6852 FIXME("Unimplemented flags %lx\n", parentStorage->openFlags);
6855 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6856 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6857 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6859 /* Need to create a temp file for the snapshot */
6860 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6863 return TransactedSnapshotImpl_Construct(parentStorage,
6864 (TransactedSnapshotImpl**)result);
6867 static HRESULT Storage_Construct(
6868 HANDLE hFile,
6869 LPCOLESTR pwcsName,
6870 ILockBytes* pLkbyt,
6871 DWORD openFlags,
6872 BOOL fileBased,
6873 BOOL create,
6874 ULONG sector_size,
6875 StorageBaseImpl** result)
6877 StorageImpl *newStorage;
6878 StorageBaseImpl *newTransactedStorage;
6879 HRESULT hr;
6881 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6882 if (FAILED(hr)) goto end;
6884 if (openFlags & STGM_TRANSACTED)
6886 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6887 if (FAILED(hr))
6888 IStorage_Release(&newStorage->base.IStorage_iface);
6889 else
6890 *result = newTransactedStorage;
6892 else
6893 *result = &newStorage->base;
6895 end:
6896 return hr;
6900 /************************************************************************
6901 * StorageUtl helper functions
6902 ***********************************************************************/
6904 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6906 WORD tmp;
6908 memcpy(&tmp, buffer+offset, sizeof(WORD));
6909 *value = lendian16toh(tmp);
6912 void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value)
6914 value = htole16(value);
6915 memcpy((BYTE *)buffer + offset, &value, sizeof(WORD));
6918 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6920 DWORD tmp;
6922 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6923 *value = lendian32toh(tmp);
6926 void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value)
6928 value = htole32(value);
6929 memcpy((BYTE *)buffer + offset, &value, sizeof(DWORD));
6932 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6933 ULARGE_INTEGER* value)
6935 #ifdef WORDS_BIGENDIAN
6936 ULARGE_INTEGER tmp;
6938 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6939 value->u.LowPart = htole32(tmp.HighPart);
6940 value->u.HighPart = htole32(tmp.LowPart);
6941 #else
6942 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6943 #endif
6946 void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value)
6948 #ifdef WORDS_BIGENDIAN
6949 ULARGE_INTEGER tmp;
6951 tmp.LowPart = htole32(value->u.HighPart);
6952 tmp.HighPart = htole32(value->u.LowPart);
6953 memcpy((BYTE *)buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6954 #else
6955 memcpy((BYTE *)buffer + offset, value, sizeof(ULARGE_INTEGER));
6956 #endif
6959 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6961 StorageUtl_ReadDWord(buffer, offset, (DWORD *)&value->Data1);
6962 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6963 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6965 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6968 void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value)
6970 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6971 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6972 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6974 memcpy((BYTE *)buffer + offset + 8, value->Data4, sizeof(value->Data4));
6977 void StorageUtl_CopyDirEntryToSTATSTG(
6978 StorageBaseImpl* storage,
6979 STATSTG* destination,
6980 const DirEntry* source,
6981 int statFlags)
6984 * The copy of the string occurs only when the flag is not set
6986 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6988 /* Use the filename for the root storage. */
6989 destination->pwcsName = 0;
6990 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6992 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6993 (source->name[0] == 0) )
6995 destination->pwcsName = 0;
6997 else
6999 destination->pwcsName =
7000 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
7002 lstrcpyW(destination->pwcsName, source->name);
7005 switch (source->stgType)
7007 case STGTY_STORAGE:
7008 case STGTY_ROOT:
7009 destination->type = STGTY_STORAGE;
7010 break;
7011 case STGTY_STREAM:
7012 destination->type = STGTY_STREAM;
7013 break;
7014 default:
7015 destination->type = STGTY_STREAM;
7016 break;
7019 destination->cbSize = source->size;
7021 currentReturnStruct->mtime = {0}; TODO
7022 currentReturnStruct->ctime = {0};
7023 currentReturnStruct->atime = {0};
7025 destination->grfMode = 0;
7026 destination->grfLocksSupported = 0;
7027 destination->clsid = source->clsid;
7028 destination->grfStateBits = 0;
7029 destination->reserved = 0;
7033 /************************************************************************
7034 * BlockChainStream implementation
7035 ***********************************************************************/
7037 /******************************************************************************
7038 * BlockChainStream_GetHeadOfChain
7040 * Returns the head of this stream chain.
7041 * Some special chains don't have directory entries, their heads are kept in
7042 * This->headOfStreamPlaceHolder.
7045 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7047 DirEntry chainEntry;
7048 HRESULT hr;
7050 if (This->headOfStreamPlaceHolder != 0)
7051 return *(This->headOfStreamPlaceHolder);
7053 if (This->ownerDirEntry != DIRENTRY_NULL)
7055 hr = StorageImpl_ReadDirEntry(
7056 This->parentStorage,
7057 This->ownerDirEntry,
7058 &chainEntry);
7060 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7061 return chainEntry.startingBlock;
7064 return BLOCK_END_OF_CHAIN;
7067 /* Read and save the index of all blocks in this stream. */
7068 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7070 ULONG next_sector, next_offset;
7071 HRESULT hr;
7072 struct BlockChainRun *last_run;
7074 if (This->indexCacheLen == 0)
7076 last_run = NULL;
7077 next_offset = 0;
7078 next_sector = BlockChainStream_GetHeadOfChain(This);
7080 else
7082 last_run = &This->indexCache[This->indexCacheLen-1];
7083 next_offset = last_run->lastOffset+1;
7084 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7085 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7086 &next_sector);
7087 if (FAILED(hr)) return hr;
7090 while (next_sector != BLOCK_END_OF_CHAIN)
7092 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7094 /* Add the current block to the cache. */
7095 if (This->indexCacheSize == 0)
7097 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7098 if (!This->indexCache) return E_OUTOFMEMORY;
7099 This->indexCacheSize = 16;
7101 else if (This->indexCacheSize == This->indexCacheLen)
7103 struct BlockChainRun *new_cache;
7104 ULONG new_size;
7106 new_size = This->indexCacheSize * 2;
7107 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7108 if (!new_cache) return E_OUTOFMEMORY;
7109 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7111 HeapFree(GetProcessHeap(), 0, This->indexCache);
7112 This->indexCache = new_cache;
7113 This->indexCacheSize = new_size;
7116 This->indexCacheLen++;
7117 last_run = &This->indexCache[This->indexCacheLen-1];
7118 last_run->firstSector = next_sector;
7119 last_run->firstOffset = next_offset;
7122 last_run->lastOffset = next_offset;
7124 /* Find the next block. */
7125 next_offset++;
7126 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7127 if (FAILED(hr)) return hr;
7130 if (This->indexCacheLen)
7132 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7133 This->numBlocks = last_run->lastOffset+1;
7135 else
7137 This->tailIndex = BLOCK_END_OF_CHAIN;
7138 This->numBlocks = 0;
7141 return S_OK;
7144 /* Locate the nth block in this stream. */
7145 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7147 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7148 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7150 if (offset >= This->numBlocks)
7151 return BLOCK_END_OF_CHAIN;
7153 while (min_run < max_run)
7155 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7156 if (offset < This->indexCache[run_to_check].firstOffset)
7158 max_offset = This->indexCache[run_to_check].firstOffset-1;
7159 max_run = run_to_check-1;
7161 else if (offset > This->indexCache[run_to_check].lastOffset)
7163 min_offset = This->indexCache[run_to_check].lastOffset+1;
7164 min_run = run_to_check+1;
7166 else
7167 /* Block is in this run. */
7168 min_run = max_run = run_to_check;
7171 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7174 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7175 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7177 BlockChainBlock *result=NULL;
7178 int i;
7180 for (i=0; i<2; i++)
7181 if (This->cachedBlocks[i].index == index)
7183 *sector = This->cachedBlocks[i].sector;
7184 *block = &This->cachedBlocks[i];
7185 return S_OK;
7188 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7189 if (*sector == BLOCK_END_OF_CHAIN)
7190 return STG_E_DOCFILECORRUPT;
7192 if (create)
7194 if (This->cachedBlocks[0].index == 0xffffffff)
7195 result = &This->cachedBlocks[0];
7196 else if (This->cachedBlocks[1].index == 0xffffffff)
7197 result = &This->cachedBlocks[1];
7198 else
7200 result = &This->cachedBlocks[This->blockToEvict++];
7201 if (This->blockToEvict == 2)
7202 This->blockToEvict = 0;
7205 if (result->dirty)
7207 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7208 return STG_E_WRITEFAULT;
7209 result->dirty = FALSE;
7212 result->read = FALSE;
7213 result->index = index;
7214 result->sector = *sector;
7217 *block = result;
7218 return S_OK;
7221 BlockChainStream* BlockChainStream_Construct(
7222 StorageImpl* parentStorage,
7223 ULONG* headOfStreamPlaceHolder,
7224 DirRef dirEntry)
7226 BlockChainStream* newStream;
7228 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7229 if(!newStream)
7230 return NULL;
7232 newStream->parentStorage = parentStorage;
7233 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7234 newStream->ownerDirEntry = dirEntry;
7235 newStream->indexCache = NULL;
7236 newStream->indexCacheLen = 0;
7237 newStream->indexCacheSize = 0;
7238 newStream->cachedBlocks[0].index = 0xffffffff;
7239 newStream->cachedBlocks[0].dirty = FALSE;
7240 newStream->cachedBlocks[1].index = 0xffffffff;
7241 newStream->cachedBlocks[1].dirty = FALSE;
7242 newStream->blockToEvict = 0;
7244 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7246 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7247 HeapFree(GetProcessHeap(), 0, newStream);
7248 return NULL;
7251 return newStream;
7254 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7256 int i;
7257 if (!This) return S_OK;
7258 for (i=0; i<2; i++)
7260 if (This->cachedBlocks[i].dirty)
7262 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7263 This->cachedBlocks[i].dirty = FALSE;
7264 else
7265 return STG_E_WRITEFAULT;
7268 return S_OK;
7271 void BlockChainStream_Destroy(BlockChainStream* This)
7273 if (This)
7275 BlockChainStream_Flush(This);
7276 HeapFree(GetProcessHeap(), 0, This->indexCache);
7278 HeapFree(GetProcessHeap(), 0, This);
7281 /******************************************************************************
7282 * BlockChainStream_Shrink
7284 * Shrinks this chain in the big block depot.
7286 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7287 ULARGE_INTEGER newSize)
7289 ULONG blockIndex;
7290 ULONG numBlocks;
7291 int i;
7294 * Figure out how many blocks are needed to contain the new size
7296 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7298 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7299 numBlocks++;
7301 if (numBlocks)
7304 * Go to the new end of chain
7306 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7308 /* Mark the new end of chain */
7309 StorageImpl_SetNextBlockInChain(
7310 This->parentStorage,
7311 blockIndex,
7312 BLOCK_END_OF_CHAIN);
7314 This->tailIndex = blockIndex;
7316 else
7318 if (This->headOfStreamPlaceHolder != 0)
7320 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7322 else
7324 DirEntry chainEntry;
7325 assert(This->ownerDirEntry != DIRENTRY_NULL);
7327 StorageImpl_ReadDirEntry(
7328 This->parentStorage,
7329 This->ownerDirEntry,
7330 &chainEntry);
7332 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7334 StorageImpl_WriteDirEntry(
7335 This->parentStorage,
7336 This->ownerDirEntry,
7337 &chainEntry);
7340 This->tailIndex = BLOCK_END_OF_CHAIN;
7343 This->numBlocks = numBlocks;
7346 * Mark the extra blocks as free
7348 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7350 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7351 StorageImpl_FreeBigBlock(This->parentStorage,
7352 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7353 if (last_run->lastOffset == last_run->firstOffset)
7354 This->indexCacheLen--;
7355 else
7356 last_run->lastOffset--;
7360 * Reset the last accessed block cache.
7362 for (i=0; i<2; i++)
7364 if (This->cachedBlocks[i].index >= numBlocks)
7366 This->cachedBlocks[i].index = 0xffffffff;
7367 This->cachedBlocks[i].dirty = FALSE;
7371 return TRUE;
7374 /******************************************************************************
7375 * BlockChainStream_Enlarge
7377 * Grows this chain in the big block depot.
7379 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7380 ULARGE_INTEGER newSize)
7382 ULONG blockIndex, currentBlock;
7383 ULONG newNumBlocks;
7384 ULONG oldNumBlocks = 0;
7386 blockIndex = BlockChainStream_GetHeadOfChain(This);
7389 * Empty chain. Create the head.
7391 if (blockIndex == BLOCK_END_OF_CHAIN)
7393 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, 1);
7394 StorageImpl_SetNextBlockInChain(This->parentStorage,
7395 blockIndex,
7396 BLOCK_END_OF_CHAIN);
7398 if (This->headOfStreamPlaceHolder != 0)
7400 *(This->headOfStreamPlaceHolder) = blockIndex;
7402 else
7404 DirEntry chainEntry;
7405 assert(This->ownerDirEntry != DIRENTRY_NULL);
7407 StorageImpl_ReadDirEntry(
7408 This->parentStorage,
7409 This->ownerDirEntry,
7410 &chainEntry);
7412 chainEntry.startingBlock = blockIndex;
7414 StorageImpl_WriteDirEntry(
7415 This->parentStorage,
7416 This->ownerDirEntry,
7417 &chainEntry);
7420 This->tailIndex = blockIndex;
7421 This->numBlocks = 1;
7425 * Figure out how many blocks are needed to contain this stream
7427 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7429 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7430 newNumBlocks++;
7433 * Go to the current end of chain
7435 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7437 currentBlock = blockIndex;
7439 while (blockIndex != BLOCK_END_OF_CHAIN)
7441 This->numBlocks++;
7442 currentBlock = blockIndex;
7444 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7445 &blockIndex)))
7446 return FALSE;
7449 This->tailIndex = currentBlock;
7452 currentBlock = This->tailIndex;
7453 oldNumBlocks = This->numBlocks;
7456 * Add new blocks to the chain
7458 if (oldNumBlocks < newNumBlocks)
7460 while (oldNumBlocks < newNumBlocks)
7462 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, newNumBlocks - oldNumBlocks);
7464 StorageImpl_SetNextBlockInChain(
7465 This->parentStorage,
7466 currentBlock,
7467 blockIndex);
7469 StorageImpl_SetNextBlockInChain(
7470 This->parentStorage,
7471 blockIndex,
7472 BLOCK_END_OF_CHAIN);
7474 currentBlock = blockIndex;
7475 oldNumBlocks++;
7478 This->tailIndex = blockIndex;
7479 This->numBlocks = newNumBlocks;
7482 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7483 return FALSE;
7485 return TRUE;
7489 /******************************************************************************
7490 * BlockChainStream_GetSize
7492 * Returns the size of this chain.
7493 * Will return the block count if this chain doesn't have a directory entry.
7495 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7497 DirEntry chainEntry;
7499 if(This->headOfStreamPlaceHolder == NULL)
7502 * This chain has a directory entry so use the size value from there.
7504 StorageImpl_ReadDirEntry(
7505 This->parentStorage,
7506 This->ownerDirEntry,
7507 &chainEntry);
7509 return chainEntry.size;
7511 else
7514 * this chain is a chain that does not have a directory entry, figure out the
7515 * size by making the product number of used blocks times the
7516 * size of them
7518 ULARGE_INTEGER result;
7519 result.QuadPart =
7520 (ULONGLONG)BlockChainStream_GetCount(This) *
7521 This->parentStorage->bigBlockSize;
7523 return result;
7527 /******************************************************************************
7528 * BlockChainStream_SetSize
7530 * Sets the size of this stream. The big block depot will be updated.
7531 * The file will grow if we grow the chain.
7533 * TODO: Free the actual blocks in the file when we shrink the chain.
7534 * Currently, the blocks are still in the file. So the file size
7535 * doesn't shrink even if we shrink streams.
7537 BOOL BlockChainStream_SetSize(
7538 BlockChainStream* This,
7539 ULARGE_INTEGER newSize)
7541 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7543 if (newSize.QuadPart == size.QuadPart)
7544 return TRUE;
7546 if (newSize.QuadPart < size.QuadPart)
7548 BlockChainStream_Shrink(This, newSize);
7550 else
7552 BlockChainStream_Enlarge(This, newSize);
7555 return TRUE;
7558 /******************************************************************************
7559 * BlockChainStream_ReadAt
7561 * Reads a specified number of bytes from this chain at the specified offset.
7562 * bytesRead may be NULL.
7563 * Failure will be returned if the specified number of bytes has not been read.
7565 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7566 ULARGE_INTEGER offset,
7567 ULONG size,
7568 void* buffer,
7569 ULONG* bytesRead)
7571 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7572 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7573 ULONG bytesToReadInBuffer;
7574 ULONG blockIndex;
7575 BYTE* bufferWalker;
7576 ULARGE_INTEGER stream_size;
7577 HRESULT hr;
7578 BlockChainBlock *cachedBlock;
7580 TRACE("%p, %li, %p, %lu, %p.\n",This, offset.LowPart, buffer, size, bytesRead);
7583 * Find the first block in the stream that contains part of the buffer.
7585 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7587 *bytesRead = 0;
7589 stream_size = BlockChainStream_GetSize(This);
7590 if (stream_size.QuadPart > offset.QuadPart)
7591 size = min(stream_size.QuadPart - offset.QuadPart, size);
7592 else
7593 return S_OK;
7596 * Start reading the buffer.
7598 bufferWalker = buffer;
7600 while (size > 0)
7602 ULARGE_INTEGER ulOffset;
7603 DWORD bytesReadAt;
7606 * Calculate how many bytes we can copy from this big block.
7608 bytesToReadInBuffer =
7609 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7611 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7613 if (FAILED(hr))
7614 return hr;
7616 if (!cachedBlock)
7618 /* Not in cache, and we're going to read past the end of the block. */
7619 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7620 offsetInBlock;
7622 StorageImpl_ReadAt(This->parentStorage,
7623 ulOffset,
7624 bufferWalker,
7625 bytesToReadInBuffer,
7626 &bytesReadAt);
7628 else
7630 if (!cachedBlock->read)
7632 ULONG read;
7633 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7634 return STG_E_READFAULT;
7636 cachedBlock->read = TRUE;
7639 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7640 bytesReadAt = bytesToReadInBuffer;
7643 blockNoInSequence++;
7644 bufferWalker += bytesReadAt;
7645 size -= bytesReadAt;
7646 *bytesRead += bytesReadAt;
7647 offsetInBlock = 0; /* There is no offset on the next block */
7649 if (bytesToReadInBuffer != bytesReadAt)
7650 break;
7653 return S_OK;
7656 /******************************************************************************
7657 * BlockChainStream_WriteAt
7659 * Writes the specified number of bytes to this chain at the specified offset.
7660 * Will fail if not all specified number of bytes have been written.
7662 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7663 ULARGE_INTEGER offset,
7664 ULONG size,
7665 const void* buffer,
7666 ULONG* bytesWritten)
7668 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7669 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7670 ULONG bytesToWrite;
7671 ULONG blockIndex;
7672 const BYTE* bufferWalker;
7673 HRESULT hr;
7674 BlockChainBlock *cachedBlock;
7676 *bytesWritten = 0;
7677 bufferWalker = buffer;
7679 while (size > 0)
7681 ULARGE_INTEGER ulOffset;
7682 DWORD bytesWrittenAt;
7685 * Calculate how many bytes we can copy to this big block.
7687 bytesToWrite =
7688 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7690 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7692 /* BlockChainStream_SetSize should have already been called to ensure we have
7693 * enough blocks in the chain to write into */
7694 if (FAILED(hr))
7696 ERR("not enough blocks in chain to write data\n");
7697 return hr;
7700 if (!cachedBlock)
7702 /* Not in cache, and we're going to write past the end of the block. */
7703 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7704 offsetInBlock;
7706 StorageImpl_WriteAt(This->parentStorage,
7707 ulOffset,
7708 bufferWalker,
7709 bytesToWrite,
7710 &bytesWrittenAt);
7712 else
7714 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7716 ULONG read;
7717 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7718 return STG_E_READFAULT;
7721 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7722 bytesWrittenAt = bytesToWrite;
7723 cachedBlock->read = TRUE;
7724 cachedBlock->dirty = TRUE;
7727 blockNoInSequence++;
7728 bufferWalker += bytesWrittenAt;
7729 size -= bytesWrittenAt;
7730 *bytesWritten += bytesWrittenAt;
7731 offsetInBlock = 0; /* There is no offset on the next block */
7733 if (bytesWrittenAt != bytesToWrite)
7734 break;
7737 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7741 /************************************************************************
7742 * SmallBlockChainStream implementation
7743 ***********************************************************************/
7745 SmallBlockChainStream* SmallBlockChainStream_Construct(
7746 StorageImpl* parentStorage,
7747 ULONG* headOfStreamPlaceHolder,
7748 DirRef dirEntry)
7750 SmallBlockChainStream* newStream;
7752 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7754 newStream->parentStorage = parentStorage;
7755 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7756 newStream->ownerDirEntry = dirEntry;
7758 return newStream;
7761 void SmallBlockChainStream_Destroy(
7762 SmallBlockChainStream* This)
7764 HeapFree(GetProcessHeap(), 0, This);
7767 /******************************************************************************
7768 * SmallBlockChainStream_GetHeadOfChain
7770 * Returns the head of this chain of small blocks.
7772 static ULONG SmallBlockChainStream_GetHeadOfChain(
7773 SmallBlockChainStream* This)
7775 DirEntry chainEntry;
7776 HRESULT hr;
7778 if (This->headOfStreamPlaceHolder != NULL)
7779 return *(This->headOfStreamPlaceHolder);
7781 if (This->ownerDirEntry)
7783 hr = StorageImpl_ReadDirEntry(
7784 This->parentStorage,
7785 This->ownerDirEntry,
7786 &chainEntry);
7788 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7789 return chainEntry.startingBlock;
7792 return BLOCK_END_OF_CHAIN;
7795 /******************************************************************************
7796 * SmallBlockChainStream_GetNextBlockInChain
7798 * Returns the index of the next small block in this chain.
7800 * Return Values:
7801 * - BLOCK_END_OF_CHAIN: end of this chain
7802 * - BLOCK_UNUSED: small block 'blockIndex' is free
7804 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7805 SmallBlockChainStream* This,
7806 ULONG blockIndex,
7807 ULONG* nextBlockInChain)
7809 ULARGE_INTEGER offsetOfBlockInDepot;
7810 DWORD buffer;
7811 ULONG bytesRead;
7812 HRESULT res;
7814 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7816 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7819 * Read those bytes in the buffer from the small block file.
7821 res = BlockChainStream_ReadAt(
7822 This->parentStorage->smallBlockDepotChain,
7823 offsetOfBlockInDepot,
7824 sizeof(DWORD),
7825 &buffer,
7826 &bytesRead);
7828 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7829 res = STG_E_READFAULT;
7831 if (SUCCEEDED(res))
7833 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7834 return S_OK;
7837 return res;
7840 /******************************************************************************
7841 * SmallBlockChainStream_SetNextBlockInChain
7843 * Writes the index of the next block of the specified block in the small
7844 * block depot.
7845 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7846 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7848 static void SmallBlockChainStream_SetNextBlockInChain(
7849 SmallBlockChainStream* This,
7850 ULONG blockIndex,
7851 ULONG nextBlock)
7853 ULARGE_INTEGER offsetOfBlockInDepot;
7854 DWORD buffer;
7855 ULONG bytesWritten;
7857 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7859 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
7862 * Read those bytes in the buffer from the small block file.
7864 BlockChainStream_WriteAt(
7865 This->parentStorage->smallBlockDepotChain,
7866 offsetOfBlockInDepot,
7867 sizeof(DWORD),
7868 &buffer,
7869 &bytesWritten);
7872 /******************************************************************************
7873 * SmallBlockChainStream_FreeBlock
7875 * Flag small block 'blockIndex' as free in the small block depot.
7877 static void SmallBlockChainStream_FreeBlock(
7878 SmallBlockChainStream* This,
7879 ULONG blockIndex)
7881 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7884 /******************************************************************************
7885 * SmallBlockChainStream_GetNextFreeBlock
7887 * Returns the index of a free small block. The small block depot will be
7888 * enlarged if necessary. The small block chain will also be enlarged if
7889 * necessary.
7891 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7892 SmallBlockChainStream* This)
7894 ULARGE_INTEGER offsetOfBlockInDepot;
7895 DWORD buffer;
7896 ULONG bytesRead;
7897 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7898 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7899 HRESULT res = S_OK;
7900 ULONG smallBlocksPerBigBlock;
7901 DirEntry rootEntry;
7902 ULONG blocksRequired;
7903 ULARGE_INTEGER old_size, size_required;
7905 offsetOfBlockInDepot.HighPart = 0;
7908 * Scan the small block depot for a free block
7910 while (nextBlockIndex != BLOCK_UNUSED)
7912 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7914 res = BlockChainStream_ReadAt(
7915 This->parentStorage->smallBlockDepotChain,
7916 offsetOfBlockInDepot,
7917 sizeof(DWORD),
7918 &buffer,
7919 &bytesRead);
7922 * If we run out of space for the small block depot, enlarge it
7924 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7926 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7928 if (nextBlockIndex != BLOCK_UNUSED)
7929 blockIndex++;
7931 else
7933 ULONG count =
7934 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7936 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7937 ULARGE_INTEGER newSize, offset;
7938 ULONG bytesWritten;
7940 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7941 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7944 * Initialize all the small blocks to free
7946 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7947 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7948 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7949 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7951 StorageImpl_SaveFileHeader(This->parentStorage);
7955 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7957 smallBlocksPerBigBlock =
7958 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7961 * Verify if we have to allocate big blocks to contain small blocks
7963 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7965 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7967 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7969 if (size_required.QuadPart > old_size.QuadPart)
7971 BlockChainStream_SetSize(
7972 This->parentStorage->smallBlockRootChain,
7973 size_required);
7975 StorageImpl_ReadDirEntry(
7976 This->parentStorage,
7977 This->parentStorage->base.storageDirEntry,
7978 &rootEntry);
7980 rootEntry.size = size_required;
7982 StorageImpl_WriteDirEntry(
7983 This->parentStorage,
7984 This->parentStorage->base.storageDirEntry,
7985 &rootEntry);
7988 return blockIndex;
7991 /******************************************************************************
7992 * SmallBlockChainStream_ReadAt
7994 * Reads a specified number of bytes from this chain at the specified offset.
7995 * bytesRead may be NULL.
7996 * Failure will be returned if the specified number of bytes has not been read.
7998 HRESULT SmallBlockChainStream_ReadAt(
7999 SmallBlockChainStream* This,
8000 ULARGE_INTEGER offset,
8001 ULONG size,
8002 void* buffer,
8003 ULONG* bytesRead)
8005 HRESULT rc = S_OK;
8006 ULARGE_INTEGER offsetInBigBlockFile;
8007 ULONG blockNoInSequence =
8008 offset.LowPart / This->parentStorage->smallBlockSize;
8010 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
8011 ULONG bytesToReadInBuffer;
8012 ULONG blockIndex;
8013 ULONG bytesReadFromBigBlockFile;
8014 BYTE* bufferWalker;
8015 ULARGE_INTEGER stream_size;
8018 * This should never happen on a small block file.
8020 assert(offset.HighPart==0);
8022 *bytesRead = 0;
8024 stream_size = SmallBlockChainStream_GetSize(This);
8025 if (stream_size.QuadPart > offset.QuadPart)
8026 size = min(stream_size.QuadPart - offset.QuadPart, size);
8027 else
8028 return S_OK;
8031 * Find the first block in the stream that contains part of the buffer.
8033 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8035 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8037 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8038 if(FAILED(rc))
8039 return rc;
8040 blockNoInSequence--;
8044 * Start reading the buffer.
8046 bufferWalker = buffer;
8048 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8051 * Calculate how many bytes we can copy from this small block.
8053 bytesToReadInBuffer =
8054 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8057 * Calculate the offset of the small block in the small block file.
8059 offsetInBigBlockFile.QuadPart =
8060 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8062 offsetInBigBlockFile.QuadPart += offsetInBlock;
8065 * Read those bytes in the buffer from the small block file.
8066 * The small block has already been identified so it shouldn't fail
8067 * unless the file is corrupt.
8069 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8070 offsetInBigBlockFile,
8071 bytesToReadInBuffer,
8072 bufferWalker,
8073 &bytesReadFromBigBlockFile);
8075 if (FAILED(rc))
8076 return rc;
8078 if (!bytesReadFromBigBlockFile)
8079 return STG_E_DOCFILECORRUPT;
8082 * Step to the next big block.
8084 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8085 if(FAILED(rc))
8086 return STG_E_DOCFILECORRUPT;
8088 bufferWalker += bytesReadFromBigBlockFile;
8089 size -= bytesReadFromBigBlockFile;
8090 *bytesRead += bytesReadFromBigBlockFile;
8091 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8094 return S_OK;
8097 /******************************************************************************
8098 * SmallBlockChainStream_WriteAt
8100 * Writes the specified number of bytes to this chain at the specified offset.
8101 * Will fail if not all specified number of bytes have been written.
8103 HRESULT SmallBlockChainStream_WriteAt(
8104 SmallBlockChainStream* This,
8105 ULARGE_INTEGER offset,
8106 ULONG size,
8107 const void* buffer,
8108 ULONG* bytesWritten)
8110 ULARGE_INTEGER offsetInBigBlockFile;
8111 ULONG blockNoInSequence =
8112 offset.LowPart / This->parentStorage->smallBlockSize;
8114 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
8115 ULONG bytesToWriteInBuffer;
8116 ULONG blockIndex;
8117 ULONG bytesWrittenToBigBlockFile;
8118 const BYTE* bufferWalker;
8119 HRESULT res;
8122 * This should never happen on a small block file.
8124 assert(offset.HighPart==0);
8127 * Find the first block in the stream that contains part of the buffer.
8129 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8131 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8133 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8134 return STG_E_DOCFILECORRUPT;
8135 blockNoInSequence--;
8139 * Start writing the buffer.
8141 *bytesWritten = 0;
8142 bufferWalker = buffer;
8143 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8146 * Calculate how many bytes we can copy to this small block.
8148 bytesToWriteInBuffer =
8149 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8152 * Calculate the offset of the small block in the small block file.
8154 offsetInBigBlockFile.QuadPart =
8155 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8157 offsetInBigBlockFile.QuadPart += offsetInBlock;
8160 * Write those bytes in the buffer to the small block file.
8162 res = BlockChainStream_WriteAt(
8163 This->parentStorage->smallBlockRootChain,
8164 offsetInBigBlockFile,
8165 bytesToWriteInBuffer,
8166 bufferWalker,
8167 &bytesWrittenToBigBlockFile);
8168 if (FAILED(res))
8169 return res;
8172 * Step to the next big block.
8174 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8175 if (FAILED(res))
8176 return res;
8177 bufferWalker += bytesWrittenToBigBlockFile;
8178 size -= bytesWrittenToBigBlockFile;
8179 *bytesWritten += bytesWrittenToBigBlockFile;
8180 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8183 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8186 /******************************************************************************
8187 * SmallBlockChainStream_Shrink
8189 * Shrinks this chain in the small block depot.
8191 static BOOL SmallBlockChainStream_Shrink(
8192 SmallBlockChainStream* This,
8193 ULARGE_INTEGER newSize)
8195 ULONG blockIndex, extraBlock;
8196 ULONG numBlocks;
8197 ULONG count = 0;
8199 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
8201 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
8202 numBlocks++;
8204 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8207 * Go to the new end of chain
8209 while (count < numBlocks)
8211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8212 &blockIndex)))
8213 return FALSE;
8214 count++;
8218 * If the count is 0, we have a special case, the head of the chain was
8219 * just freed.
8221 if (count == 0)
8223 DirEntry chainEntry;
8225 StorageImpl_ReadDirEntry(This->parentStorage,
8226 This->ownerDirEntry,
8227 &chainEntry);
8229 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8231 StorageImpl_WriteDirEntry(This->parentStorage,
8232 This->ownerDirEntry,
8233 &chainEntry);
8236 * We start freeing the chain at the head block.
8238 extraBlock = blockIndex;
8240 else
8242 /* Get the next block before marking the new end */
8243 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8244 &extraBlock)))
8245 return FALSE;
8247 /* Mark the new end of chain */
8248 SmallBlockChainStream_SetNextBlockInChain(
8249 This,
8250 blockIndex,
8251 BLOCK_END_OF_CHAIN);
8255 * Mark the extra blocks as free
8257 while (extraBlock != BLOCK_END_OF_CHAIN)
8259 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8260 &blockIndex)))
8261 return FALSE;
8262 SmallBlockChainStream_FreeBlock(This, extraBlock);
8263 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8264 extraBlock = blockIndex;
8267 return TRUE;
8270 /******************************************************************************
8271 * SmallBlockChainStream_Enlarge
8273 * Grows this chain in the small block depot.
8275 static BOOL SmallBlockChainStream_Enlarge(
8276 SmallBlockChainStream* This,
8277 ULARGE_INTEGER newSize)
8279 ULONG blockIndex, currentBlock;
8280 ULONG newNumBlocks;
8281 ULONG oldNumBlocks = 0;
8283 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8286 * Empty chain. Create the head.
8288 if (blockIndex == BLOCK_END_OF_CHAIN)
8290 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8291 SmallBlockChainStream_SetNextBlockInChain(
8292 This,
8293 blockIndex,
8294 BLOCK_END_OF_CHAIN);
8296 if (This->headOfStreamPlaceHolder != NULL)
8298 *(This->headOfStreamPlaceHolder) = blockIndex;
8300 else
8302 DirEntry chainEntry;
8304 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8305 &chainEntry);
8307 chainEntry.startingBlock = blockIndex;
8309 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8310 &chainEntry);
8314 currentBlock = blockIndex;
8317 * Figure out how many blocks are needed to contain this stream
8319 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
8321 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
8322 newNumBlocks++;
8325 * Go to the current end of chain
8327 while (blockIndex != BLOCK_END_OF_CHAIN)
8329 oldNumBlocks++;
8330 currentBlock = blockIndex;
8331 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8332 return FALSE;
8336 * Add new blocks to the chain
8338 while (oldNumBlocks < newNumBlocks)
8340 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8341 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8343 SmallBlockChainStream_SetNextBlockInChain(
8344 This,
8345 blockIndex,
8346 BLOCK_END_OF_CHAIN);
8348 currentBlock = blockIndex;
8349 oldNumBlocks++;
8352 return TRUE;
8355 /******************************************************************************
8356 * SmallBlockChainStream_SetSize
8358 * Sets the size of this stream.
8359 * The file will grow if we grow the chain.
8361 * TODO: Free the actual blocks in the file when we shrink the chain.
8362 * Currently, the blocks are still in the file. So the file size
8363 * doesn't shrink even if we shrink streams.
8365 BOOL SmallBlockChainStream_SetSize(
8366 SmallBlockChainStream* This,
8367 ULARGE_INTEGER newSize)
8369 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8371 if (newSize.LowPart == size.LowPart)
8372 return TRUE;
8374 if (newSize.LowPart < size.LowPart)
8376 SmallBlockChainStream_Shrink(This, newSize);
8378 else
8380 SmallBlockChainStream_Enlarge(This, newSize);
8383 return TRUE;
8386 /******************************************************************************
8387 * SmallBlockChainStream_GetCount
8389 * Returns the number of small blocks that comprises this chain.
8390 * This is not the size of the stream as the last block may not be full!
8393 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8395 ULONG blockIndex;
8396 ULONG count = 0;
8398 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8400 while(blockIndex != BLOCK_END_OF_CHAIN)
8402 count++;
8404 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8405 blockIndex, &blockIndex)))
8406 return 0;
8409 return count;
8412 /******************************************************************************
8413 * SmallBlockChainStream_GetSize
8415 * Returns the size of this chain.
8417 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8419 DirEntry chainEntry;
8421 if(This->headOfStreamPlaceHolder != NULL)
8423 ULARGE_INTEGER result;
8424 result.HighPart = 0;
8426 result.LowPart = SmallBlockChainStream_GetCount(This) *
8427 This->parentStorage->smallBlockSize;
8429 return result;
8432 StorageImpl_ReadDirEntry(
8433 This->parentStorage,
8434 This->ownerDirEntry,
8435 &chainEntry);
8437 return chainEntry.size;
8441 /************************************************************************
8442 * Miscellaneous storage functions
8443 ***********************************************************************/
8445 static HRESULT create_storagefile(
8446 LPCOLESTR pwcsName,
8447 DWORD grfMode,
8448 DWORD grfAttrs,
8449 STGOPTIONS* pStgOptions,
8450 REFIID riid,
8451 void** ppstgOpen)
8453 StorageBaseImpl* newStorage = 0;
8454 HANDLE hFile = INVALID_HANDLE_VALUE;
8455 HRESULT hr = STG_E_INVALIDFLAG;
8456 DWORD shareMode;
8457 DWORD accessMode;
8458 DWORD creationMode;
8459 DWORD fileAttributes;
8460 WCHAR tempFileName[MAX_PATH];
8462 if (ppstgOpen == 0)
8463 return STG_E_INVALIDPOINTER;
8465 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8466 return STG_E_INVALIDPARAMETER;
8468 /* if no share mode given then DENY_NONE is the default */
8469 if (STGM_SHARE_MODE(grfMode) == 0)
8470 grfMode |= STGM_SHARE_DENY_NONE;
8472 if ( FAILED( validateSTGM(grfMode) ))
8473 goto end;
8475 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8476 switch(STGM_ACCESS_MODE(grfMode))
8478 case STGM_WRITE:
8479 case STGM_READWRITE:
8480 break;
8481 default:
8482 goto end;
8485 /* in direct mode, can only use SHARE_EXCLUSIVE */
8486 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8487 goto end;
8489 /* but in transacted mode, any share mode is valid */
8492 * Generate a unique name.
8494 if (pwcsName == 0)
8496 WCHAR tempPath[MAX_PATH];
8498 memset(tempPath, 0, sizeof(tempPath));
8499 memset(tempFileName, 0, sizeof(tempFileName));
8501 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8502 tempPath[0] = '.';
8504 if (GetTempFileNameW(tempPath, L"STO", 0, tempFileName) != 0)
8505 pwcsName = tempFileName;
8506 else
8508 hr = STG_E_INSUFFICIENTMEMORY;
8509 goto end;
8512 creationMode = TRUNCATE_EXISTING;
8514 else
8516 creationMode = GetCreationModeFromSTGM(grfMode);
8520 * Interpret the STGM value grfMode
8522 shareMode = GetShareModeFromSTGM(grfMode);
8523 accessMode = GetAccessModeFromSTGM(grfMode);
8525 if (grfMode & STGM_DELETEONRELEASE)
8526 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8527 else
8528 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8530 *ppstgOpen = 0;
8532 hFile = CreateFileW(pwcsName,
8533 accessMode,
8534 shareMode,
8535 NULL,
8536 creationMode,
8537 fileAttributes,
8540 if (hFile == INVALID_HANDLE_VALUE)
8542 if(GetLastError() == ERROR_FILE_EXISTS)
8543 hr = STG_E_FILEALREADYEXISTS;
8544 else
8545 hr = E_FAIL;
8546 goto end;
8550 * Allocate and initialize the new IStorage object.
8552 hr = Storage_Construct(
8553 hFile,
8554 pwcsName,
8555 NULL,
8556 grfMode,
8557 TRUE,
8558 TRUE,
8559 pStgOptions->ulSectorSize,
8560 &newStorage);
8562 if (FAILED(hr))
8564 goto end;
8567 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8568 IStorage_Release(&newStorage->IStorage_iface);
8570 end:
8571 TRACE("<-- %p r = %#lx\n", *ppstgOpen, hr);
8573 return hr;
8576 /******************************************************************************
8577 * StgCreateDocfile [OLE32.@]
8578 * Creates a new compound file storage object
8580 * PARAMS
8581 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8582 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8583 * reserved [ ?] unused?, usually 0
8584 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8586 * RETURNS
8587 * S_OK if the file was successfully created
8588 * some STG_E_ value if error
8589 * NOTES
8590 * if pwcsName is NULL, create file with new unique name
8591 * the function can returns
8592 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8593 * (unrealized now)
8595 HRESULT WINAPI StgCreateDocfile(
8596 LPCOLESTR pwcsName,
8597 DWORD grfMode,
8598 DWORD reserved,
8599 IStorage **ppstgOpen)
8601 STGOPTIONS stgoptions = {1, 0, 512};
8603 TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName), grfMode, reserved, ppstgOpen);
8605 if (ppstgOpen == 0)
8606 return STG_E_INVALIDPOINTER;
8607 if (reserved != 0)
8608 return STG_E_INVALIDPARAMETER;
8610 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8613 /******************************************************************************
8614 * StgCreateStorageEx [OLE32.@]
8616 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8618 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName),
8619 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8621 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8623 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8624 return STG_E_INVALIDPARAMETER;
8627 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8629 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8630 return STG_E_INVALIDPARAMETER;
8633 if (stgfmt == STGFMT_FILE)
8635 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8636 return STG_E_INVALIDPARAMETER;
8639 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8641 STGOPTIONS defaultOptions = {1, 0, 512};
8643 if (!pStgOptions) pStgOptions = &defaultOptions;
8644 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8648 ERR("Invalid stgfmt argument\n");
8649 return STG_E_INVALIDPARAMETER;
8652 /******************************************************************************
8653 * StgCreatePropSetStg [OLE32.@]
8655 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8656 IPropertySetStorage **propset)
8658 TRACE("%p, %#lx, %p.\n", pstg, reserved, propset);
8659 if (reserved)
8660 return STG_E_INVALIDPARAMETER;
8662 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8665 /******************************************************************************
8666 * StgOpenStorageEx [OLE32.@]
8668 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8670 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName),
8671 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8673 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8675 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8676 return STG_E_INVALIDPARAMETER;
8679 switch (stgfmt)
8681 case STGFMT_FILE:
8682 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8683 return STG_E_INVALIDPARAMETER;
8685 case STGFMT_STORAGE:
8686 break;
8688 case STGFMT_DOCFILE:
8689 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8691 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8692 return STG_E_INVALIDPARAMETER;
8694 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8695 break;
8697 case STGFMT_ANY:
8698 WARN("STGFMT_ANY assuming storage\n");
8699 break;
8701 default:
8702 return STG_E_INVALIDPARAMETER;
8705 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8709 /******************************************************************************
8710 * StgOpenStorage [OLE32.@]
8712 HRESULT WINAPI StgOpenStorage(
8713 const OLECHAR *pwcsName,
8714 IStorage *pstgPriority,
8715 DWORD grfMode,
8716 SNB snbExclude,
8717 DWORD reserved,
8718 IStorage **ppstgOpen)
8720 StorageBaseImpl* newStorage = 0;
8721 HRESULT hr = S_OK;
8722 HANDLE hFile = 0;
8723 DWORD shareMode;
8724 DWORD accessMode;
8725 LPWSTR temp_name = NULL;
8727 TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName), pstgPriority, grfMode,
8728 snbExclude, reserved, ppstgOpen);
8730 if (pstgPriority)
8732 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8733 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8734 if (FAILED(hr)) goto end;
8735 pwcsName = temp_name;
8736 TRACE("using filename %s\n", debugstr_w(temp_name));
8739 if (pwcsName == 0)
8741 hr = STG_E_INVALIDNAME;
8742 goto end;
8745 if (ppstgOpen == 0)
8747 hr = STG_E_INVALIDPOINTER;
8748 goto end;
8751 if (reserved)
8753 hr = STG_E_INVALIDPARAMETER;
8754 goto end;
8757 if (grfMode & STGM_PRIORITY)
8759 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8760 return STG_E_INVALIDFLAG;
8761 if (grfMode & STGM_DELETEONRELEASE)
8762 return STG_E_INVALIDFUNCTION;
8763 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8764 return STG_E_INVALIDFLAG;
8765 grfMode &= ~0xf0; /* remove the existing sharing mode */
8766 grfMode |= STGM_SHARE_DENY_NONE;
8770 * Validate the sharing mode
8772 if (grfMode & STGM_DIRECT_SWMR)
8774 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8775 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8777 hr = STG_E_INVALIDFLAG;
8778 goto end;
8781 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8782 switch(STGM_SHARE_MODE(grfMode))
8784 case STGM_SHARE_EXCLUSIVE:
8785 case STGM_SHARE_DENY_WRITE:
8786 break;
8787 default:
8788 hr = STG_E_INVALIDFLAG;
8789 goto end;
8792 if ( FAILED( validateSTGM(grfMode) ) ||
8793 (grfMode&STGM_CREATE))
8795 hr = STG_E_INVALIDFLAG;
8796 goto end;
8799 /* shared reading requires transacted or single writer mode */
8800 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8801 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8802 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8804 hr = STG_E_INVALIDFLAG;
8805 goto end;
8809 * Interpret the STGM value grfMode
8811 shareMode = GetShareModeFromSTGM(grfMode);
8812 accessMode = GetAccessModeFromSTGM(grfMode);
8814 *ppstgOpen = 0;
8816 hFile = CreateFileW( pwcsName,
8817 accessMode,
8818 shareMode,
8819 NULL,
8820 OPEN_EXISTING,
8821 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8824 if (hFile==INVALID_HANDLE_VALUE)
8826 DWORD last_error = GetLastError();
8828 hr = E_FAIL;
8830 switch (last_error)
8832 case ERROR_FILE_NOT_FOUND:
8833 hr = STG_E_FILENOTFOUND;
8834 break;
8836 case ERROR_PATH_NOT_FOUND:
8837 hr = STG_E_PATHNOTFOUND;
8838 break;
8840 case ERROR_ACCESS_DENIED:
8841 case ERROR_WRITE_PROTECT:
8842 hr = STG_E_ACCESSDENIED;
8843 break;
8845 case ERROR_SHARING_VIOLATION:
8846 hr = STG_E_SHAREVIOLATION;
8847 break;
8849 default:
8850 hr = E_FAIL;
8853 goto end;
8857 * Refuse to open the file if it's too small to be a structured storage file
8858 * FIXME: verify the file when reading instead of here
8860 if (GetFileSize(hFile, NULL) < HEADER_SIZE)
8862 CloseHandle(hFile);
8863 hr = STG_E_FILEALREADYEXISTS;
8864 goto end;
8868 * Allocate and initialize the new IStorage object.
8870 hr = Storage_Construct(
8871 hFile,
8872 pwcsName,
8873 NULL,
8874 grfMode,
8875 TRUE,
8876 FALSE,
8877 512,
8878 &newStorage);
8880 if (FAILED(hr))
8883 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8885 if(hr == STG_E_INVALIDHEADER)
8886 hr = STG_E_FILEALREADYEXISTS;
8887 goto end;
8890 *ppstgOpen = &newStorage->IStorage_iface;
8892 end:
8893 CoTaskMemFree(temp_name);
8894 if (pstgPriority) IStorage_Release(pstgPriority);
8895 TRACE("<-- %#lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8896 return hr;
8899 /******************************************************************************
8900 * StgCreateDocfileOnILockBytes [OLE32.@]
8902 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8903 ILockBytes *plkbyt,
8904 DWORD grfMode,
8905 DWORD reserved,
8906 IStorage** ppstgOpen)
8908 StorageBaseImpl* newStorage = 0;
8909 HRESULT hr = S_OK;
8911 if ((ppstgOpen == 0) || (plkbyt == 0))
8912 return STG_E_INVALIDPOINTER;
8915 * Allocate and initialize the new IStorage object.
8917 hr = Storage_Construct(
8920 plkbyt,
8921 grfMode,
8922 FALSE,
8923 TRUE,
8924 512,
8925 &newStorage);
8927 if (FAILED(hr))
8929 return hr;
8932 *ppstgOpen = &newStorage->IStorage_iface;
8934 return hr;
8937 /******************************************************************************
8938 * StgOpenStorageOnILockBytes [OLE32.@]
8940 HRESULT WINAPI StgOpenStorageOnILockBytes(
8941 ILockBytes *plkbyt,
8942 IStorage *pstgPriority,
8943 DWORD grfMode,
8944 SNB snbExclude,
8945 DWORD reserved,
8946 IStorage **ppstgOpen)
8948 StorageBaseImpl* newStorage = 0;
8949 HRESULT hr = S_OK;
8951 if ((plkbyt == 0) || (ppstgOpen == 0))
8952 return STG_E_INVALIDPOINTER;
8954 if ( FAILED( validateSTGM(grfMode) ))
8955 return STG_E_INVALIDFLAG;
8957 *ppstgOpen = 0;
8960 * Allocate and initialize the new IStorage object.
8962 hr = Storage_Construct(
8965 plkbyt,
8966 grfMode,
8967 FALSE,
8968 FALSE,
8969 512,
8970 &newStorage);
8972 if (FAILED(hr))
8974 return hr;
8977 *ppstgOpen = &newStorage->IStorage_iface;
8979 return hr;
8982 /******************************************************************************
8983 * StgSetTimes [ole32.@]
8984 * StgSetTimes [OLE32.@]
8988 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8989 FILETIME const *patime, FILETIME const *pmtime)
8991 IStorage *stg = NULL;
8992 HRESULT r;
8994 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8996 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8997 0, 0, &stg);
8998 if( SUCCEEDED(r) )
9000 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9001 IStorage_Release(stg);
9004 return r;
9007 /******************************************************************************
9008 * StgIsStorageILockBytes [OLE32.@]
9010 * Determines if the ILockBytes contains a storage object.
9012 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9014 BYTE sig[sizeof(STORAGE_magic)];
9015 ULARGE_INTEGER offset;
9016 ULONG read = 0;
9018 offset.HighPart = 0;
9019 offset.LowPart = 0;
9021 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9023 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9024 return S_OK;
9026 return S_FALSE;
9029 /******************************************************************************
9030 * WriteClassStg [OLE32.@]
9032 * This method will store the specified CLSID in the specified storage object
9034 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9036 if(!pStg)
9037 return E_INVALIDARG;
9039 if(!rclsid)
9040 return STG_E_INVALIDPOINTER;
9042 return IStorage_SetClass(pStg, rclsid);
9045 /***********************************************************************
9046 * ReadClassStg (OLE32.@)
9048 * This method reads the CLSID previously written to a storage object with
9049 * the WriteClassStg.
9051 * PARAMS
9052 * pstg [I] IStorage pointer
9053 * pclsid [O] Pointer to where the CLSID is written
9055 * RETURNS
9056 * Success: S_OK.
9057 * Failure: HRESULT code.
9059 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9061 STATSTG pstatstg;
9062 HRESULT hRes;
9064 TRACE("(%p, %p)\n", pstg, pclsid);
9066 if(!pstg || !pclsid)
9067 return E_INVALIDARG;
9070 * read a STATSTG structure (contains the clsid) from the storage
9072 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9074 if(SUCCEEDED(hRes))
9075 *pclsid=pstatstg.clsid;
9077 return hRes;
9080 /***********************************************************************
9081 * OleLoadFromStream (OLE32.@)
9083 * This function loads an object from stream
9085 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9087 CLSID clsid;
9088 HRESULT res;
9089 LPPERSISTSTREAM xstm;
9091 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9093 res=ReadClassStm(pStm,&clsid);
9094 if (FAILED(res))
9095 return res;
9096 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9097 if (FAILED(res))
9098 return res;
9099 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9100 if (FAILED(res)) {
9101 IUnknown_Release((IUnknown*)*ppvObj);
9102 return res;
9104 res=IPersistStream_Load(xstm,pStm);
9105 IPersistStream_Release(xstm);
9106 /* FIXME: all refcounts ok at this point? I think they should be:
9107 * pStm : unchanged
9108 * ppvObj : 1
9109 * xstm : 0 (released)
9111 return res;
9114 /***********************************************************************
9115 * OleSaveToStream (OLE32.@)
9117 * This function saves an object with the IPersistStream interface on it
9118 * to the specified stream.
9120 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9123 CLSID clsid;
9124 HRESULT res;
9126 TRACE("(%p,%p)\n",pPStm,pStm);
9128 res=IPersistStream_GetClassID(pPStm,&clsid);
9130 if (SUCCEEDED(res)){
9132 res=WriteClassStm(pStm,&clsid);
9134 if (SUCCEEDED(res))
9136 res=IPersistStream_Save(pPStm,pStm,TRUE);
9139 TRACE("Finished Save\n");
9140 return res;
9143 /*************************************************************************
9144 * STORAGE_CreateOleStream [Internal]
9146 * Creates the "\001OLE" stream in the IStorage if necessary.
9148 * PARAMS
9149 * storage [I] Dest storage to create the stream in
9150 * flags [I] flags to be set for newly created stream
9152 * RETURNS
9153 * HRESULT return value
9155 * NOTES
9157 * This stream is still unknown, MS Word seems to have extra data
9158 * but since the data is stored in the OLESTREAM there should be
9159 * no need to recreate the stream. If the stream is manually
9160 * deleted it will create it with this default data.
9163 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9165 static const DWORD version_magic = 0x02000001;
9166 IStream *stream;
9167 HRESULT hr;
9169 hr = IStorage_CreateStream(storage, L"\1Ole", STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9170 if (hr == S_OK)
9172 struct empty_1ole_stream {
9173 DWORD version_magic;
9174 DWORD flags;
9175 DWORD update_options;
9176 DWORD reserved;
9177 DWORD mon_stream_size;
9179 struct empty_1ole_stream stream_data;
9181 stream_data.version_magic = version_magic;
9182 stream_data.flags = flags;
9183 stream_data.update_options = 0;
9184 stream_data.reserved = 0;
9185 stream_data.mon_stream_size = 0;
9187 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9188 IStream_Release(stream);
9191 return hr;
9194 /* write a string to a stream, preceded by its length */
9195 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9197 HRESULT r;
9198 LPSTR str;
9199 DWORD len = 0;
9201 if( string )
9202 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9203 r = IStream_Write( stm, &len, sizeof(len), NULL);
9204 if( FAILED( r ) )
9205 return r;
9206 if(len == 0)
9207 return r;
9208 str = CoTaskMemAlloc( len );
9209 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9210 r = IStream_Write( stm, str, len, NULL);
9211 CoTaskMemFree( str );
9212 return r;
9215 /* read a string preceded by its length from a stream */
9216 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9218 HRESULT r;
9219 DWORD len, count = 0;
9220 LPSTR str;
9221 LPWSTR wstr;
9223 r = IStream_Read( stm, &len, sizeof(len), &count );
9224 if( FAILED( r ) )
9225 return r;
9226 if( count != sizeof(len) )
9227 return E_OUTOFMEMORY;
9229 TRACE("%ld bytes\n",len);
9231 str = CoTaskMemAlloc( len );
9232 if( !str )
9233 return E_OUTOFMEMORY;
9234 count = 0;
9235 r = IStream_Read( stm, str, len, &count );
9236 if( FAILED( r ) )
9238 CoTaskMemFree( str );
9239 return r;
9241 if( count != len )
9243 CoTaskMemFree( str );
9244 return E_OUTOFMEMORY;
9247 TRACE("Read string %s\n",debugstr_an(str,len));
9249 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9250 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9251 if( wstr )
9253 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9254 wstr[len] = 0;
9256 CoTaskMemFree( str );
9258 *string = wstr;
9260 return r;
9264 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9265 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9267 IStream *pstm;
9268 HRESULT r = S_OK;
9270 static const BYTE unknown1[12] =
9271 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9272 0xFF, 0xFF, 0xFF, 0xFF};
9273 static const BYTE unknown2[16] =
9274 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9277 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9278 debugstr_w(lpszUserType), debugstr_w(szClipName),
9279 debugstr_w(szProgIDName));
9281 /* Create a CompObj stream */
9282 r = IStorage_CreateStream(pstg, L"\1CompObj",
9283 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9284 if( FAILED (r) )
9285 return r;
9287 /* Write CompObj Structure to stream */
9288 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9290 if( SUCCEEDED( r ) )
9291 r = WriteClassStm( pstm, clsid );
9293 if( SUCCEEDED( r ) )
9294 r = STREAM_WriteString( pstm, lpszUserType );
9295 if( SUCCEEDED( r ) )
9296 r = STREAM_WriteString( pstm, szClipName );
9297 if( SUCCEEDED( r ) )
9298 r = STREAM_WriteString( pstm, szProgIDName );
9299 if( SUCCEEDED( r ) )
9300 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9302 IStream_Release( pstm );
9304 return r;
9307 /***********************************************************************
9308 * WriteFmtUserTypeStg (OLE32.@)
9310 HRESULT WINAPI WriteFmtUserTypeStg(
9311 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9313 STATSTG stat;
9314 HRESULT r;
9315 WCHAR szwClipName[0x40];
9316 CLSID clsid;
9317 LPWSTR wstrProgID = NULL;
9318 DWORD n;
9320 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9322 /* get the clipboard format name */
9323 if( cf )
9325 n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName));
9326 szwClipName[n]=0;
9329 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9331 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9332 if(SUCCEEDED(r))
9333 clsid = stat.clsid;
9334 else
9335 clsid = CLSID_NULL;
9337 ProgIDFromCLSID(&clsid, &wstrProgID);
9339 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9341 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9342 cf ? szwClipName : NULL, wstrProgID );
9344 CoTaskMemFree(wstrProgID);
9346 return r;
9350 /******************************************************************************
9351 * ReadFmtUserTypeStg [OLE32.@]
9353 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9355 HRESULT r;
9356 IStream *stm = 0;
9357 unsigned char unknown1[12];
9358 unsigned char unknown2[16];
9359 DWORD count;
9360 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9361 CLSID clsid;
9363 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9365 r = IStorage_OpenStream( pstg, L"\1CompObj", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9366 if( FAILED ( r ) )
9368 WARN("Failed to open stream r = %#lx\n", r);
9369 return r;
9372 /* read the various parts of the structure */
9373 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9374 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9375 goto end;
9376 r = ReadClassStm( stm, &clsid );
9377 if( FAILED( r ) )
9378 goto end;
9380 r = STREAM_ReadString( stm, &szCLSIDName );
9381 if( FAILED( r ) )
9382 goto end;
9384 r = STREAM_ReadString( stm, &szOleTypeName );
9385 if( FAILED( r ) )
9386 goto end;
9388 r = STREAM_ReadString( stm, &szProgIDName );
9389 if( FAILED( r ) )
9390 goto end;
9392 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9393 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9394 goto end;
9396 /* ok, success... now we just need to store what we found */
9397 if( pcf )
9398 *pcf = RegisterClipboardFormatW( szOleTypeName );
9400 if( lplpszUserType )
9402 *lplpszUserType = szCLSIDName;
9403 szCLSIDName = NULL;
9406 end:
9407 CoTaskMemFree( szCLSIDName );
9408 CoTaskMemFree( szOleTypeName );
9409 CoTaskMemFree( szProgIDName );
9410 IStream_Release( stm );
9412 return r;
9415 /******************************************************************************
9416 * StgIsStorageFile [OLE32.@]
9417 * Verify if the file contains a storage object
9419 * PARAMS
9420 * fn [ I] Filename
9422 * RETURNS
9423 * S_OK if file has magic bytes as a storage object
9424 * S_FALSE if file is not storage
9426 HRESULT WINAPI
9427 StgIsStorageFile(LPCOLESTR fn)
9429 HANDLE hf;
9430 BYTE magic[8];
9431 DWORD bytes_read;
9433 TRACE("%s\n", debugstr_w(fn));
9434 hf = CreateFileW(fn, GENERIC_READ,
9435 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9436 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9438 if (hf == INVALID_HANDLE_VALUE)
9439 return STG_E_FILENOTFOUND;
9441 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9443 WARN(" unable to read file\n");
9444 CloseHandle(hf);
9445 return S_FALSE;
9448 CloseHandle(hf);
9450 if (bytes_read != 8) {
9451 TRACE(" too short\n");
9452 return S_FALSE;
9455 if (!memcmp(magic,STORAGE_magic,8)) {
9456 TRACE(" -> YES\n");
9457 return S_OK;
9460 TRACE(" -> Invalid header.\n");
9461 return S_FALSE;
9464 /***********************************************************************
9465 * WriteClassStm (OLE32.@)
9467 * Writes a CLSID to a stream.
9469 * PARAMS
9470 * pStm [I] Stream to write to.
9471 * rclsid [I] CLSID to write.
9473 * RETURNS
9474 * Success: S_OK.
9475 * Failure: HRESULT code.
9477 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9479 TRACE("(%p,%p)\n",pStm,rclsid);
9481 if (!pStm || !rclsid)
9482 return E_INVALIDARG;
9484 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9487 /***********************************************************************
9488 * ReadClassStm (OLE32.@)
9490 * Reads a CLSID from a stream.
9492 * PARAMS
9493 * pStm [I] Stream to read from.
9494 * rclsid [O] CLSID to read.
9496 * RETURNS
9497 * Success: S_OK.
9498 * Failure: HRESULT code.
9500 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9502 ULONG nbByte;
9503 HRESULT res;
9505 TRACE("(%p,%p)\n",pStm,pclsid);
9507 if (!pStm || !pclsid)
9508 return E_INVALIDARG;
9510 /* clear the output args */
9511 *pclsid = CLSID_NULL;
9513 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9515 if (FAILED(res))
9516 return res;
9518 if (nbByte != sizeof(CLSID))
9519 return STG_E_READFAULT;
9520 else
9521 return S_OK;
9525 /************************************************************************
9526 * OleConvert Functions
9527 ***********************************************************************/
9529 #define OLESTREAM_ID 0x501
9530 #define OLESTREAM_MAX_STR_LEN 255
9532 /* OLESTREAM memory structure to use for Get and Put Routines */
9533 typedef struct
9535 DWORD dwOleID;
9536 DWORD dwTypeID;
9537 DWORD dwOleTypeNameLength;
9538 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9539 CHAR *pstrOleObjFileName;
9540 DWORD dwOleObjFileNameLength;
9541 DWORD dwMetaFileWidth;
9542 DWORD dwMetaFileHeight;
9543 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9544 DWORD dwDataLength;
9545 BYTE *pData;
9546 } OLECONVERT_OLESTREAM_DATA;
9548 /* CompObj Stream structure */
9549 typedef struct
9551 BYTE byUnknown1[12];
9552 CLSID clsid;
9553 DWORD dwCLSIDNameLength;
9554 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9555 DWORD dwOleTypeNameLength;
9556 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9557 DWORD dwProgIDNameLength;
9558 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9559 BYTE byUnknown2[16];
9560 } OLECONVERT_ISTORAGE_COMPOBJ;
9562 /* Ole Presentation Stream structure */
9563 typedef struct
9565 BYTE byUnknown1[28];
9566 DWORD dwExtentX;
9567 DWORD dwExtentY;
9568 DWORD dwSize;
9569 BYTE *pData;
9570 } OLECONVERT_ISTORAGE_OLEPRES;
9573 /*************************************************************************
9574 * OLECONVERT_LoadOLE10 [Internal]
9576 * Loads the OLE10 STREAM to memory
9578 * PARAMS
9579 * pOleStream [I] The OLESTREAM
9580 * pData [I] Data Structure for the OLESTREAM Data
9582 * RETURNS
9583 * Success: S_OK
9584 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9585 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9587 * NOTES
9588 * This function is used by OleConvertOLESTREAMToIStorage only.
9590 * Memory allocated for pData must be freed by the caller
9592 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStream1)
9594 DWORD dwSize;
9595 HRESULT hRes = S_OK;
9596 int nTryCnt=0;
9597 int max_try = 6;
9599 pData->pData = NULL;
9600 pData->pstrOleObjFileName = NULL;
9602 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9604 /* Get the OleID */
9605 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9606 if(dwSize != sizeof(pData->dwOleID))
9608 hRes = CONVERT10_E_OLESTREAM_GET;
9610 else if(pData->dwOleID != OLESTREAM_ID)
9612 hRes = CONVERT10_E_OLESTREAM_FMT;
9614 else
9616 hRes = S_OK;
9617 break;
9621 if(hRes == S_OK)
9623 /* Get the TypeID... more info needed for this field */
9624 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9625 if(dwSize != sizeof(pData->dwTypeID))
9627 hRes = CONVERT10_E_OLESTREAM_GET;
9630 if(hRes == S_OK)
9632 if(pData->dwTypeID != 0)
9634 /* Get the length of the OleTypeName */
9635 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9636 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9638 hRes = CONVERT10_E_OLESTREAM_GET;
9641 if(hRes == S_OK)
9643 if(pData->dwOleTypeNameLength > 0)
9645 /* Get the OleTypeName */
9646 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9647 if(dwSize != pData->dwOleTypeNameLength)
9649 hRes = CONVERT10_E_OLESTREAM_GET;
9653 if(bStream1)
9655 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9656 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9658 hRes = CONVERT10_E_OLESTREAM_GET;
9660 if(hRes == S_OK)
9662 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9663 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9664 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9665 if(pData->pstrOleObjFileName)
9667 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9668 if(dwSize != pData->dwOleObjFileNameLength)
9670 hRes = CONVERT10_E_OLESTREAM_GET;
9673 else
9674 hRes = CONVERT10_E_OLESTREAM_GET;
9677 else
9679 /* Get the Width of the Metafile */
9680 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9681 if(dwSize != sizeof(pData->dwMetaFileWidth))
9683 hRes = CONVERT10_E_OLESTREAM_GET;
9685 if(hRes == S_OK)
9687 /* Get the Height of the Metafile */
9688 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9689 if(dwSize != sizeof(pData->dwMetaFileHeight))
9691 hRes = CONVERT10_E_OLESTREAM_GET;
9695 if(hRes == S_OK)
9697 /* Get the Length of the Data */
9698 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9699 if(dwSize != sizeof(pData->dwDataLength))
9701 hRes = CONVERT10_E_OLESTREAM_GET;
9705 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9707 if(!bStream1) /* if it is a second OLE stream data */
9709 pData->dwDataLength -= 8;
9710 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9711 if(dwSize != sizeof(pData->strUnknown))
9713 hRes = CONVERT10_E_OLESTREAM_GET;
9717 if(hRes == S_OK)
9719 if(pData->dwDataLength > 0)
9721 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9723 /* Get Data (ex. IStorage, Metafile, or BMP) */
9724 if(pData->pData)
9726 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9727 if(dwSize != pData->dwDataLength)
9729 hRes = CONVERT10_E_OLESTREAM_GET;
9732 else
9734 hRes = CONVERT10_E_OLESTREAM_GET;
9740 return hRes;
9743 /*************************************************************************
9744 * OLECONVERT_SaveOLE10 [Internal]
9746 * Saves the OLE10 STREAM From memory
9748 * PARAMS
9749 * pData [I] Data Structure for the OLESTREAM Data
9750 * pOleStream [I] The OLESTREAM to save
9752 * RETURNS
9753 * Success: S_OK
9754 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9756 * NOTES
9757 * This function is used by OleConvertIStorageToOLESTREAM only.
9760 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9762 DWORD dwSize;
9763 HRESULT hRes = S_OK;
9766 /* Set the OleID */
9767 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9768 if(dwSize != sizeof(pData->dwOleID))
9770 hRes = CONVERT10_E_OLESTREAM_PUT;
9773 if(hRes == S_OK)
9775 /* Set the TypeID */
9776 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9777 if(dwSize != sizeof(pData->dwTypeID))
9779 hRes = CONVERT10_E_OLESTREAM_PUT;
9783 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9785 /* Set the Length of the OleTypeName */
9786 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9787 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9789 hRes = CONVERT10_E_OLESTREAM_PUT;
9792 if(hRes == S_OK)
9794 if(pData->dwOleTypeNameLength > 0)
9796 /* Set the OleTypeName */
9797 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9798 if(dwSize != pData->dwOleTypeNameLength)
9800 hRes = CONVERT10_E_OLESTREAM_PUT;
9805 if(hRes == S_OK)
9807 /* Set the width of the Metafile */
9808 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9809 if(dwSize != sizeof(pData->dwMetaFileWidth))
9811 hRes = CONVERT10_E_OLESTREAM_PUT;
9815 if(hRes == S_OK)
9817 /* Set the height of the Metafile */
9818 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9819 if(dwSize != sizeof(pData->dwMetaFileHeight))
9821 hRes = CONVERT10_E_OLESTREAM_PUT;
9825 if(hRes == S_OK)
9827 /* Set the length of the Data */
9828 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9829 if(dwSize != sizeof(pData->dwDataLength))
9831 hRes = CONVERT10_E_OLESTREAM_PUT;
9835 if(hRes == S_OK)
9837 if(pData->dwDataLength > 0)
9839 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9840 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9841 if(dwSize != pData->dwDataLength)
9843 hRes = CONVERT10_E_OLESTREAM_PUT;
9848 return hRes;
9851 /*************************************************************************
9852 * OLECONVERT_GetOLE20FromOLE10[Internal]
9854 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9855 * opens it, and copies the content to the dest IStorage for
9856 * OleConvertOLESTREAMToIStorage
9859 * PARAMS
9860 * pDestStorage [I] The IStorage to copy the data to
9861 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9862 * nBufferLength [I] The size of the buffer
9864 * RETURNS
9865 * Nothing
9867 * NOTES
9871 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9873 HRESULT hRes;
9874 HANDLE hFile;
9875 IStorage *pTempStorage;
9876 DWORD dwNumOfBytesWritten;
9877 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9879 /* Create a temp File */
9880 GetTempPathW(MAX_PATH, wstrTempDir);
9881 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9882 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9884 if(hFile != INVALID_HANDLE_VALUE)
9886 /* Write IStorage Data to File */
9887 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9888 CloseHandle(hFile);
9890 /* Open and copy temp storage to the Dest Storage */
9891 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9892 if(hRes == S_OK)
9894 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9895 IStorage_Release(pTempStorage);
9897 DeleteFileW(wstrTempFile);
9902 /*************************************************************************
9903 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9905 * Saves the OLE10 STREAM From memory
9907 * PARAMS
9908 * pStorage [I] The Src IStorage to copy
9909 * pData [I] The Dest Memory to write to.
9911 * RETURNS
9912 * The size in bytes allocated for pData
9914 * NOTES
9915 * Memory allocated for pData must be freed by the caller
9917 * Used by OleConvertIStorageToOLESTREAM only.
9920 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9922 HANDLE hFile;
9923 HRESULT hRes;
9924 DWORD nDataLength = 0;
9925 IStorage *pTempStorage;
9926 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9928 *pData = NULL;
9930 /* Create temp Storage */
9931 GetTempPathW(MAX_PATH, wstrTempDir);
9932 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9933 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9935 if(hRes == S_OK)
9937 /* Copy Src Storage to the Temp Storage */
9938 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9939 IStorage_Release(pTempStorage);
9941 /* Open Temp Storage as a file and copy to memory */
9942 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9943 if(hFile != INVALID_HANDLE_VALUE)
9945 nDataLength = GetFileSize(hFile, NULL);
9946 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9947 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9948 CloseHandle(hFile);
9950 DeleteFileW(wstrTempFile);
9952 return nDataLength;
9955 /*************************************************************************
9956 * OLECONVERT_CreateCompObjStream [Internal]
9958 * Creates a "\001CompObj" is the destination IStorage if necessary.
9960 * PARAMS
9961 * pStorage [I] The dest IStorage to create the CompObj Stream
9962 * if necessary.
9963 * strOleTypeName [I] The ProgID
9965 * RETURNS
9966 * Success: S_OK
9967 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9969 * NOTES
9970 * This function is used by OleConvertOLESTREAMToIStorage only.
9972 * The stream data is stored in the OLESTREAM and there should be
9973 * no need to recreate the stream. If the stream is manually
9974 * deleted it will attempt to create it by querying the registry.
9978 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9980 IStream *pStream;
9981 HRESULT hStorageRes, hRes = S_OK;
9982 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9983 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9985 static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9986 static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9988 /* Initialize the CompObj structure */
9989 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9990 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9991 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9994 /* Create a CompObj stream if it doesn't exist */
9995 hStorageRes = IStorage_CreateStream(pStorage, L"\1CompObj",
9996 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9997 if(hStorageRes == S_OK)
9999 /* copy the OleTypeName to the compobj struct */
10000 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10001 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10003 /* copy the OleTypeName to the compobj struct */
10004 /* Note: in the test made, these were Identical */
10005 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10006 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10008 /* Get the CLSID */
10009 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10010 bufferW, OLESTREAM_MAX_STR_LEN );
10011 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10013 if(hRes == S_OK)
10015 HKEY hKey;
10016 LONG hErr;
10017 /* Get the CLSID Default Name from the Registry */
10018 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10019 if(hErr == ERROR_SUCCESS)
10021 char strTemp[OLESTREAM_MAX_STR_LEN];
10022 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10023 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10024 if(hErr == ERROR_SUCCESS)
10026 strcpy(IStorageCompObj.strCLSIDName, strTemp);
10028 RegCloseKey(hKey);
10032 /* Write CompObj Structure to stream */
10033 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10035 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10037 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10038 if(IStorageCompObj.dwCLSIDNameLength > 0)
10040 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10042 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10043 if(IStorageCompObj.dwOleTypeNameLength > 0)
10045 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10047 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10048 if(IStorageCompObj.dwProgIDNameLength > 0)
10050 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10052 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10053 IStream_Release(pStream);
10055 return hRes;
10059 /*************************************************************************
10060 * OLECONVERT_CreateOlePresStream[Internal]
10062 * Creates the "\002OlePres000" Stream with the Metafile data
10064 * PARAMS
10065 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10066 * dwExtentX [I] Width of the Metafile
10067 * dwExtentY [I] Height of the Metafile
10068 * pData [I] Metafile data
10069 * dwDataLength [I] Size of the Metafile data
10071 * RETURNS
10072 * Success: S_OK
10073 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10075 * NOTES
10076 * This function is used by OleConvertOLESTREAMToIStorage only.
10079 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10081 HRESULT hRes;
10082 IStream *pStream;
10083 static const BYTE pOlePresStreamHeader[] =
10085 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10086 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10087 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10088 0x00, 0x00, 0x00, 0x00
10091 static const BYTE pOlePresStreamHeaderEmpty[] =
10093 0x00, 0x00, 0x00, 0x00,
10094 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10095 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10096 0x00, 0x00, 0x00, 0x00
10099 /* Create the OlePres000 Stream */
10100 hRes = IStorage_CreateStream(pStorage, L"\2OlePres000",
10101 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10103 if(hRes == S_OK)
10105 DWORD nHeaderSize;
10106 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10108 memset(&OlePres, 0, sizeof(OlePres));
10109 /* Do we have any metafile data to save */
10110 if(dwDataLength > 0)
10112 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10113 nHeaderSize = sizeof(pOlePresStreamHeader);
10115 else
10117 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10118 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10120 /* Set width and height of the metafile */
10121 OlePres.dwExtentX = dwExtentX;
10122 OlePres.dwExtentY = -dwExtentY;
10124 /* Set Data and Length */
10125 if(dwDataLength > sizeof(METAFILEPICT16))
10127 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10128 OlePres.pData = &(pData[8]);
10130 /* Save OlePres000 Data to Stream */
10131 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10132 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10133 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10134 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10135 if(OlePres.dwSize > 0)
10137 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10139 IStream_Release(pStream);
10143 /*************************************************************************
10144 * OLECONVERT_CreateOle10NativeStream [Internal]
10146 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10148 * PARAMS
10149 * pStorage [I] Dest storage to create the stream in
10150 * pData [I] Ole10 Native Data (ex. bmp)
10151 * dwDataLength [I] Size of the Ole10 Native Data
10153 * RETURNS
10154 * Nothing
10156 * NOTES
10157 * This function is used by OleConvertOLESTREAMToIStorage only.
10159 * Might need to verify the data and return appropriate error message
10162 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10164 HRESULT hRes;
10165 IStream *pStream;
10167 /* Create the Ole10Native Stream */
10168 hRes = IStorage_CreateStream(pStorage, L"\1Ole10Native",
10169 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10171 if(hRes == S_OK)
10173 /* Write info to stream */
10174 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10175 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10176 IStream_Release(pStream);
10181 /*************************************************************************
10182 * OLECONVERT_GetOLE10ProgID [Internal]
10184 * Finds the ProgID (or OleTypeID) from the IStorage
10186 * PARAMS
10187 * pStorage [I] The Src IStorage to get the ProgID
10188 * strProgID [I] the ProgID string to get
10189 * dwSize [I] the size of the string
10191 * RETURNS
10192 * Success: S_OK
10193 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10195 * NOTES
10196 * This function is used by OleConvertIStorageToOLESTREAM only.
10200 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10202 HRESULT hRes;
10203 IStream *pStream;
10204 LARGE_INTEGER iSeekPos;
10205 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10207 /* Open the CompObj Stream */
10208 hRes = IStorage_OpenStream(pStorage, L"\1CompObj", NULL,
10209 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10210 if(hRes == S_OK)
10213 /*Get the OleType from the CompObj Stream */
10214 iSeekPos.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10215 iSeekPos.HighPart = 0;
10217 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10218 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10219 iSeekPos.LowPart = CompObj.dwCLSIDNameLength;
10220 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10221 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10222 iSeekPos.LowPart = CompObj.dwOleTypeNameLength;
10223 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10225 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10226 if(*dwSize > 0)
10228 IStream_Read(pStream, strProgID, *dwSize, NULL);
10230 IStream_Release(pStream);
10232 else
10234 STATSTG stat;
10235 LPOLESTR wstrProgID;
10237 /* Get the OleType from the registry */
10238 REFCLSID clsid = &(stat.clsid);
10239 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10240 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10241 if(hRes == S_OK)
10243 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10244 CoTaskMemFree(wstrProgID);
10248 return hRes;
10251 /*************************************************************************
10252 * OLECONVERT_GetOle10PresData [Internal]
10254 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10256 * PARAMS
10257 * pStorage [I] Src IStroage
10258 * pOleStream [I] Dest OleStream Mem Struct
10260 * RETURNS
10261 * Nothing
10263 * NOTES
10264 * This function is used by OleConvertIStorageToOLESTREAM only.
10266 * Memory allocated for pData must be freed by the caller
10270 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10273 HRESULT hRes;
10274 IStream *pStream;
10276 /* Initialize Default data for OLESTREAM */
10277 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10278 pOleStreamData[0].dwTypeID = 2;
10279 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10280 pOleStreamData[1].dwTypeID = 0;
10281 pOleStreamData[0].dwMetaFileWidth = 0;
10282 pOleStreamData[0].dwMetaFileHeight = 0;
10283 pOleStreamData[0].pData = NULL;
10284 pOleStreamData[1].pData = NULL;
10286 /* Open Ole10Native Stream */
10287 hRes = IStorage_OpenStream(pStorage, L"\1Ole10Native", NULL,
10288 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10289 if(hRes == S_OK)
10292 /* Read Size and Data */
10293 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10294 if(pOleStreamData->dwDataLength > 0)
10296 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10297 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10299 IStream_Release(pStream);
10305 /*************************************************************************
10306 * OLECONVERT_GetOle20PresData[Internal]
10308 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10310 * PARAMS
10311 * pStorage [I] Src IStroage
10312 * pOleStreamData [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
10322 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10324 HRESULT hRes;
10325 IStream *pStream;
10326 OLECONVERT_ISTORAGE_OLEPRES olePress;
10328 /* Initialize Default data for OLESTREAM */
10329 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10330 pOleStreamData[0].dwTypeID = 2;
10331 pOleStreamData[0].dwMetaFileWidth = 0;
10332 pOleStreamData[0].dwMetaFileHeight = 0;
10333 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10334 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10335 pOleStreamData[1].dwTypeID = 0;
10336 pOleStreamData[1].dwOleTypeNameLength = 0;
10337 pOleStreamData[1].strOleTypeName[0] = 0;
10338 pOleStreamData[1].dwMetaFileWidth = 0;
10339 pOleStreamData[1].dwMetaFileHeight = 0;
10340 pOleStreamData[1].pData = NULL;
10341 pOleStreamData[1].dwDataLength = 0;
10344 /* Open OlePress000 stream */
10345 hRes = IStorage_OpenStream(pStorage, L"\2OlePres000", NULL,
10346 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10347 if(hRes == S_OK)
10349 LARGE_INTEGER iSeekPos;
10350 METAFILEPICT16 MetaFilePict;
10351 static const char strMetafilePictName[] = "METAFILEPICT";
10353 /* Set the TypeID for a Metafile */
10354 pOleStreamData[1].dwTypeID = 5;
10356 /* Set the OleTypeName to Metafile */
10357 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10358 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10360 iSeekPos.HighPart = 0;
10361 iSeekPos.LowPart = sizeof(olePress.byUnknown1);
10363 /* Get Presentation Data */
10364 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10365 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10366 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10367 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10369 /*Set width and Height */
10370 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10371 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10372 if(olePress.dwSize > 0)
10374 /* Set Length */
10375 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10377 /* Set MetaFilePict struct */
10378 MetaFilePict.mm = 8;
10379 MetaFilePict.xExt = olePress.dwExtentX;
10380 MetaFilePict.yExt = olePress.dwExtentY;
10381 MetaFilePict.hMF = 0;
10383 /* Get Metafile Data */
10384 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10385 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10386 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10388 IStream_Release(pStream);
10392 /*************************************************************************
10393 * OleConvertOLESTREAMToIStorage [OLE32.@]
10395 * Read info on MSDN
10397 * TODO
10398 * DVTARGETDEVICE parameter is not handled
10399 * Still unsure of some mem fields for OLE 10 Stream
10400 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10401 * and "\001OLE" streams
10404 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10405 LPOLESTREAM pOleStream,
10406 LPSTORAGE pstg,
10407 const DVTARGETDEVICE* ptd)
10409 int i;
10410 HRESULT hRes=S_OK;
10411 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10413 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10415 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10417 if(ptd != NULL)
10419 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10422 if(pstg == NULL || pOleStream == NULL)
10424 hRes = E_INVALIDARG;
10427 if(hRes == S_OK)
10429 /* Load the OLESTREAM to Memory */
10430 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10433 if(hRes == S_OK)
10435 /* Load the OLESTREAM to Memory (part 2)*/
10436 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10439 if(hRes == S_OK)
10442 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10444 /* Do we have the IStorage Data in the OLESTREAM */
10445 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10447 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10448 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10450 else
10452 /* It must be an original OLE 1.0 source */
10453 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10456 else
10458 /* It must be an original OLE 1.0 source */
10459 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10462 /* Create CompObj Stream if necessary */
10463 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10464 if(hRes == S_OK)
10466 /*Create the Ole Stream if necessary */
10467 STORAGE_CreateOleStream(pstg, 0);
10472 /* Free allocated memory */
10473 for(i=0; i < 2; i++)
10475 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10476 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10477 pOleStreamData[i].pstrOleObjFileName = NULL;
10479 return hRes;
10482 /*************************************************************************
10483 * OleConvertIStorageToOLESTREAM [OLE32.@]
10485 * Read info on MSDN
10487 * Read info on MSDN
10489 * TODO
10490 * Still unsure of some mem fields for OLE 10 Stream
10491 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10492 * and "\001OLE" streams.
10495 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10496 LPSTORAGE pstg,
10497 LPOLESTREAM pOleStream)
10499 int i;
10500 HRESULT hRes = S_OK;
10501 IStream *pStream;
10502 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10504 TRACE("%p %p\n", pstg, pOleStream);
10506 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10508 if(pstg == NULL || pOleStream == NULL)
10510 hRes = E_INVALIDARG;
10512 if(hRes == S_OK)
10514 /* Get the ProgID */
10515 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10516 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10518 if(hRes == S_OK)
10520 /* Was it originally Ole10 */
10521 hRes = IStorage_OpenStream(pstg, L"\1Ole10Native", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10522 if(hRes == S_OK)
10524 IStream_Release(pStream);
10525 /* Get Presentation Data for Ole10Native */
10526 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10528 else
10530 /* Get Presentation Data (OLE20) */
10531 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10534 /* Save OLESTREAM */
10535 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10536 if(hRes == S_OK)
10538 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10543 /* Free allocated memory */
10544 for(i=0; i < 2; i++)
10546 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10549 return hRes;
10552 enum stream_1ole_flags {
10553 OleStream_LinkedObject = 0x00000001,
10554 OleStream_Convert = 0x00000004
10557 /*************************************************************************
10558 * OleConvertIStorageToOLESTREAMEx [OLE32.@]
10560 HRESULT WINAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg, CLIPFORMAT cf, LONG width, LONG height,
10561 DWORD size, LPSTGMEDIUM medium, LPOLESTREAM olestream )
10563 FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg, cf, width, height, size, medium, olestream);
10565 return E_NOTIMPL;
10568 /***********************************************************************
10569 * GetConvertStg (OLE32.@)
10571 HRESULT WINAPI GetConvertStg(IStorage *stg)
10573 static const DWORD version_magic = 0x02000001;
10574 DWORD header[2];
10575 IStream *stream;
10576 HRESULT hr;
10578 TRACE("%p\n", stg);
10580 if (!stg) return E_INVALIDARG;
10582 hr = IStorage_OpenStream(stg, L"\1Ole", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10583 if (FAILED(hr)) return hr;
10585 hr = IStream_Read(stream, header, sizeof(header), NULL);
10586 IStream_Release(stream);
10587 if (FAILED(hr)) return hr;
10589 if (header[0] != version_magic)
10591 ERR("got wrong version magic for 1Ole stream, %#lx.\n", header[0]);
10592 return E_FAIL;
10595 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10598 /***********************************************************************
10599 * SetConvertStg (OLE32.@)
10601 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10603 DWORD flags = convert ? OleStream_Convert : 0;
10604 IStream *stream;
10605 DWORD header[2];
10606 HRESULT hr;
10608 TRACE("(%p, %d)\n", storage, convert);
10610 hr = IStorage_OpenStream(storage, L"\1Ole", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10611 if (FAILED(hr))
10613 if (hr != STG_E_FILENOTFOUND)
10614 return hr;
10616 return STORAGE_CreateOleStream(storage, flags);
10619 hr = IStream_Read(stream, header, sizeof(header), NULL);
10620 if (FAILED(hr))
10622 IStream_Release(stream);
10623 return hr;
10626 /* update flag if differs */
10627 if ((header[1] ^ flags) & OleStream_Convert)
10629 LARGE_INTEGER pos = {{0}};
10631 if (header[1] & OleStream_Convert)
10632 flags = header[1] & ~OleStream_Convert;
10633 else
10634 flags = header[1] | OleStream_Convert;
10636 pos.QuadPart = sizeof(DWORD);
10637 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10638 if (FAILED(hr))
10640 IStream_Release(stream);
10641 return hr;
10644 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10647 IStream_Release(stream);
10648 return hr;