mf/session: Forward more events to the application.
[wine/zf.git] / dlls / ole32 / storage32.c
blob980b6c8d9f6249fbd1bb1f8862b179aa432aa172
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winnls.h"
45 #include "winuser.h"
46 #include "wine/debug.h"
48 #include "storage32.h"
49 #include "ole2.h" /* For Write/ReadClassStm */
51 #include "winreg.h"
52 #include "wine/wingdi16.h"
53 #include "compobj_private.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 * These are signatures to detect the type of Document file.
61 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
62 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
64 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
67 /****************************************************************************
68 * StorageInternalImpl definitions.
70 * Definition of the implementation structure for the IStorage interface.
71 * This one implements the IStorage interface for storage that are
72 * inside another storage.
74 typedef struct StorageInternalImpl
76 struct StorageBaseImpl base;
79 * Entry in the parent's stream tracking list
81 struct list ParentListEntry;
83 StorageBaseImpl *parentStorage;
84 } StorageInternalImpl;
86 static const IStorageVtbl StorageInternalImpl_Vtbl;
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
89 typedef struct TransactedDirEntry
91 /* If applicable, a reference to the original DirEntry in the transacted
92 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
93 DirRef transactedParentEntry;
95 /* True if this entry is being used. */
96 BOOL inuse;
98 /* True if data is up to date. */
99 BOOL read;
101 /* True if this entry has been modified. */
102 BOOL dirty;
104 /* True if this entry's stream has been modified. */
105 BOOL stream_dirty;
107 /* True if this entry has been deleted in the transacted storage, but the
108 * delete has not yet been committed. */
109 BOOL deleted;
111 /* If this entry's stream has been modified, a reference to where the stream
112 * is stored in the snapshot file. */
113 DirRef stream_entry;
115 /* This directory entry's data, including any changes that have been made. */
116 DirEntry data;
118 /* A reference to the parent of this node. This is only valid while we are
119 * committing changes. */
120 DirRef parent;
122 /* A reference to a newly-created entry in the transacted parent. This is
123 * always equal to transactedParentEntry except when committing changes. */
124 DirRef newTransactedParentEntry;
125 } TransactedDirEntry;
128 /****************************************************************************
129 * Transacted storage object.
131 typedef struct TransactedSnapshotImpl
133 struct StorageBaseImpl base;
136 * Modified streams are temporarily saved to the scratch file.
138 StorageBaseImpl *scratch;
140 /* The directory structure is kept here, so that we can track how these
141 * entries relate to those in the parent storage. */
142 TransactedDirEntry *entries;
143 ULONG entries_size;
144 ULONG firstFreeEntry;
147 * Changes are committed to the transacted parent.
149 StorageBaseImpl *transactedParent;
151 /* The transaction signature from when we last committed */
152 ULONG lastTransactionSig;
153 } TransactedSnapshotImpl;
155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
156 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
158 typedef struct TransactedSharedImpl
160 struct StorageBaseImpl base;
163 * Snapshot and uncommitted changes go here.
165 TransactedSnapshotImpl *scratch;
168 * Changes are committed to the transacted parent.
170 StorageBaseImpl *transactedParent;
172 /* The transaction signature from when we last committed */
173 ULONG lastTransactionSig;
174 } TransactedSharedImpl;
177 /****************************************************************************
178 * BlockChainStream definitions.
180 * The BlockChainStream class is a utility class that is used to create an
181 * abstraction of the big block chains in the storage file.
184 struct BlockChainRun
186 /* This represents a range of blocks that happen reside in consecutive sectors. */
187 ULONG firstSector;
188 ULONG firstOffset;
189 ULONG lastOffset;
192 typedef struct BlockChainBlock
194 ULONG index;
195 ULONG sector;
196 BOOL read;
197 BOOL dirty;
198 BYTE data[MAX_BIG_BLOCK_SIZE];
199 } BlockChainBlock;
201 struct BlockChainStream
203 StorageImpl* parentStorage;
204 ULONG* headOfStreamPlaceHolder;
205 DirRef ownerDirEntry;
206 struct BlockChainRun* indexCache;
207 ULONG indexCacheLen;
208 ULONG indexCacheSize;
209 BlockChainBlock cachedBlocks[2];
210 ULONG blockToEvict;
211 ULONG tailIndex;
212 ULONG numBlocks;
215 /* Returns the number of blocks that comprises this chain.
216 * This is not the size of the stream as the last block may not be full!
218 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
220 return This->numBlocks;
223 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
224 static void BlockChainStream_Destroy(BlockChainStream*);
225 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
226 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
227 static HRESULT BlockChainStream_Flush(BlockChainStream*);
228 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
229 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
232 /****************************************************************************
233 * SmallBlockChainStream definitions.
235 * The SmallBlockChainStream class is a utility class that is used to create an
236 * abstraction of the small block chains in the storage file.
239 struct SmallBlockChainStream
241 StorageImpl* parentStorage;
242 DirRef ownerDirEntry;
243 ULONG* headOfStreamPlaceHolder;
246 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
247 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
248 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
249 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
250 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
251 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
254 /************************************************************************
255 * STGM Functions
256 ***********************************************************************/
258 /************************************************************************
259 * This method validates an STGM parameter that can contain the values below
261 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
262 * The stgm values contained in 0xffff0000 are bitmasks.
264 * STGM_DIRECT 0x00000000
265 * STGM_TRANSACTED 0x00010000
266 * STGM_SIMPLE 0x08000000
268 * STGM_READ 0x00000000
269 * STGM_WRITE 0x00000001
270 * STGM_READWRITE 0x00000002
272 * STGM_SHARE_DENY_NONE 0x00000040
273 * STGM_SHARE_DENY_READ 0x00000030
274 * STGM_SHARE_DENY_WRITE 0x00000020
275 * STGM_SHARE_EXCLUSIVE 0x00000010
277 * STGM_PRIORITY 0x00040000
278 * STGM_DELETEONRELEASE 0x04000000
280 * STGM_CREATE 0x00001000
281 * STGM_CONVERT 0x00020000
282 * STGM_FAILIFTHERE 0x00000000
284 * STGM_NOSCRATCH 0x00100000
285 * STGM_NOSNAPSHOT 0x00200000
287 static HRESULT validateSTGM(DWORD stgm)
289 DWORD access = STGM_ACCESS_MODE(stgm);
290 DWORD share = STGM_SHARE_MODE(stgm);
291 DWORD create = STGM_CREATE_MODE(stgm);
293 if (stgm&~STGM_KNOWN_FLAGS)
295 ERR("unknown flags %08x\n", stgm);
296 return E_FAIL;
299 switch (access)
301 case STGM_READ:
302 case STGM_WRITE:
303 case STGM_READWRITE:
304 break;
305 default:
306 return E_FAIL;
309 switch (share)
311 case STGM_SHARE_DENY_NONE:
312 case STGM_SHARE_DENY_READ:
313 case STGM_SHARE_DENY_WRITE:
314 case STGM_SHARE_EXCLUSIVE:
315 break;
316 case 0:
317 if (!(stgm & STGM_TRANSACTED))
318 return E_FAIL;
319 break;
320 default:
321 return E_FAIL;
324 switch (create)
326 case STGM_CREATE:
327 case STGM_FAILIFTHERE:
328 break;
329 default:
330 return E_FAIL;
334 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
336 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
337 return E_FAIL;
340 * STGM_CREATE | STGM_CONVERT
341 * if both are false, STGM_FAILIFTHERE is set to TRUE
343 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
344 return E_FAIL;
347 * STGM_NOSCRATCH requires STGM_TRANSACTED
349 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
350 return E_FAIL;
353 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
354 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
356 if ( (stgm & STGM_NOSNAPSHOT) &&
357 (!(stgm & STGM_TRANSACTED) ||
358 share == STGM_SHARE_EXCLUSIVE ||
359 share == STGM_SHARE_DENY_WRITE) )
360 return E_FAIL;
362 return S_OK;
365 /************************************************************************
366 * GetShareModeFromSTGM
368 * This method will return a share mode flag from a STGM value.
369 * The STGM value is assumed valid.
371 static DWORD GetShareModeFromSTGM(DWORD stgm)
373 switch (STGM_SHARE_MODE(stgm))
375 case 0:
376 assert(stgm & STGM_TRANSACTED);
377 /* fall-through */
378 case STGM_SHARE_DENY_NONE:
379 return FILE_SHARE_READ | FILE_SHARE_WRITE;
380 case STGM_SHARE_DENY_READ:
381 return FILE_SHARE_WRITE;
382 case STGM_SHARE_DENY_WRITE:
383 case STGM_SHARE_EXCLUSIVE:
384 return FILE_SHARE_READ;
386 ERR("Invalid share mode!\n");
387 assert(0);
388 return 0;
391 /************************************************************************
392 * GetAccessModeFromSTGM
394 * This method will return an access mode flag from a STGM value.
395 * The STGM value is assumed valid.
397 static DWORD GetAccessModeFromSTGM(DWORD stgm)
399 switch (STGM_ACCESS_MODE(stgm))
401 case STGM_READ:
402 return GENERIC_READ;
403 case STGM_WRITE:
404 case STGM_READWRITE:
405 return GENERIC_READ | GENERIC_WRITE;
407 ERR("Invalid access mode!\n");
408 assert(0);
409 return 0;
412 /************************************************************************
413 * GetCreationModeFromSTGM
415 * This method will return a creation mode flag from a STGM value.
416 * The STGM value is assumed valid.
418 static DWORD GetCreationModeFromSTGM(DWORD stgm)
420 switch(STGM_CREATE_MODE(stgm))
422 case STGM_CREATE:
423 return CREATE_ALWAYS;
424 case STGM_CONVERT:
425 FIXME("STGM_CONVERT not implemented!\n");
426 return CREATE_NEW;
427 case STGM_FAILIFTHERE:
428 return CREATE_NEW;
430 ERR("Invalid create mode!\n");
431 assert(0);
432 return 0;
436 /************************************************************************
437 * IDirectWriterLock implementation
438 ***********************************************************************/
440 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
442 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
445 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
447 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
448 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
451 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
453 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
454 return IStorage_AddRef(&This->IStorage_iface);
457 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
459 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
460 return IStorage_Release(&This->IStorage_iface);
463 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
465 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
466 FIXME("(%p)->(%d): stub\n", This, timeout);
467 return E_NOTIMPL;
470 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
472 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
473 FIXME("(%p): stub\n", This);
474 return E_NOTIMPL;
477 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
479 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
480 FIXME("(%p): stub\n", This);
481 return E_NOTIMPL;
484 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
486 directwriterlock_QueryInterface,
487 directwriterlock_AddRef,
488 directwriterlock_Release,
489 directwriterlock_WaitForWriteAccess,
490 directwriterlock_ReleaseWriteAccess,
491 directwriterlock_HaveWriteAccess
495 /************************************************************************
496 * StorageBaseImpl implementation : Tree helper functions
497 ***********************************************************************/
499 /****************************************************************************
501 * Internal Method
503 * Case insensitive comparison of DirEntry.name by first considering
504 * their size.
506 * Returns <0 when name1 < name2
507 * >0 when name1 > name2
508 * 0 when name1 == name2
510 static LONG entryNameCmp(
511 const OLECHAR *name1,
512 const OLECHAR *name2)
514 LONG diff = lstrlenW(name1) - lstrlenW(name2);
516 while (diff == 0 && *name1 != 0)
519 * We compare the string themselves only when they are of the same length
521 diff = towupper(*name1++) - towupper(*name2++);
524 return diff;
527 /****************************************************************************
529 * Internal Method
531 * Find and read the element of a storage with the given name.
533 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
534 const OLECHAR *name, DirEntry *data)
536 DirRef currentEntry;
538 /* Read the storage entry to find the root of the tree. */
539 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
541 currentEntry = data->dirRootEntry;
543 while (currentEntry != DIRENTRY_NULL)
545 LONG cmp;
547 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
549 cmp = entryNameCmp(name, data->name);
551 if (cmp == 0)
552 /* found it */
553 break;
555 else if (cmp < 0)
556 currentEntry = data->leftChild;
558 else if (cmp > 0)
559 currentEntry = data->rightChild;
562 return currentEntry;
565 /****************************************************************************
567 * Internal Method
569 * Find and read the binary tree parent of the element with the given name.
571 * If there is no such element, find a place where it could be inserted and
572 * return STG_E_FILENOTFOUND.
574 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
575 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
576 ULONG *relation)
578 DirRef childEntry;
579 DirEntry childData;
581 /* Read the storage entry to find the root of the tree. */
582 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
584 *parentEntry = storageEntry;
585 *relation = DIRENTRY_RELATION_DIR;
587 childEntry = parentData->dirRootEntry;
589 while (childEntry != DIRENTRY_NULL)
591 LONG cmp;
593 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
595 cmp = entryNameCmp(childName, childData.name);
597 if (cmp == 0)
598 /* found it */
599 break;
601 else if (cmp < 0)
603 *parentData = childData;
604 *parentEntry = childEntry;
605 *relation = DIRENTRY_RELATION_PREVIOUS;
607 childEntry = parentData->leftChild;
610 else if (cmp > 0)
612 *parentData = childData;
613 *parentEntry = childEntry;
614 *relation = DIRENTRY_RELATION_NEXT;
616 childEntry = parentData->rightChild;
620 if (childEntry == DIRENTRY_NULL)
621 return STG_E_FILENOTFOUND;
622 else
623 return S_OK;
626 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
628 switch (relation)
630 case DIRENTRY_RELATION_PREVIOUS:
631 entry->leftChild = new_target;
632 break;
633 case DIRENTRY_RELATION_NEXT:
634 entry->rightChild = new_target;
635 break;
636 case DIRENTRY_RELATION_DIR:
637 entry->dirRootEntry = new_target;
638 break;
639 default:
640 assert(0);
644 /****************************************************************************
646 * Internal Method
648 * Add a directory entry to a storage
650 static HRESULT insertIntoTree(
651 StorageBaseImpl *This,
652 DirRef parentStorageIndex,
653 DirRef newEntryIndex)
655 DirEntry currentEntry;
656 DirEntry newEntry;
659 * Read the inserted entry
661 StorageBaseImpl_ReadDirEntry(This,
662 newEntryIndex,
663 &newEntry);
666 * Read the storage entry
668 StorageBaseImpl_ReadDirEntry(This,
669 parentStorageIndex,
670 &currentEntry);
672 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
675 * The root storage contains some element, therefore, start the research
676 * for the appropriate location.
678 BOOL found = FALSE;
679 DirRef current, next, previous, currentEntryId;
682 * Keep a reference to the root of the storage's element tree
684 currentEntryId = currentEntry.dirRootEntry;
687 * Read
689 StorageBaseImpl_ReadDirEntry(This,
690 currentEntry.dirRootEntry,
691 &currentEntry);
693 previous = currentEntry.leftChild;
694 next = currentEntry.rightChild;
695 current = currentEntryId;
697 while (!found)
699 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
701 if (diff < 0)
703 if (previous != DIRENTRY_NULL)
705 StorageBaseImpl_ReadDirEntry(This,
706 previous,
707 &currentEntry);
708 current = previous;
710 else
712 currentEntry.leftChild = newEntryIndex;
713 StorageBaseImpl_WriteDirEntry(This,
714 current,
715 &currentEntry);
716 found = TRUE;
719 else if (diff > 0)
721 if (next != DIRENTRY_NULL)
723 StorageBaseImpl_ReadDirEntry(This,
724 next,
725 &currentEntry);
726 current = next;
728 else
730 currentEntry.rightChild = newEntryIndex;
731 StorageBaseImpl_WriteDirEntry(This,
732 current,
733 &currentEntry);
734 found = TRUE;
737 else
740 * Trying to insert an item with the same name in the
741 * subtree structure.
743 return STG_E_FILEALREADYEXISTS;
746 previous = currentEntry.leftChild;
747 next = currentEntry.rightChild;
750 else
753 * The storage is empty, make the new entry the root of its element tree
755 currentEntry.dirRootEntry = newEntryIndex;
756 StorageBaseImpl_WriteDirEntry(This,
757 parentStorageIndex,
758 &currentEntry);
761 return S_OK;
764 /*************************************************************************
766 * Internal Method
768 * This method removes a directory entry from its parent storage tree without
769 * freeing any resources attached to it.
771 static HRESULT removeFromTree(
772 StorageBaseImpl *This,
773 DirRef parentStorageIndex,
774 DirRef deletedIndex)
776 DirEntry entryToDelete;
777 DirEntry parentEntry;
778 DirRef parentEntryRef;
779 ULONG typeOfRelation;
780 HRESULT hr;
782 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
784 if (hr != S_OK)
785 return hr;
788 * Find the element that links to the one we want to delete.
790 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
791 &parentEntry, &parentEntryRef, &typeOfRelation);
793 if (hr != S_OK)
794 return hr;
796 if (entryToDelete.leftChild != DIRENTRY_NULL)
799 * Replace the deleted entry with its left child
801 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
803 hr = StorageBaseImpl_WriteDirEntry(
804 This,
805 parentEntryRef,
806 &parentEntry);
807 if(FAILED(hr))
809 return hr;
812 if (entryToDelete.rightChild != DIRENTRY_NULL)
815 * We need to reinsert the right child somewhere. We already know it and
816 * its children are greater than everything in the left tree, so we
817 * insert it at the rightmost point in the left tree.
819 DirRef newRightChildParent = entryToDelete.leftChild;
820 DirEntry newRightChildParentEntry;
824 hr = StorageBaseImpl_ReadDirEntry(
825 This,
826 newRightChildParent,
827 &newRightChildParentEntry);
828 if (FAILED(hr))
830 return hr;
833 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
834 newRightChildParent = newRightChildParentEntry.rightChild;
835 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
837 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
839 hr = StorageBaseImpl_WriteDirEntry(
840 This,
841 newRightChildParent,
842 &newRightChildParentEntry);
843 if (FAILED(hr))
845 return hr;
849 else
852 * Replace the deleted entry with its right child
854 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
856 hr = StorageBaseImpl_WriteDirEntry(
857 This,
858 parentEntryRef,
859 &parentEntry);
860 if(FAILED(hr))
862 return hr;
866 return hr;
870 /************************************************************************
871 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
872 ***********************************************************************/
875 * IEnumSTATSTGImpl definitions.
877 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
878 * This class allows iterating through the content of a storage and finding
879 * specific items inside it.
881 struct IEnumSTATSTGImpl
883 IEnumSTATSTG IEnumSTATSTG_iface;
885 LONG ref; /* Reference count */
886 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
887 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
889 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
892 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
894 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
897 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
899 IStorage_Release(&This->parentStorage->IStorage_iface);
900 HeapFree(GetProcessHeap(), 0, This);
903 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
904 IEnumSTATSTG* iface,
905 REFIID riid,
906 void** ppvObject)
908 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
910 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
912 if (ppvObject==0)
913 return E_INVALIDARG;
915 *ppvObject = 0;
917 if (IsEqualGUID(&IID_IUnknown, riid) ||
918 IsEqualGUID(&IID_IEnumSTATSTG, riid))
920 *ppvObject = &This->IEnumSTATSTG_iface;
921 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
922 TRACE("<-- %p\n", *ppvObject);
923 return S_OK;
926 TRACE("<-- E_NOINTERFACE\n");
927 return E_NOINTERFACE;
930 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
931 IEnumSTATSTG* iface)
933 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
934 return InterlockedIncrement(&This->ref);
937 static ULONG WINAPI IEnumSTATSTGImpl_Release(
938 IEnumSTATSTG* iface)
940 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
942 ULONG newRef;
944 newRef = InterlockedDecrement(&This->ref);
946 if (newRef==0)
948 IEnumSTATSTGImpl_Destroy(This);
951 return newRef;
954 static HRESULT IEnumSTATSTGImpl_GetNextRef(
955 IEnumSTATSTGImpl* This,
956 DirRef *ref)
958 DirRef result = DIRENTRY_NULL;
959 DirRef searchNode;
960 DirEntry entry;
961 HRESULT hr;
962 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
964 TRACE("%p,%p\n", This, ref);
966 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
967 This->parentStorage->storageDirEntry, &entry);
968 searchNode = entry.dirRootEntry;
970 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
972 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
974 if (SUCCEEDED(hr))
976 LONG diff = entryNameCmp( entry.name, This->name);
978 if (diff <= 0)
980 searchNode = entry.rightChild;
982 else
984 result = searchNode;
985 memcpy(result_name, entry.name, sizeof(result_name));
986 searchNode = entry.leftChild;
991 if (SUCCEEDED(hr))
993 *ref = result;
994 if (result != DIRENTRY_NULL)
995 memcpy(This->name, result_name, sizeof(result_name));
998 TRACE("<-- %08x\n", hr);
999 return hr;
1002 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
1003 IEnumSTATSTG* iface,
1004 ULONG celt,
1005 STATSTG* rgelt,
1006 ULONG* pceltFetched)
1008 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1010 DirEntry currentEntry;
1011 STATSTG* currentReturnStruct = rgelt;
1012 ULONG objectFetched = 0;
1013 DirRef currentSearchNode;
1014 HRESULT hr=S_OK;
1016 TRACE("%p,%u,%p,%p\n", iface, celt, rgelt, pceltFetched);
1018 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
1019 return E_INVALIDARG;
1021 if (This->parentStorage->reverted)
1023 TRACE("<-- STG_E_REVERTED\n");
1024 return STG_E_REVERTED;
1028 * To avoid the special case, get another pointer to a ULONG value if
1029 * the caller didn't supply one.
1031 if (pceltFetched==0)
1032 pceltFetched = &objectFetched;
1035 * Start the iteration, we will iterate until we hit the end of the
1036 * linked list or until we hit the number of items to iterate through
1038 *pceltFetched = 0;
1040 while ( *pceltFetched < celt )
1042 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1044 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1046 memset(currentReturnStruct, 0, sizeof(*currentReturnStruct));
1047 break;
1051 * Read the entry from the storage.
1053 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
1054 currentSearchNode,
1055 &currentEntry);
1056 if (FAILED(hr)) break;
1059 * Copy the information to the return buffer.
1061 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1062 currentReturnStruct,
1063 &currentEntry,
1064 STATFLAG_DEFAULT);
1067 * Step to the next item in the iteration
1069 (*pceltFetched)++;
1070 currentReturnStruct++;
1073 if (SUCCEEDED(hr) && *pceltFetched != celt)
1074 hr = S_FALSE;
1076 TRACE("<-- %08x (asked %u, got %u)\n", hr, celt, *pceltFetched);
1077 return hr;
1081 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1082 IEnumSTATSTG* iface,
1083 ULONG celt)
1085 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1087 ULONG objectFetched = 0;
1088 DirRef currentSearchNode;
1089 HRESULT hr=S_OK;
1091 TRACE("%p,%u\n", iface, celt);
1093 if (This->parentStorage->reverted)
1095 TRACE("<-- STG_E_REVERTED\n");
1096 return STG_E_REVERTED;
1099 while ( (objectFetched < celt) )
1101 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1103 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1104 break;
1106 objectFetched++;
1109 if (SUCCEEDED(hr) && objectFetched != celt)
1110 return S_FALSE;
1112 TRACE("<-- %08x\n", hr);
1113 return hr;
1116 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
1117 IEnumSTATSTG* iface)
1119 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1121 TRACE("%p\n", iface);
1123 if (This->parentStorage->reverted)
1125 TRACE("<-- STG_E_REVERTED\n");
1126 return STG_E_REVERTED;
1129 This->name[0] = 0;
1131 return S_OK;
1134 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1136 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
1137 IEnumSTATSTG* iface,
1138 IEnumSTATSTG** ppenum)
1140 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1141 IEnumSTATSTGImpl* newClone;
1143 TRACE("%p,%p\n", iface, ppenum);
1145 if (This->parentStorage->reverted)
1147 TRACE("<-- STG_E_REVERTED\n");
1148 return STG_E_REVERTED;
1151 if (ppenum==0)
1152 return E_INVALIDARG;
1154 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1155 This->storageDirEntry);
1156 if (!newClone)
1158 *ppenum = NULL;
1159 return E_OUTOFMEMORY;
1163 * The new clone enumeration must point to the same current node as
1164 * the old one.
1166 memcpy(newClone->name, This->name, sizeof(newClone->name));
1168 *ppenum = &newClone->IEnumSTATSTG_iface;
1170 return S_OK;
1174 * Virtual function table for the IEnumSTATSTGImpl class.
1176 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1178 IEnumSTATSTGImpl_QueryInterface,
1179 IEnumSTATSTGImpl_AddRef,
1180 IEnumSTATSTGImpl_Release,
1181 IEnumSTATSTGImpl_Next,
1182 IEnumSTATSTGImpl_Skip,
1183 IEnumSTATSTGImpl_Reset,
1184 IEnumSTATSTGImpl_Clone
1187 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
1188 StorageBaseImpl* parentStorage,
1189 DirRef storageDirEntry)
1191 IEnumSTATSTGImpl* newEnumeration;
1193 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1195 if (newEnumeration)
1197 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1198 newEnumeration->ref = 1;
1199 newEnumeration->name[0] = 0;
1202 * We want to nail-down the reference to the storage in case the
1203 * enumeration out-lives the storage in the client application.
1205 newEnumeration->parentStorage = parentStorage;
1206 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1208 newEnumeration->storageDirEntry = storageDirEntry;
1211 return newEnumeration;
1215 /************************************************************************
1216 * StorageBaseImpl implementation
1217 ***********************************************************************/
1219 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
1221 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1224 /************************************************************************
1225 * StorageBaseImpl_QueryInterface (IUnknown)
1227 * This method implements the common QueryInterface for all IStorage
1228 * implementations contained in this file.
1230 * See Windows documentation for more details on IUnknown methods.
1232 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
1233 IStorage* iface,
1234 REFIID riid,
1235 void** ppvObject)
1237 StorageBaseImpl *This = impl_from_IStorage(iface);
1239 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
1241 if (!ppvObject)
1242 return E_INVALIDARG;
1244 *ppvObject = 0;
1246 if (IsEqualGUID(&IID_IUnknown, riid) ||
1247 IsEqualGUID(&IID_IStorage, riid))
1249 *ppvObject = &This->IStorage_iface;
1251 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1253 *ppvObject = &This->IPropertySetStorage_iface;
1255 /* locking interface is reported for writer only */
1256 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1258 *ppvObject = &This->IDirectWriterLock_iface;
1260 else
1262 TRACE("<-- E_NOINTERFACE\n");
1263 return E_NOINTERFACE;
1266 IStorage_AddRef(iface);
1267 TRACE("<-- %p\n", *ppvObject);
1268 return S_OK;
1271 /************************************************************************
1272 * StorageBaseImpl_AddRef (IUnknown)
1274 * This method implements the common AddRef for all IStorage
1275 * implementations contained in this file.
1277 * See Windows documentation for more details on IUnknown methods.
1279 static ULONG WINAPI StorageBaseImpl_AddRef(
1280 IStorage* iface)
1282 StorageBaseImpl *This = impl_from_IStorage(iface);
1283 ULONG ref = InterlockedIncrement(&This->ref);
1285 TRACE("(%p) AddRef to %d\n", This, ref);
1287 return ref;
1290 /************************************************************************
1291 * StorageBaseImpl_Release (IUnknown)
1293 * This method implements the common Release for all IStorage
1294 * implementations contained in this file.
1296 * See Windows documentation for more details on IUnknown methods.
1298 static ULONG WINAPI StorageBaseImpl_Release(
1299 IStorage* iface)
1301 StorageBaseImpl *This = impl_from_IStorage(iface);
1303 ULONG ref = InterlockedDecrement(&This->ref);
1305 TRACE("(%p) ReleaseRef to %d\n", This, ref);
1307 if (ref == 0)
1310 * Since we are using a system of base-classes, we want to call the
1311 * destructor of the appropriate derived class. To do this, we are
1312 * using virtual functions to implement the destructor.
1314 StorageBaseImpl_Destroy(This);
1317 return ref;
1320 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1321 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1322 SNB snbExclude, IStorage *pstgDest);
1324 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1325 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1326 SNB snbExclude, IStorage *pstgDest)
1328 DirEntry data;
1329 HRESULT hr;
1330 BOOL skip = FALSE;
1331 IStorage *pstgTmp;
1332 IStream *pstrChild, *pstrTmp;
1333 STATSTG strStat;
1335 if (srcEntry == DIRENTRY_NULL)
1336 return S_OK;
1338 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1340 if (FAILED(hr))
1341 return hr;
1343 if ( snbExclude )
1345 WCHAR **snb = snbExclude;
1347 while ( *snb != NULL && !skip )
1349 if ( wcscmp(data.name, *snb) == 0 )
1350 skip = TRUE;
1351 ++snb;
1355 if (!skip)
1357 if (data.stgType == STGTY_STORAGE && !skip_storage)
1360 * create a new storage in destination storage
1362 hr = IStorage_CreateStorage( pstgDest, data.name,
1363 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1364 0, 0,
1365 &pstgTmp );
1368 * if it already exist, don't create a new one use this one
1370 if (hr == STG_E_FILEALREADYEXISTS)
1372 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1373 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1374 NULL, 0, &pstgTmp );
1377 if (SUCCEEDED(hr))
1379 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1380 skip_stream, NULL, pstgTmp );
1382 IStorage_Release(pstgTmp);
1385 else if (data.stgType == STGTY_STREAM && !skip_stream)
1388 * create a new stream in destination storage. If the stream already
1389 * exist, it will be deleted and a new one will be created.
1391 hr = IStorage_CreateStream( pstgDest, data.name,
1392 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1393 0, 0, &pstrTmp );
1396 * open child stream storage. This operation must succeed even if the
1397 * stream is already open, so we use internal functions to do it.
1399 if (hr == S_OK)
1401 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1403 if (streamimpl)
1405 pstrChild = &streamimpl->IStream_iface;
1406 if (pstrChild)
1407 IStream_AddRef(pstrChild);
1409 else
1411 pstrChild = NULL;
1412 hr = E_OUTOFMEMORY;
1416 if (hr == S_OK)
1419 * Get the size of the source stream
1421 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1424 * Set the size of the destination stream.
1426 IStream_SetSize(pstrTmp, strStat.cbSize);
1429 * do the copy
1431 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1432 NULL, NULL );
1434 IStream_Release( pstrChild );
1437 IStream_Release( pstrTmp );
1441 /* copy siblings */
1442 if (SUCCEEDED(hr))
1443 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1444 skip_stream, snbExclude, pstgDest );
1446 if (SUCCEEDED(hr))
1447 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1448 skip_stream, snbExclude, pstgDest );
1450 TRACE("<-- %08x\n", hr);
1451 return hr;
1454 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1456 StgStreamImpl *strm;
1458 TRACE("%p,%d\n", stg, streamEntry);
1460 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1462 if (strm->dirEntry == streamEntry)
1464 return TRUE;
1468 return FALSE;
1471 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1473 StorageInternalImpl *childstg;
1475 TRACE("%p,%d\n", stg, storageEntry);
1477 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1479 if (childstg->base.storageDirEntry == storageEntry)
1481 return TRUE;
1485 return FALSE;
1488 /************************************************************************
1489 * StorageBaseImpl_OpenStream (IStorage)
1491 * This method will open the specified stream object from the current storage.
1493 * See Windows documentation for more details on IStorage methods.
1495 static HRESULT WINAPI StorageBaseImpl_OpenStream(
1496 IStorage* iface,
1497 const OLECHAR* pwcsName, /* [string][in] */
1498 void* reserved1, /* [unique][in] */
1499 DWORD grfMode, /* [in] */
1500 DWORD reserved2, /* [in] */
1501 IStream** ppstm) /* [out] */
1503 StorageBaseImpl *This = impl_from_IStorage(iface);
1504 StgStreamImpl* newStream;
1505 DirEntry currentEntry;
1506 DirRef streamEntryRef;
1507 HRESULT res = STG_E_UNKNOWN;
1509 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1510 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1512 if ( (pwcsName==NULL) || (ppstm==0) )
1514 res = E_INVALIDARG;
1515 goto end;
1518 *ppstm = NULL;
1520 if ( FAILED( validateSTGM(grfMode) ) ||
1521 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1523 res = STG_E_INVALIDFLAG;
1524 goto end;
1528 * As documented.
1530 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1532 res = STG_E_INVALIDFUNCTION;
1533 goto end;
1536 if (This->reverted)
1538 res = STG_E_REVERTED;
1539 goto end;
1543 * Check that we're compatible with the parent's storage mode, but
1544 * only if we are not in transacted mode
1546 if(!(This->openFlags & STGM_TRANSACTED)) {
1547 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1549 res = STG_E_INVALIDFLAG;
1550 goto end;
1555 * Search for the element with the given name
1557 streamEntryRef = findElement(
1558 This,
1559 This->storageDirEntry,
1560 pwcsName,
1561 &currentEntry);
1564 * If it was found, construct the stream object and return a pointer to it.
1566 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1567 (currentEntry.stgType==STGTY_STREAM) )
1569 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1571 /* A single stream cannot be opened a second time. */
1572 res = STG_E_ACCESSDENIED;
1573 goto end;
1576 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1578 if (newStream)
1580 newStream->grfMode = grfMode;
1581 *ppstm = &newStream->IStream_iface;
1583 IStream_AddRef(*ppstm);
1585 res = S_OK;
1586 goto end;
1589 res = E_OUTOFMEMORY;
1590 goto end;
1593 res = STG_E_FILENOTFOUND;
1595 end:
1596 if (res == S_OK)
1597 TRACE("<-- IStream %p\n", *ppstm);
1598 TRACE("<-- %08x\n", res);
1599 return res;
1602 /************************************************************************
1603 * StorageBaseImpl_OpenStorage (IStorage)
1605 * This method will open a new storage object from the current storage.
1607 * See Windows documentation for more details on IStorage methods.
1609 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
1610 IStorage* iface,
1611 const OLECHAR* pwcsName, /* [string][unique][in] */
1612 IStorage* pstgPriority, /* [unique][in] */
1613 DWORD grfMode, /* [in] */
1614 SNB snbExclude, /* [unique][in] */
1615 DWORD reserved, /* [in] */
1616 IStorage** ppstg) /* [out] */
1618 StorageBaseImpl *This = impl_from_IStorage(iface);
1619 StorageInternalImpl* newStorage;
1620 StorageBaseImpl* newTransactedStorage;
1621 DirEntry currentEntry;
1622 DirRef storageEntryRef;
1623 HRESULT res = STG_E_UNKNOWN;
1625 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1626 iface, debugstr_w(pwcsName), pstgPriority,
1627 grfMode, snbExclude, reserved, ppstg);
1629 if ((pwcsName==NULL) || (ppstg==0) )
1631 res = E_INVALIDARG;
1632 goto end;
1635 if (This->openFlags & STGM_SIMPLE)
1637 res = STG_E_INVALIDFUNCTION;
1638 goto end;
1641 /* as documented */
1642 if (snbExclude != NULL)
1644 res = STG_E_INVALIDPARAMETER;
1645 goto end;
1648 if ( FAILED( validateSTGM(grfMode) ))
1650 res = STG_E_INVALIDFLAG;
1651 goto end;
1655 * As documented.
1657 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1658 (grfMode & STGM_DELETEONRELEASE) ||
1659 (grfMode & STGM_PRIORITY) )
1661 res = STG_E_INVALIDFUNCTION;
1662 goto end;
1665 if (This->reverted)
1666 return STG_E_REVERTED;
1669 * Check that we're compatible with the parent's storage mode,
1670 * but only if we are not transacted
1672 if(!(This->openFlags & STGM_TRANSACTED)) {
1673 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1675 res = STG_E_ACCESSDENIED;
1676 goto end;
1680 *ppstg = NULL;
1682 storageEntryRef = findElement(
1683 This,
1684 This->storageDirEntry,
1685 pwcsName,
1686 &currentEntry);
1688 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1689 (currentEntry.stgType==STGTY_STORAGE) )
1691 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1693 /* A single storage cannot be opened a second time. */
1694 res = STG_E_ACCESSDENIED;
1695 goto end;
1698 newStorage = StorageInternalImpl_Construct(
1699 This,
1700 grfMode,
1701 storageEntryRef);
1703 if (newStorage != 0)
1705 if (grfMode & STGM_TRANSACTED)
1707 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1709 if (FAILED(res))
1711 HeapFree(GetProcessHeap(), 0, newStorage);
1712 goto end;
1715 *ppstg = &newTransactedStorage->IStorage_iface;
1717 else
1719 *ppstg = &newStorage->base.IStorage_iface;
1722 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1724 res = S_OK;
1725 goto end;
1728 res = STG_E_INSUFFICIENTMEMORY;
1729 goto end;
1732 res = STG_E_FILENOTFOUND;
1734 end:
1735 TRACE("<-- %08x\n", res);
1736 return res;
1739 /************************************************************************
1740 * StorageBaseImpl_EnumElements (IStorage)
1742 * This method will create an enumerator object that can be used to
1743 * retrieve information about all the elements in the storage object.
1745 * See Windows documentation for more details on IStorage methods.
1747 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1748 IStorage* iface,
1749 DWORD reserved1, /* [in] */
1750 void* reserved2, /* [size_is][unique][in] */
1751 DWORD reserved3, /* [in] */
1752 IEnumSTATSTG** ppenum) /* [out] */
1754 StorageBaseImpl *This = impl_from_IStorage(iface);
1755 IEnumSTATSTGImpl* newEnum;
1757 TRACE("(%p, %d, %p, %d, %p)\n",
1758 iface, reserved1, reserved2, reserved3, ppenum);
1760 if (!ppenum)
1761 return E_INVALIDARG;
1763 if (This->reverted)
1764 return STG_E_REVERTED;
1766 newEnum = IEnumSTATSTGImpl_Construct(
1767 This,
1768 This->storageDirEntry);
1770 if (newEnum)
1772 *ppenum = &newEnum->IEnumSTATSTG_iface;
1773 return S_OK;
1776 return E_OUTOFMEMORY;
1779 /************************************************************************
1780 * StorageBaseImpl_Stat (IStorage)
1782 * This method will retrieve information about this storage object.
1784 * See Windows documentation for more details on IStorage methods.
1786 static HRESULT WINAPI StorageBaseImpl_Stat(
1787 IStorage* iface,
1788 STATSTG* pstatstg, /* [out] */
1789 DWORD grfStatFlag) /* [in] */
1791 StorageBaseImpl *This = impl_from_IStorage(iface);
1792 DirEntry currentEntry;
1793 HRESULT res = STG_E_UNKNOWN;
1795 TRACE("(%p, %p, %x)\n",
1796 iface, pstatstg, grfStatFlag);
1798 if (!pstatstg)
1800 res = E_INVALIDARG;
1801 goto end;
1804 if (This->reverted)
1806 res = STG_E_REVERTED;
1807 goto end;
1810 res = StorageBaseImpl_ReadDirEntry(
1811 This,
1812 This->storageDirEntry,
1813 &currentEntry);
1815 if (SUCCEEDED(res))
1817 StorageUtl_CopyDirEntryToSTATSTG(
1818 This,
1819 pstatstg,
1820 &currentEntry,
1821 grfStatFlag);
1823 pstatstg->grfMode = This->openFlags;
1824 pstatstg->grfStateBits = This->stateBits;
1827 end:
1828 if (res == S_OK)
1830 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
1832 TRACE("<-- %08x\n", res);
1833 return res;
1836 /************************************************************************
1837 * StorageBaseImpl_RenameElement (IStorage)
1839 * This method will rename the specified element.
1841 * See Windows documentation for more details on IStorage methods.
1843 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1844 IStorage* iface,
1845 const OLECHAR* pwcsOldName, /* [in] */
1846 const OLECHAR* pwcsNewName) /* [in] */
1848 StorageBaseImpl *This = impl_from_IStorage(iface);
1849 DirEntry currentEntry;
1850 DirRef currentEntryRef;
1852 TRACE("(%p, %s, %s)\n",
1853 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1855 if (This->reverted)
1856 return STG_E_REVERTED;
1858 currentEntryRef = findElement(This,
1859 This->storageDirEntry,
1860 pwcsNewName,
1861 &currentEntry);
1863 if (currentEntryRef != DIRENTRY_NULL)
1866 * There is already an element with the new name
1868 return STG_E_FILEALREADYEXISTS;
1872 * Search for the old element name
1874 currentEntryRef = findElement(This,
1875 This->storageDirEntry,
1876 pwcsOldName,
1877 &currentEntry);
1879 if (currentEntryRef != DIRENTRY_NULL)
1881 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1882 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1884 WARN("Element is already open; cannot rename.\n");
1885 return STG_E_ACCESSDENIED;
1888 /* Remove the element from its current position in the tree */
1889 removeFromTree(This, This->storageDirEntry,
1890 currentEntryRef);
1892 /* Change the name of the element */
1893 lstrcpyW(currentEntry.name, pwcsNewName);
1895 /* Delete any sibling links */
1896 currentEntry.leftChild = DIRENTRY_NULL;
1897 currentEntry.rightChild = DIRENTRY_NULL;
1899 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1900 &currentEntry);
1902 /* Insert the element in a new position in the tree */
1903 insertIntoTree(This, This->storageDirEntry,
1904 currentEntryRef);
1906 else
1909 * There is no element with the old name
1911 return STG_E_FILENOTFOUND;
1914 return StorageBaseImpl_Flush(This);
1917 /************************************************************************
1918 * StorageBaseImpl_CreateStream (IStorage)
1920 * This method will create a stream object within this storage
1922 * See Windows documentation for more details on IStorage methods.
1924 static HRESULT WINAPI StorageBaseImpl_CreateStream(
1925 IStorage* iface,
1926 const OLECHAR* pwcsName, /* [string][in] */
1927 DWORD grfMode, /* [in] */
1928 DWORD reserved1, /* [in] */
1929 DWORD reserved2, /* [in] */
1930 IStream** ppstm) /* [out] */
1932 StorageBaseImpl *This = impl_from_IStorage(iface);
1933 StgStreamImpl* newStream;
1934 DirEntry currentEntry, newStreamEntry;
1935 DirRef currentEntryRef, newStreamEntryRef;
1936 HRESULT hr;
1938 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1939 iface, debugstr_w(pwcsName), grfMode,
1940 reserved1, reserved2, ppstm);
1942 if (ppstm == 0)
1943 return STG_E_INVALIDPOINTER;
1945 if (pwcsName == 0)
1946 return STG_E_INVALIDNAME;
1948 if (reserved1 || reserved2)
1949 return STG_E_INVALIDPARAMETER;
1951 if ( FAILED( validateSTGM(grfMode) ))
1952 return STG_E_INVALIDFLAG;
1954 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1955 return STG_E_INVALIDFLAG;
1957 if (This->reverted)
1958 return STG_E_REVERTED;
1961 * As documented.
1963 if ((grfMode & STGM_DELETEONRELEASE) ||
1964 (grfMode & STGM_TRANSACTED))
1965 return STG_E_INVALIDFUNCTION;
1968 * Don't worry about permissions in transacted mode, as we can always write
1969 * changes; we just can't always commit them.
1971 if(!(This->openFlags & STGM_TRANSACTED)) {
1972 /* Can't create a stream on read-only storage */
1973 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1974 return STG_E_ACCESSDENIED;
1976 /* Can't create a stream with greater access than the parent. */
1977 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1978 return STG_E_ACCESSDENIED;
1981 if(This->openFlags & STGM_SIMPLE)
1982 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1984 *ppstm = 0;
1986 currentEntryRef = findElement(This,
1987 This->storageDirEntry,
1988 pwcsName,
1989 &currentEntry);
1991 if (currentEntryRef != DIRENTRY_NULL)
1994 * An element with this name already exists
1996 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1998 IStorage_DestroyElement(iface, pwcsName);
2000 else
2001 return STG_E_FILEALREADYEXISTS;
2005 * memset the empty entry
2007 memset(&newStreamEntry, 0, sizeof(DirEntry));
2009 newStreamEntry.sizeOfNameString =
2010 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2012 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2013 return STG_E_INVALIDNAME;
2015 lstrcpyW(newStreamEntry.name, pwcsName);
2017 newStreamEntry.stgType = STGTY_STREAM;
2018 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2019 newStreamEntry.size.u.LowPart = 0;
2020 newStreamEntry.size.u.HighPart = 0;
2022 newStreamEntry.leftChild = DIRENTRY_NULL;
2023 newStreamEntry.rightChild = DIRENTRY_NULL;
2024 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2026 /* call CoFileTime to get the current time
2027 newStreamEntry.ctime
2028 newStreamEntry.mtime
2031 /* newStreamEntry.clsid */
2034 * Create an entry with the new data
2036 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2037 if (FAILED(hr))
2038 return hr;
2041 * Insert the new entry in the parent storage's tree.
2043 hr = insertIntoTree(
2044 This,
2045 This->storageDirEntry,
2046 newStreamEntryRef);
2047 if (FAILED(hr))
2049 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2050 return hr;
2054 * Open the stream to return it.
2056 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2058 if (newStream)
2060 *ppstm = &newStream->IStream_iface;
2061 IStream_AddRef(*ppstm);
2063 else
2065 return STG_E_INSUFFICIENTMEMORY;
2068 return StorageBaseImpl_Flush(This);
2071 /************************************************************************
2072 * StorageBaseImpl_SetClass (IStorage)
2074 * This method will write the specified CLSID in the directory entry of this
2075 * storage.
2077 * See Windows documentation for more details on IStorage methods.
2079 static HRESULT WINAPI StorageBaseImpl_SetClass(
2080 IStorage* iface,
2081 REFCLSID clsid) /* [in] */
2083 StorageBaseImpl *This = impl_from_IStorage(iface);
2084 HRESULT hRes;
2085 DirEntry currentEntry;
2087 TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid));
2089 if (This->reverted)
2090 return STG_E_REVERTED;
2092 hRes = StorageBaseImpl_ReadDirEntry(This,
2093 This->storageDirEntry,
2094 &currentEntry);
2095 if (SUCCEEDED(hRes))
2097 currentEntry.clsid = *clsid;
2099 hRes = StorageBaseImpl_WriteDirEntry(This,
2100 This->storageDirEntry,
2101 &currentEntry);
2104 if (SUCCEEDED(hRes))
2105 hRes = StorageBaseImpl_Flush(This);
2107 return hRes;
2110 /************************************************************************
2111 * StorageBaseImpl_CreateStorage (IStorage)
2113 * This method will create the storage object within the provided storage.
2115 * See Windows documentation for more details on IStorage methods.
2117 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
2118 IStorage* iface,
2119 const OLECHAR *pwcsName, /* [string][in] */
2120 DWORD grfMode, /* [in] */
2121 DWORD reserved1, /* [in] */
2122 DWORD reserved2, /* [in] */
2123 IStorage **ppstg) /* [out] */
2125 StorageBaseImpl* This = impl_from_IStorage(iface);
2127 DirEntry currentEntry;
2128 DirEntry newEntry;
2129 DirRef currentEntryRef;
2130 DirRef newEntryRef;
2131 HRESULT hr;
2133 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2134 iface, debugstr_w(pwcsName), grfMode,
2135 reserved1, reserved2, ppstg);
2137 if (ppstg == 0)
2138 return STG_E_INVALIDPOINTER;
2140 if (This->openFlags & STGM_SIMPLE)
2142 return STG_E_INVALIDFUNCTION;
2145 if (pwcsName == 0)
2146 return STG_E_INVALIDNAME;
2148 *ppstg = NULL;
2150 if ( FAILED( validateSTGM(grfMode) ) ||
2151 (grfMode & STGM_DELETEONRELEASE) )
2153 WARN("bad grfMode: 0x%x\n", grfMode);
2154 return STG_E_INVALIDFLAG;
2157 if (This->reverted)
2158 return STG_E_REVERTED;
2161 * Check that we're compatible with the parent's storage mode
2163 if ( !(This->openFlags & STGM_TRANSACTED) &&
2164 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2166 WARN("access denied\n");
2167 return STG_E_ACCESSDENIED;
2170 currentEntryRef = findElement(This,
2171 This->storageDirEntry,
2172 pwcsName,
2173 &currentEntry);
2175 if (currentEntryRef != DIRENTRY_NULL)
2178 * An element with this name already exists
2180 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2181 ((This->openFlags & STGM_TRANSACTED) ||
2182 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2184 hr = IStorage_DestroyElement(iface, pwcsName);
2185 if (FAILED(hr))
2186 return hr;
2188 else
2190 WARN("file already exists\n");
2191 return STG_E_FILEALREADYEXISTS;
2194 else if (!(This->openFlags & STGM_TRANSACTED) &&
2195 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2197 WARN("read-only storage\n");
2198 return STG_E_ACCESSDENIED;
2201 memset(&newEntry, 0, sizeof(DirEntry));
2203 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2205 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2207 FIXME("name too long\n");
2208 return STG_E_INVALIDNAME;
2211 lstrcpyW(newEntry.name, pwcsName);
2213 newEntry.stgType = STGTY_STORAGE;
2214 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2215 newEntry.size.u.LowPart = 0;
2216 newEntry.size.u.HighPart = 0;
2218 newEntry.leftChild = DIRENTRY_NULL;
2219 newEntry.rightChild = DIRENTRY_NULL;
2220 newEntry.dirRootEntry = DIRENTRY_NULL;
2222 /* call CoFileTime to get the current time
2223 newEntry.ctime
2224 newEntry.mtime
2227 /* newEntry.clsid */
2230 * Create a new directory entry for the storage
2232 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2233 if (FAILED(hr))
2234 return hr;
2237 * Insert the new directory entry into the parent storage's tree
2239 hr = insertIntoTree(
2240 This,
2241 This->storageDirEntry,
2242 newEntryRef);
2243 if (FAILED(hr))
2245 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2246 return hr;
2250 * Open it to get a pointer to return.
2252 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2254 if( (hr != S_OK) || (*ppstg == NULL))
2256 return hr;
2259 if (SUCCEEDED(hr))
2260 hr = StorageBaseImpl_Flush(This);
2262 return S_OK;
2265 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2266 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2267 SNB snbExclude, IStorage *pstgDest)
2269 DirEntry data;
2270 HRESULT hr;
2272 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2274 if (SUCCEEDED(hr))
2275 hr = IStorage_SetClass( pstgDest, &data.clsid );
2277 if (SUCCEEDED(hr))
2278 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2279 skip_stream, snbExclude, pstgDest );
2281 TRACE("<-- %08x\n", hr);
2282 return hr;
2285 /*************************************************************************
2286 * CopyTo (IStorage)
2288 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2289 IStorage* iface,
2290 DWORD ciidExclude, /* [in] */
2291 const IID* rgiidExclude, /* [size_is][unique][in] */
2292 SNB snbExclude, /* [unique][in] */
2293 IStorage* pstgDest) /* [unique][in] */
2295 StorageBaseImpl *This = impl_from_IStorage(iface);
2297 BOOL skip_storage = FALSE, skip_stream = FALSE;
2298 DWORD i;
2300 TRACE("(%p, %d, %p, %p, %p)\n",
2301 iface, ciidExclude, rgiidExclude,
2302 snbExclude, pstgDest);
2304 if ( pstgDest == 0 )
2305 return STG_E_INVALIDPOINTER;
2307 for(i = 0; i < ciidExclude; ++i)
2309 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2310 skip_storage = TRUE;
2311 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2312 skip_stream = TRUE;
2313 else
2314 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2317 if (!skip_storage)
2319 /* Give up early if it looks like this would be infinitely recursive.
2320 * Oddly enough, this includes some cases that aren't really recursive, like
2321 * copying to a transacted child. */
2322 IStorage *pstgDestAncestor = pstgDest;
2323 IStorage *pstgDestAncestorChild = NULL;
2325 /* Go up the chain from the destination until we find the source storage. */
2326 while (pstgDestAncestor != iface) {
2327 pstgDestAncestorChild = pstgDest;
2329 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2331 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2333 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2335 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2337 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2339 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2341 else
2342 break;
2345 if (pstgDestAncestor == iface)
2347 BOOL fail = TRUE;
2349 if (pstgDestAncestorChild && snbExclude)
2351 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2352 DirEntry data;
2353 WCHAR **snb = snbExclude;
2355 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2357 while ( *snb != NULL && fail )
2359 if ( wcscmp(data.name, *snb) == 0 )
2360 fail = FALSE;
2361 ++snb;
2365 if (fail)
2366 return STG_E_ACCESSDENIED;
2370 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2371 skip_storage, skip_stream, snbExclude, pstgDest );
2374 /*************************************************************************
2375 * MoveElementTo (IStorage)
2377 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2378 IStorage* iface,
2379 const OLECHAR *pwcsName, /* [string][in] */
2380 IStorage *pstgDest, /* [unique][in] */
2381 const OLECHAR *pwcsNewName,/* [string][in] */
2382 DWORD grfFlags) /* [in] */
2384 FIXME("(%p %s %p %s %u): stub\n", iface,
2385 debugstr_w(pwcsName), pstgDest,
2386 debugstr_w(pwcsNewName), grfFlags);
2387 return E_NOTIMPL;
2390 /*************************************************************************
2391 * Commit (IStorage)
2393 * Ensures that any changes made to a storage object open in transacted mode
2394 * are reflected in the parent storage
2396 * In a non-transacted mode, this ensures all cached writes are completed.
2398 static HRESULT WINAPI StorageBaseImpl_Commit(
2399 IStorage* iface,
2400 DWORD grfCommitFlags)/* [in] */
2402 StorageBaseImpl* This = impl_from_IStorage(iface);
2403 TRACE("(%p %d)\n", iface, grfCommitFlags);
2404 return StorageBaseImpl_Flush(This);
2407 /*************************************************************************
2408 * Revert (IStorage)
2410 * Discard all changes that have been made since the last commit operation
2412 static HRESULT WINAPI StorageBaseImpl_Revert(
2413 IStorage* iface)
2415 TRACE("(%p)\n", iface);
2416 return S_OK;
2419 /*********************************************************************
2421 * Internal helper function for StorageBaseImpl_DestroyElement()
2423 * Delete the contents of a storage entry.
2426 static HRESULT deleteStorageContents(
2427 StorageBaseImpl *parentStorage,
2428 DirRef indexToDelete,
2429 DirEntry entryDataToDelete)
2431 IEnumSTATSTG *elements = 0;
2432 IStorage *childStorage = 0;
2433 STATSTG currentElement;
2434 HRESULT hr;
2435 HRESULT destroyHr = S_OK;
2436 StorageInternalImpl *stg, *stg2;
2438 TRACE("%p,%d\n", parentStorage, indexToDelete);
2440 /* Invalidate any open storage objects. */
2441 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2443 if (stg->base.storageDirEntry == indexToDelete)
2445 StorageBaseImpl_Invalidate(&stg->base);
2450 * Open the storage and enumerate it
2452 hr = IStorage_OpenStorage(
2453 &parentStorage->IStorage_iface,
2454 entryDataToDelete.name,
2456 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2459 &childStorage);
2461 if (hr != S_OK)
2463 TRACE("<-- %08x\n", hr);
2464 return hr;
2468 * Enumerate the elements
2470 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2471 if (FAILED(hr))
2473 IStorage_Release(childStorage);
2474 TRACE("<-- %08x\n", hr);
2475 return hr;
2481 * Obtain the next element
2483 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2484 if (hr==S_OK)
2486 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2488 CoTaskMemFree(currentElement.pwcsName);
2492 * We need to Reset the enumeration every time because we delete elements
2493 * and the enumeration could be invalid
2495 IEnumSTATSTG_Reset(elements);
2497 } while ((hr == S_OK) && (destroyHr == S_OK));
2499 IStorage_Release(childStorage);
2500 IEnumSTATSTG_Release(elements);
2502 TRACE("%08x\n", hr);
2503 return destroyHr;
2506 /*********************************************************************
2508 * Internal helper function for StorageBaseImpl_DestroyElement()
2510 * Perform the deletion of a stream's data
2513 static HRESULT deleteStreamContents(
2514 StorageBaseImpl *parentStorage,
2515 DirRef indexToDelete,
2516 DirEntry entryDataToDelete)
2518 IStream *pis;
2519 HRESULT hr;
2520 ULARGE_INTEGER size;
2521 StgStreamImpl *strm, *strm2;
2523 /* Invalidate any open stream objects. */
2524 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2526 if (strm->dirEntry == indexToDelete)
2528 TRACE("Stream deleted %p\n", strm);
2529 strm->parentStorage = NULL;
2530 list_remove(&strm->StrmListEntry);
2534 size.u.HighPart = 0;
2535 size.u.LowPart = 0;
2537 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2538 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2540 if (hr!=S_OK)
2542 TRACE("<-- %08x\n", hr);
2543 return(hr);
2547 * Zap the stream
2549 hr = IStream_SetSize(pis, size);
2551 if(hr != S_OK)
2553 TRACE("<-- %08x\n", hr);
2554 return hr;
2558 * Release the stream object.
2560 IStream_Release(pis);
2561 TRACE("<-- %08x\n", hr);
2562 return S_OK;
2565 /*************************************************************************
2566 * DestroyElement (IStorage)
2568 * Strategy: This implementation is built this way for simplicity not for speed.
2569 * I always delete the topmost element of the enumeration and adjust
2570 * the deleted element pointer all the time. This takes longer to
2571 * do but allows reinvoking DestroyElement whenever we encounter a
2572 * storage object. The optimisation resides in the usage of another
2573 * enumeration strategy that would give all the leaves of a storage
2574 * first. (postfix order)
2576 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2577 IStorage* iface,
2578 const OLECHAR *pwcsName)/* [string][in] */
2580 StorageBaseImpl *This = impl_from_IStorage(iface);
2582 HRESULT hr = S_OK;
2583 DirEntry entryToDelete;
2584 DirRef entryToDeleteRef;
2586 TRACE("(%p, %s)\n",
2587 iface, debugstr_w(pwcsName));
2589 if (pwcsName==NULL)
2590 return STG_E_INVALIDPOINTER;
2592 if (This->reverted)
2593 return STG_E_REVERTED;
2595 if ( !(This->openFlags & STGM_TRANSACTED) &&
2596 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2597 return STG_E_ACCESSDENIED;
2599 entryToDeleteRef = findElement(
2600 This,
2601 This->storageDirEntry,
2602 pwcsName,
2603 &entryToDelete);
2605 if ( entryToDeleteRef == DIRENTRY_NULL )
2607 TRACE("<-- STG_E_FILENOTFOUND\n");
2608 return STG_E_FILENOTFOUND;
2611 if ( entryToDelete.stgType == STGTY_STORAGE )
2613 hr = deleteStorageContents(
2614 This,
2615 entryToDeleteRef,
2616 entryToDelete);
2618 else if ( entryToDelete.stgType == STGTY_STREAM )
2620 hr = deleteStreamContents(
2621 This,
2622 entryToDeleteRef,
2623 entryToDelete);
2626 if (hr!=S_OK)
2628 TRACE("<-- %08x\n", hr);
2629 return hr;
2633 * Remove the entry from its parent storage
2635 hr = removeFromTree(
2636 This,
2637 This->storageDirEntry,
2638 entryToDeleteRef);
2641 * Invalidate the entry
2643 if (SUCCEEDED(hr))
2644 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2646 if (SUCCEEDED(hr))
2647 hr = StorageBaseImpl_Flush(This);
2649 TRACE("<-- %08x\n", hr);
2650 return hr;
2653 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2655 struct list *cur, *cur2;
2656 StgStreamImpl *strm=NULL;
2657 StorageInternalImpl *childstg=NULL;
2659 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2660 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2661 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2662 strm->parentStorage = NULL;
2663 list_remove(cur);
2666 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2667 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2668 StorageBaseImpl_Invalidate( &childstg->base );
2671 if (stg->transactedChild)
2673 StorageBaseImpl_Invalidate(stg->transactedChild);
2675 stg->transactedChild = NULL;
2679 /******************************************************************************
2680 * SetElementTimes (IStorage)
2682 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2683 IStorage* iface,
2684 const OLECHAR *pwcsName,/* [string][in] */
2685 const FILETIME *pctime, /* [in] */
2686 const FILETIME *patime, /* [in] */
2687 const FILETIME *pmtime) /* [in] */
2689 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2690 return S_OK;
2693 /******************************************************************************
2694 * SetStateBits (IStorage)
2696 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2697 IStorage* iface,
2698 DWORD grfStateBits,/* [in] */
2699 DWORD grfMask) /* [in] */
2701 StorageBaseImpl *This = impl_from_IStorage(iface);
2703 if (This->reverted)
2704 return STG_E_REVERTED;
2706 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2707 return S_OK;
2710 /******************************************************************************
2711 * Internal stream list handlers
2714 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2716 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2717 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2720 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2722 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2723 list_remove(&(strm->StrmListEntry));
2726 static HRESULT StorageBaseImpl_CopyStream(
2727 StorageBaseImpl *dst, DirRef dst_entry,
2728 StorageBaseImpl *src, DirRef src_entry)
2730 HRESULT hr;
2731 BYTE data[4096];
2732 DirEntry srcdata;
2733 ULARGE_INTEGER bytes_copied;
2734 ULONG bytestocopy, bytesread, byteswritten;
2736 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2738 if (SUCCEEDED(hr))
2740 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2742 bytes_copied.QuadPart = 0;
2743 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2745 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2747 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2748 data, &bytesread);
2749 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2751 if (SUCCEEDED(hr))
2752 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2753 data, &byteswritten);
2754 if (SUCCEEDED(hr))
2756 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2757 bytes_copied.QuadPart += byteswritten;
2762 return hr;
2765 static HRESULT StorageBaseImpl_DupStorageTree(
2766 StorageBaseImpl *dst, DirRef *dst_entry,
2767 StorageBaseImpl *src, DirRef src_entry)
2769 HRESULT hr;
2770 DirEntry data;
2771 BOOL has_stream=FALSE;
2773 if (src_entry == DIRENTRY_NULL)
2775 *dst_entry = DIRENTRY_NULL;
2776 return S_OK;
2779 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2780 if (SUCCEEDED(hr))
2782 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2783 data.startingBlock = BLOCK_END_OF_CHAIN;
2784 data.size.QuadPart = 0;
2786 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2789 if (SUCCEEDED(hr))
2790 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2792 if (SUCCEEDED(hr))
2793 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2795 if (SUCCEEDED(hr))
2796 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2798 if (SUCCEEDED(hr) && has_stream)
2799 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2801 return hr;
2804 static HRESULT StorageBaseImpl_CopyStorageTree(
2805 StorageBaseImpl *dst, DirRef dst_entry,
2806 StorageBaseImpl *src, DirRef src_entry)
2808 HRESULT hr;
2809 DirEntry src_data, dst_data;
2810 DirRef new_root_entry;
2812 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2814 if (SUCCEEDED(hr))
2816 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2819 if (SUCCEEDED(hr))
2821 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
2822 dst_data.clsid = src_data.clsid;
2823 dst_data.ctime = src_data.ctime;
2824 dst_data.mtime = src_data.mtime;
2825 dst_data.dirRootEntry = new_root_entry;
2828 if (SUCCEEDED(hr))
2829 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2831 return hr;
2834 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2836 HRESULT hr;
2837 DirEntry data;
2838 ULARGE_INTEGER zero;
2840 if (entry == DIRENTRY_NULL)
2841 return S_OK;
2843 zero.QuadPart = 0;
2845 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2847 if (SUCCEEDED(hr) && include_siblings)
2848 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2850 if (SUCCEEDED(hr) && include_siblings)
2851 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2853 if (SUCCEEDED(hr))
2854 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2856 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2857 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2859 if (SUCCEEDED(hr))
2860 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2862 return hr;
2866 /************************************************************************
2867 * StorageImpl implementation
2868 ***********************************************************************/
2870 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2871 ULARGE_INTEGER offset,
2872 void* buffer,
2873 ULONG size,
2874 ULONG* bytesRead)
2876 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2879 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2880 ULARGE_INTEGER offset,
2881 const void* buffer,
2882 const ULONG size,
2883 ULONG* bytesWritten)
2885 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2888 /******************************************************************************
2889 * StorageImpl_LoadFileHeader
2891 * This method will read in the file header
2893 static HRESULT StorageImpl_LoadFileHeader(
2894 StorageImpl* This)
2896 HRESULT hr;
2897 BYTE headerBigBlock[HEADER_SIZE];
2898 int index;
2899 ULARGE_INTEGER offset;
2900 DWORD bytes_read;
2902 TRACE("\n");
2904 * Get a pointer to the big block of data containing the header.
2906 offset.u.HighPart = 0;
2907 offset.u.LowPart = 0;
2908 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2909 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2910 hr = STG_E_FILENOTFOUND;
2913 * Extract the information from the header.
2915 if (SUCCEEDED(hr))
2918 * Check for the "magic number" signature and return an error if it is not
2919 * found.
2921 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2923 return STG_E_OLDFORMAT;
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2928 return STG_E_INVALIDHEADER;
2931 StorageUtl_ReadWord(
2932 headerBigBlock,
2933 OFFSET_BIGBLOCKSIZEBITS,
2934 &This->bigBlockSizeBits);
2936 StorageUtl_ReadWord(
2937 headerBigBlock,
2938 OFFSET_SMALLBLOCKSIZEBITS,
2939 &This->smallBlockSizeBits);
2941 StorageUtl_ReadDWord(
2942 headerBigBlock,
2943 OFFSET_BBDEPOTCOUNT,
2944 &This->bigBlockDepotCount);
2946 StorageUtl_ReadDWord(
2947 headerBigBlock,
2948 OFFSET_ROOTSTARTBLOCK,
2949 &This->rootStartBlock);
2951 StorageUtl_ReadDWord(
2952 headerBigBlock,
2953 OFFSET_TRANSACTIONSIG,
2954 &This->transactionSig);
2956 StorageUtl_ReadDWord(
2957 headerBigBlock,
2958 OFFSET_SMALLBLOCKLIMIT,
2959 &This->smallBlockLimit);
2961 StorageUtl_ReadDWord(
2962 headerBigBlock,
2963 OFFSET_SBDEPOTSTART,
2964 &This->smallBlockDepotStart);
2966 StorageUtl_ReadDWord(
2967 headerBigBlock,
2968 OFFSET_EXTBBDEPOTSTART,
2969 &This->extBigBlockDepotStart);
2971 StorageUtl_ReadDWord(
2972 headerBigBlock,
2973 OFFSET_EXTBBDEPOTCOUNT,
2974 &This->extBigBlockDepotCount);
2976 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2978 StorageUtl_ReadDWord(
2979 headerBigBlock,
2980 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2981 &(This->bigBlockDepotStart[index]));
2985 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2987 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2988 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2991 * Right now, the code is making some assumptions about the size of the
2992 * blocks, just make sure they are what we're expecting.
2994 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2995 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2996 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2998 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2999 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3000 hr = STG_E_INVALIDHEADER;
3002 else
3003 hr = S_OK;
3006 return hr;
3009 /******************************************************************************
3010 * StorageImpl_SaveFileHeader
3012 * This method will save to the file the header
3014 static void StorageImpl_SaveFileHeader(
3015 StorageImpl* This)
3017 BYTE headerBigBlock[HEADER_SIZE];
3018 int index;
3019 ULARGE_INTEGER offset;
3020 DWORD bytes_written;
3021 DWORD major_version, dirsectorcount;
3023 if (This->bigBlockSizeBits == 0x9)
3024 major_version = 3;
3025 else if (This->bigBlockSizeBits == 0xc)
3026 major_version = 4;
3027 else
3029 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3030 major_version = 4;
3033 memset(headerBigBlock, 0, HEADER_SIZE);
3034 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3037 * Write the information to the header.
3039 StorageUtl_WriteWord(
3040 headerBigBlock,
3041 OFFSET_MINORVERSION,
3042 0x3e);
3044 StorageUtl_WriteWord(
3045 headerBigBlock,
3046 OFFSET_MAJORVERSION,
3047 major_version);
3049 StorageUtl_WriteWord(
3050 headerBigBlock,
3051 OFFSET_BYTEORDERMARKER,
3052 (WORD)-2);
3054 StorageUtl_WriteWord(
3055 headerBigBlock,
3056 OFFSET_BIGBLOCKSIZEBITS,
3057 This->bigBlockSizeBits);
3059 StorageUtl_WriteWord(
3060 headerBigBlock,
3061 OFFSET_SMALLBLOCKSIZEBITS,
3062 This->smallBlockSizeBits);
3064 if (major_version >= 4)
3066 if (This->rootBlockChain)
3067 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3068 else
3069 /* This file is being created, and it will start out with one block. */
3070 dirsectorcount = 1;
3072 else
3073 /* This field must be 0 in versions older than 4 */
3074 dirsectorcount = 0;
3076 StorageUtl_WriteDWord(
3077 headerBigBlock,
3078 OFFSET_DIRSECTORCOUNT,
3079 dirsectorcount);
3081 StorageUtl_WriteDWord(
3082 headerBigBlock,
3083 OFFSET_BBDEPOTCOUNT,
3084 This->bigBlockDepotCount);
3086 StorageUtl_WriteDWord(
3087 headerBigBlock,
3088 OFFSET_ROOTSTARTBLOCK,
3089 This->rootStartBlock);
3091 StorageUtl_WriteDWord(
3092 headerBigBlock,
3093 OFFSET_TRANSACTIONSIG,
3094 This->transactionSig);
3096 StorageUtl_WriteDWord(
3097 headerBigBlock,
3098 OFFSET_SMALLBLOCKLIMIT,
3099 This->smallBlockLimit);
3101 StorageUtl_WriteDWord(
3102 headerBigBlock,
3103 OFFSET_SBDEPOTSTART,
3104 This->smallBlockDepotStart);
3106 StorageUtl_WriteDWord(
3107 headerBigBlock,
3108 OFFSET_SBDEPOTCOUNT,
3109 This->smallBlockDepotChain ?
3110 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3112 StorageUtl_WriteDWord(
3113 headerBigBlock,
3114 OFFSET_EXTBBDEPOTSTART,
3115 This->extBigBlockDepotStart);
3117 StorageUtl_WriteDWord(
3118 headerBigBlock,
3119 OFFSET_EXTBBDEPOTCOUNT,
3120 This->extBigBlockDepotCount);
3122 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3124 StorageUtl_WriteDWord(
3125 headerBigBlock,
3126 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3127 (This->bigBlockDepotStart[index]));
3130 offset.QuadPart = 0;
3131 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3135 /************************************************************************
3136 * StorageImpl implementation : DirEntry methods
3137 ***********************************************************************/
3139 /******************************************************************************
3140 * StorageImpl_ReadRawDirEntry
3142 * This method will read the raw data from a directory entry in the file.
3144 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3146 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3148 ULARGE_INTEGER offset;
3149 HRESULT hr;
3150 ULONG bytesRead;
3152 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3154 hr = BlockChainStream_ReadAt(
3155 This->rootBlockChain,
3156 offset,
3157 RAW_DIRENTRY_SIZE,
3158 buffer,
3159 &bytesRead);
3161 if (bytesRead != RAW_DIRENTRY_SIZE)
3162 return STG_E_READFAULT;
3164 return hr;
3167 /******************************************************************************
3168 * StorageImpl_WriteRawDirEntry
3170 * This method will write the raw data from a directory entry in the file.
3172 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3174 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3176 ULARGE_INTEGER offset;
3177 ULONG bytesRead;
3179 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3181 return BlockChainStream_WriteAt(
3182 This->rootBlockChain,
3183 offset,
3184 RAW_DIRENTRY_SIZE,
3185 buffer,
3186 &bytesRead);
3189 /***************************************************************************
3191 * Internal Method
3193 * Mark a directory entry in the file as free.
3195 static HRESULT StorageImpl_DestroyDirEntry(
3196 StorageBaseImpl *base,
3197 DirRef index)
3199 BYTE emptyData[RAW_DIRENTRY_SIZE];
3200 StorageImpl *storage = (StorageImpl*)base;
3202 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3204 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3207 /******************************************************************************
3208 * UpdateRawDirEntry
3210 * Update raw directory entry data from the fields in newData.
3212 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3214 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3216 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3218 memcpy(
3219 buffer + OFFSET_PS_NAME,
3220 newData->name,
3221 DIRENTRY_NAME_BUFFER_LEN );
3223 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3225 StorageUtl_WriteWord(
3226 buffer,
3227 OFFSET_PS_NAMELENGTH,
3228 newData->sizeOfNameString);
3230 StorageUtl_WriteDWord(
3231 buffer,
3232 OFFSET_PS_LEFTCHILD,
3233 newData->leftChild);
3235 StorageUtl_WriteDWord(
3236 buffer,
3237 OFFSET_PS_RIGHTCHILD,
3238 newData->rightChild);
3240 StorageUtl_WriteDWord(
3241 buffer,
3242 OFFSET_PS_DIRROOT,
3243 newData->dirRootEntry);
3245 StorageUtl_WriteGUID(
3246 buffer,
3247 OFFSET_PS_GUID,
3248 &newData->clsid);
3250 StorageUtl_WriteDWord(
3251 buffer,
3252 OFFSET_PS_CTIMELOW,
3253 newData->ctime.dwLowDateTime);
3255 StorageUtl_WriteDWord(
3256 buffer,
3257 OFFSET_PS_CTIMEHIGH,
3258 newData->ctime.dwHighDateTime);
3260 StorageUtl_WriteDWord(
3261 buffer,
3262 OFFSET_PS_MTIMELOW,
3263 newData->mtime.dwLowDateTime);
3265 StorageUtl_WriteDWord(
3266 buffer,
3267 OFFSET_PS_MTIMEHIGH,
3268 newData->ctime.dwHighDateTime);
3270 StorageUtl_WriteDWord(
3271 buffer,
3272 OFFSET_PS_STARTBLOCK,
3273 newData->startingBlock);
3275 StorageUtl_WriteDWord(
3276 buffer,
3277 OFFSET_PS_SIZE,
3278 newData->size.u.LowPart);
3280 StorageUtl_WriteDWord(
3281 buffer,
3282 OFFSET_PS_SIZE_HIGH,
3283 newData->size.u.HighPart);
3286 /***************************************************************************
3288 * Internal Method
3290 * Reserve a directory entry in the file and initialize it.
3292 static HRESULT StorageImpl_CreateDirEntry(
3293 StorageBaseImpl *base,
3294 const DirEntry *newData,
3295 DirRef *index)
3297 StorageImpl *storage = (StorageImpl*)base;
3298 ULONG currentEntryIndex = 0;
3299 ULONG newEntryIndex = DIRENTRY_NULL;
3300 HRESULT hr = S_OK;
3301 BYTE currentData[RAW_DIRENTRY_SIZE];
3302 WORD sizeOfNameString;
3306 hr = StorageImpl_ReadRawDirEntry(storage,
3307 currentEntryIndex,
3308 currentData);
3310 if (SUCCEEDED(hr))
3312 StorageUtl_ReadWord(
3313 currentData,
3314 OFFSET_PS_NAMELENGTH,
3315 &sizeOfNameString);
3317 if (sizeOfNameString == 0)
3320 * The entry exists and is available, we found it.
3322 newEntryIndex = currentEntryIndex;
3325 else
3328 * We exhausted the directory entries, we will create more space below
3330 newEntryIndex = currentEntryIndex;
3332 currentEntryIndex++;
3334 } while (newEntryIndex == DIRENTRY_NULL);
3337 * grow the directory stream
3339 if (FAILED(hr))
3341 BYTE emptyData[RAW_DIRENTRY_SIZE];
3342 ULARGE_INTEGER newSize;
3343 ULONG entryIndex;
3344 ULONG lastEntry = 0;
3345 ULONG blockCount = 0;
3348 * obtain the new count of blocks in the directory stream
3350 blockCount = BlockChainStream_GetCount(
3351 storage->rootBlockChain)+1;
3354 * initialize the size used by the directory stream
3356 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3359 * add a block to the directory stream
3361 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3364 * memset the empty entry in order to initialize the unused newly
3365 * created entries
3367 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3370 * initialize them
3372 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3374 for(
3375 entryIndex = newEntryIndex + 1;
3376 entryIndex < lastEntry;
3377 entryIndex++)
3379 StorageImpl_WriteRawDirEntry(
3380 storage,
3381 entryIndex,
3382 emptyData);
3385 StorageImpl_SaveFileHeader(storage);
3388 UpdateRawDirEntry(currentData, newData);
3390 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3392 if (SUCCEEDED(hr))
3393 *index = newEntryIndex;
3395 return hr;
3398 /******************************************************************************
3399 * StorageImpl_ReadDirEntry
3401 * This method will read the specified directory entry.
3403 static HRESULT StorageImpl_ReadDirEntry(
3404 StorageImpl* This,
3405 DirRef index,
3406 DirEntry* buffer)
3408 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3409 HRESULT readRes;
3411 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3413 if (SUCCEEDED(readRes))
3415 memset(buffer->name, 0, sizeof(buffer->name));
3416 memcpy(
3417 buffer->name,
3418 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3419 DIRENTRY_NAME_BUFFER_LEN );
3420 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3422 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3424 StorageUtl_ReadWord(
3425 currentEntry,
3426 OFFSET_PS_NAMELENGTH,
3427 &buffer->sizeOfNameString);
3429 StorageUtl_ReadDWord(
3430 currentEntry,
3431 OFFSET_PS_LEFTCHILD,
3432 &buffer->leftChild);
3434 StorageUtl_ReadDWord(
3435 currentEntry,
3436 OFFSET_PS_RIGHTCHILD,
3437 &buffer->rightChild);
3439 StorageUtl_ReadDWord(
3440 currentEntry,
3441 OFFSET_PS_DIRROOT,
3442 &buffer->dirRootEntry);
3444 StorageUtl_ReadGUID(
3445 currentEntry,
3446 OFFSET_PS_GUID,
3447 &buffer->clsid);
3449 StorageUtl_ReadDWord(
3450 currentEntry,
3451 OFFSET_PS_CTIMELOW,
3452 &buffer->ctime.dwLowDateTime);
3454 StorageUtl_ReadDWord(
3455 currentEntry,
3456 OFFSET_PS_CTIMEHIGH,
3457 &buffer->ctime.dwHighDateTime);
3459 StorageUtl_ReadDWord(
3460 currentEntry,
3461 OFFSET_PS_MTIMELOW,
3462 &buffer->mtime.dwLowDateTime);
3464 StorageUtl_ReadDWord(
3465 currentEntry,
3466 OFFSET_PS_MTIMEHIGH,
3467 &buffer->mtime.dwHighDateTime);
3469 StorageUtl_ReadDWord(
3470 currentEntry,
3471 OFFSET_PS_STARTBLOCK,
3472 &buffer->startingBlock);
3474 StorageUtl_ReadDWord(
3475 currentEntry,
3476 OFFSET_PS_SIZE,
3477 &buffer->size.u.LowPart);
3479 if (This->bigBlockSize < 4096)
3481 /* Version 3 files may have junk in the high part of size. */
3482 buffer->size.u.HighPart = 0;
3484 else
3486 StorageUtl_ReadDWord(
3487 currentEntry,
3488 OFFSET_PS_SIZE_HIGH,
3489 &buffer->size.u.HighPart);
3493 return readRes;
3496 /*********************************************************************
3497 * Write the specified directory entry to the file
3499 static HRESULT StorageImpl_WriteDirEntry(
3500 StorageImpl* This,
3501 DirRef index,
3502 const DirEntry* buffer)
3504 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3506 UpdateRawDirEntry(currentEntry, buffer);
3508 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3512 /************************************************************************
3513 * StorageImpl implementation : Block methods
3514 ***********************************************************************/
3516 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3518 return (ULONGLONG)(index+1) * This->bigBlockSize;
3521 static HRESULT StorageImpl_ReadBigBlock(
3522 StorageImpl* This,
3523 ULONG blockIndex,
3524 void* buffer,
3525 ULONG* out_read)
3527 ULARGE_INTEGER ulOffset;
3528 DWORD read=0;
3529 HRESULT hr;
3531 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3533 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3535 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3537 /* File ends during this block; fill the rest with 0's. */
3538 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3541 if (out_read) *out_read = read;
3543 return hr;
3546 static BOOL StorageImpl_ReadDWordFromBigBlock(
3547 StorageImpl* This,
3548 ULONG blockIndex,
3549 ULONG offset,
3550 DWORD* value)
3552 ULARGE_INTEGER ulOffset;
3553 DWORD read;
3554 DWORD tmp;
3556 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3557 ulOffset.QuadPart += offset;
3559 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3560 *value = lendian32toh(tmp);
3561 return (read == sizeof(DWORD));
3564 static BOOL StorageImpl_WriteBigBlock(
3565 StorageImpl* This,
3566 ULONG blockIndex,
3567 const void* buffer)
3569 ULARGE_INTEGER ulOffset;
3570 DWORD wrote;
3572 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3574 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3575 return (wrote == This->bigBlockSize);
3578 static BOOL StorageImpl_WriteDWordToBigBlock(
3579 StorageImpl* This,
3580 ULONG blockIndex,
3581 ULONG offset,
3582 DWORD value)
3584 ULARGE_INTEGER ulOffset;
3585 DWORD wrote;
3587 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3588 ulOffset.QuadPart += offset;
3590 value = htole32(value);
3591 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3592 return (wrote == sizeof(DWORD));
3595 /******************************************************************************
3596 * Storage32Impl_SmallBlocksToBigBlocks
3598 * This method will convert a small block chain to a big block chain.
3599 * The small block chain will be destroyed.
3601 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3602 StorageImpl* This,
3603 SmallBlockChainStream** ppsbChain)
3605 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3606 ULARGE_INTEGER size, offset;
3607 ULONG cbRead, cbWritten;
3608 ULARGE_INTEGER cbTotalRead;
3609 DirRef streamEntryRef;
3610 HRESULT resWrite = S_OK;
3611 HRESULT resRead;
3612 DirEntry streamEntry;
3613 BYTE *buffer;
3614 BlockChainStream *bbTempChain = NULL;
3615 BlockChainStream *bigBlockChain = NULL;
3618 * Create a temporary big block chain that doesn't have
3619 * an associated directory entry. This temporary chain will be
3620 * used to copy data from small blocks to big blocks.
3622 bbTempChain = BlockChainStream_Construct(This,
3623 &bbHeadOfChain,
3624 DIRENTRY_NULL);
3625 if(!bbTempChain) return NULL;
3627 * Grow the big block chain.
3629 size = SmallBlockChainStream_GetSize(*ppsbChain);
3630 BlockChainStream_SetSize(bbTempChain, size);
3633 * Copy the contents of the small block chain to the big block chain
3634 * by small block size increments.
3636 offset.u.LowPart = 0;
3637 offset.u.HighPart = 0;
3638 cbTotalRead.QuadPart = 0;
3640 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3643 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3644 offset,
3645 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3646 buffer,
3647 &cbRead);
3648 if (FAILED(resRead))
3649 break;
3651 if (cbRead > 0)
3653 cbTotalRead.QuadPart += cbRead;
3655 resWrite = BlockChainStream_WriteAt(bbTempChain,
3656 offset,
3657 cbRead,
3658 buffer,
3659 &cbWritten);
3661 if (FAILED(resWrite))
3662 break;
3664 offset.u.LowPart += cbRead;
3666 else
3668 resRead = STG_E_READFAULT;
3669 break;
3671 } while (cbTotalRead.QuadPart < size.QuadPart);
3672 HeapFree(GetProcessHeap(),0,buffer);
3674 size.u.HighPart = 0;
3675 size.u.LowPart = 0;
3677 if (FAILED(resRead) || FAILED(resWrite))
3679 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3680 BlockChainStream_SetSize(bbTempChain, size);
3681 BlockChainStream_Destroy(bbTempChain);
3682 return NULL;
3686 * Destroy the small block chain.
3688 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3689 SmallBlockChainStream_SetSize(*ppsbChain, size);
3690 SmallBlockChainStream_Destroy(*ppsbChain);
3691 *ppsbChain = 0;
3694 * Change the directory entry. This chain is now a big block chain
3695 * and it doesn't reside in the small blocks chain anymore.
3697 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3699 streamEntry.startingBlock = bbHeadOfChain;
3701 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3704 * Destroy the temporary entryless big block chain.
3705 * Create a new big block chain associated with this entry.
3707 BlockChainStream_Destroy(bbTempChain);
3708 bigBlockChain = BlockChainStream_Construct(This,
3709 NULL,
3710 streamEntryRef);
3712 return bigBlockChain;
3715 /******************************************************************************
3716 * Storage32Impl_BigBlocksToSmallBlocks
3718 * This method will convert a big block chain to a small block chain.
3719 * The big block chain will be destroyed on success.
3721 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3722 StorageImpl* This,
3723 BlockChainStream** ppbbChain,
3724 ULARGE_INTEGER newSize)
3726 ULARGE_INTEGER size, offset, cbTotalRead;
3727 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3728 DirRef streamEntryRef;
3729 HRESULT resWrite = S_OK, resRead = S_OK;
3730 DirEntry streamEntry;
3731 BYTE* buffer;
3732 SmallBlockChainStream* sbTempChain;
3734 TRACE("%p %p\n", This, ppbbChain);
3736 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3737 DIRENTRY_NULL);
3739 if(!sbTempChain)
3740 return NULL;
3742 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3743 size = BlockChainStream_GetSize(*ppbbChain);
3744 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3746 offset.u.HighPart = 0;
3747 offset.u.LowPart = 0;
3748 cbTotalRead.QuadPart = 0;
3749 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3750 while(cbTotalRead.QuadPart < size.QuadPart)
3752 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3753 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3754 buffer, &cbRead);
3756 if(FAILED(resRead))
3757 break;
3759 if(cbRead > 0)
3761 cbTotalRead.QuadPart += cbRead;
3763 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3764 cbRead, buffer, &cbWritten);
3766 if(FAILED(resWrite))
3767 break;
3769 offset.u.LowPart += cbRead;
3771 else
3773 resRead = STG_E_READFAULT;
3774 break;
3777 HeapFree(GetProcessHeap(), 0, buffer);
3779 size.u.HighPart = 0;
3780 size.u.LowPart = 0;
3782 if(FAILED(resRead) || FAILED(resWrite))
3784 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3785 SmallBlockChainStream_SetSize(sbTempChain, size);
3786 SmallBlockChainStream_Destroy(sbTempChain);
3787 return NULL;
3790 /* destroy the original big block chain */
3791 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3792 BlockChainStream_SetSize(*ppbbChain, size);
3793 BlockChainStream_Destroy(*ppbbChain);
3794 *ppbbChain = NULL;
3796 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3797 streamEntry.startingBlock = sbHeadOfChain;
3798 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3800 SmallBlockChainStream_Destroy(sbTempChain);
3801 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3804 /******************************************************************************
3805 * Storage32Impl_AddBlockDepot
3807 * This will create a depot block, essentially it is a block initialized
3808 * to BLOCK_UNUSEDs.
3810 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3812 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3813 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3814 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3815 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3818 * Initialize blocks as free
3820 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3822 /* Reserve the range lock sector */
3823 if (depotIndex == rangeLockDepot)
3825 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3828 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3831 /******************************************************************************
3832 * Storage32Impl_GetExtDepotBlock
3834 * Returns the index of the block that corresponds to the specified depot
3835 * index. This method is only for depot indexes equal or greater than
3836 * COUNT_BBDEPOTINHEADER.
3838 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3840 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3841 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3842 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3843 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3844 ULONG blockIndex = BLOCK_UNUSED;
3845 ULONG extBlockIndex;
3846 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3847 int index, num_blocks;
3849 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3851 if (extBlockCount >= This->extBigBlockDepotCount)
3852 return BLOCK_UNUSED;
3854 if (This->indexExtBlockDepotCached != extBlockCount)
3856 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3858 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3860 num_blocks = This->bigBlockSize / 4;
3862 for (index = 0; index < num_blocks; index++)
3864 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3865 This->extBlockDepotCached[index] = blockIndex;
3868 This->indexExtBlockDepotCached = extBlockCount;
3871 blockIndex = This->extBlockDepotCached[extBlockOffset];
3873 return blockIndex;
3876 /******************************************************************************
3877 * Storage32Impl_SetExtDepotBlock
3879 * Associates the specified block index to the specified depot index.
3880 * This method is only for depot indexes equal or greater than
3881 * COUNT_BBDEPOTINHEADER.
3883 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3885 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3886 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3887 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3888 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3889 ULONG extBlockIndex;
3891 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3893 assert(extBlockCount < This->extBigBlockDepotCount);
3895 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3897 if (extBlockIndex != BLOCK_UNUSED)
3899 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3900 extBlockOffset * sizeof(ULONG),
3901 blockIndex);
3904 if (This->indexExtBlockDepotCached == extBlockCount)
3906 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3910 /******************************************************************************
3911 * Storage32Impl_AddExtBlockDepot
3913 * Creates an extended depot block.
3915 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3917 ULONG numExtBlocks = This->extBigBlockDepotCount;
3918 ULONG nextExtBlock = This->extBigBlockDepotStart;
3919 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3920 ULONG index = BLOCK_UNUSED;
3921 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3922 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3923 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3925 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3926 blocksPerDepotBlock;
3928 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3931 * The first extended block.
3933 This->extBigBlockDepotStart = index;
3935 else
3938 * Find the last existing extended block.
3940 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3943 * Add the new extended block to the chain.
3945 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3946 index);
3950 * Initialize this block.
3952 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3953 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3955 /* Add the block to our cache. */
3956 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3958 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3959 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3961 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3962 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3964 This->extBigBlockDepotLocations = new_cache;
3965 This->extBigBlockDepotLocationsSize = new_cache_size;
3967 This->extBigBlockDepotLocations[numExtBlocks] = index;
3969 return index;
3972 /************************************************************************
3973 * StorageImpl_GetNextBlockInChain
3975 * This method will retrieve the block index of the next big block in
3976 * in the chain.
3978 * Params: This - Pointer to the Storage object.
3979 * blockIndex - Index of the block to retrieve the chain
3980 * for.
3981 * nextBlockIndex - receives the return value.
3983 * Returns: This method returns the index of the next block in the chain.
3984 * It will return the constants:
3985 * BLOCK_SPECIAL - If the block given was not part of a
3986 * chain.
3987 * BLOCK_END_OF_CHAIN - If the block given was the last in
3988 * a chain.
3989 * BLOCK_UNUSED - If the block given was not past of a chain
3990 * and is available.
3991 * BLOCK_EXTBBDEPOT - This block is part of the extended
3992 * big block depot.
3994 * See Windows documentation for more details on IStorage methods.
3996 static HRESULT StorageImpl_GetNextBlockInChain(
3997 StorageImpl* This,
3998 ULONG blockIndex,
3999 ULONG* nextBlockIndex)
4001 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4002 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4003 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4004 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4005 ULONG read;
4006 ULONG depotBlockIndexPos;
4007 int index, num_blocks;
4009 *nextBlockIndex = BLOCK_SPECIAL;
4011 if(depotBlockCount >= This->bigBlockDepotCount)
4013 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
4014 This->bigBlockDepotCount);
4015 return STG_E_READFAULT;
4019 * Cache the currently accessed depot block.
4021 if (depotBlockCount != This->indexBlockDepotCached)
4023 This->indexBlockDepotCached = depotBlockCount;
4025 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4027 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4029 else
4032 * We have to look in the extended depot.
4034 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4037 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4039 if (!read)
4040 return STG_E_READFAULT;
4042 num_blocks = This->bigBlockSize / 4;
4044 for (index = 0; index < num_blocks; index++)
4046 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4047 This->blockDepotCached[index] = *nextBlockIndex;
4051 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4053 return S_OK;
4056 /******************************************************************************
4057 * Storage32Impl_GetNextExtendedBlock
4059 * Given an extended block this method will return the next extended block.
4061 * NOTES:
4062 * The last ULONG of an extended block is the block index of the next
4063 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4064 * depot.
4066 * Return values:
4067 * - The index of the next extended block
4068 * - BLOCK_UNUSED: there is no next extended block.
4069 * - Any other return values denotes failure.
4071 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4073 ULONG nextBlockIndex = BLOCK_SPECIAL;
4074 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4076 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4077 &nextBlockIndex);
4079 return nextBlockIndex;
4082 /******************************************************************************
4083 * StorageImpl_SetNextBlockInChain
4085 * This method will write the index of the specified block's next block
4086 * in the big block depot.
4088 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4089 * do the following
4091 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4092 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4093 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4096 static void StorageImpl_SetNextBlockInChain(
4097 StorageImpl* This,
4098 ULONG blockIndex,
4099 ULONG nextBlock)
4101 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4102 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4103 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4104 ULONG depotBlockIndexPos;
4106 assert(depotBlockCount < This->bigBlockDepotCount);
4107 assert(blockIndex != nextBlock);
4109 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4110 /* This should never happen (storage file format spec forbids it), but
4111 * older versions of Wine may have generated broken files. We don't want to
4112 * assert and potentially lose data, but we do want to know if this ever
4113 * happens in a newly-created file. */
4114 ERR("Using range lock page\n");
4116 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4118 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4120 else
4123 * We have to look in the extended depot.
4125 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4128 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4129 nextBlock);
4131 * Update the cached block depot, if necessary.
4133 if (depotBlockCount == This->indexBlockDepotCached)
4135 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4139 /******************************************************************************
4140 * StorageImpl_GetNextFreeBigBlock
4142 * Returns the index of the next free big block.
4143 * If the big block depot is filled, this method will enlarge it.
4146 static ULONG StorageImpl_GetNextFreeBigBlock(
4147 StorageImpl* This)
4149 ULONG depotBlockIndexPos;
4150 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4151 ULONG depotBlockOffset;
4152 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4153 ULONG nextBlockIndex = BLOCK_SPECIAL;
4154 int depotIndex = 0;
4155 ULONG freeBlock = BLOCK_UNUSED;
4156 ULONG read;
4157 ULARGE_INTEGER neededSize;
4158 STATSTG statstg;
4160 depotIndex = This->prevFreeBlock / blocksPerDepot;
4161 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4164 * Scan the entire big block depot until we find a block marked free
4166 while (nextBlockIndex != BLOCK_UNUSED)
4168 if (depotIndex < COUNT_BBDEPOTINHEADER)
4170 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4173 * Grow the primary depot.
4175 if (depotBlockIndexPos == BLOCK_UNUSED)
4177 depotBlockIndexPos = depotIndex*blocksPerDepot;
4180 * Add a block depot.
4182 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4183 This->bigBlockDepotCount++;
4184 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4187 * Flag it as a block depot.
4189 StorageImpl_SetNextBlockInChain(This,
4190 depotBlockIndexPos,
4191 BLOCK_SPECIAL);
4193 /* Save new header information.
4195 StorageImpl_SaveFileHeader(This);
4198 else
4200 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4202 if (depotBlockIndexPos == BLOCK_UNUSED)
4205 * Grow the extended depot.
4207 ULONG extIndex = BLOCK_UNUSED;
4208 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4209 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4211 if (extBlockOffset == 0)
4213 /* We need an extended block.
4215 extIndex = Storage32Impl_AddExtBlockDepot(This);
4216 This->extBigBlockDepotCount++;
4217 depotBlockIndexPos = extIndex + 1;
4219 else
4220 depotBlockIndexPos = depotIndex * blocksPerDepot;
4223 * Add a block depot and mark it in the extended block.
4225 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4226 This->bigBlockDepotCount++;
4227 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4229 /* Flag the block depot.
4231 StorageImpl_SetNextBlockInChain(This,
4232 depotBlockIndexPos,
4233 BLOCK_SPECIAL);
4235 /* If necessary, flag the extended depot block.
4237 if (extIndex != BLOCK_UNUSED)
4238 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4240 /* Save header information.
4242 StorageImpl_SaveFileHeader(This);
4246 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4248 if (read)
4250 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4251 ( nextBlockIndex != BLOCK_UNUSED))
4253 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4255 if (nextBlockIndex == BLOCK_UNUSED)
4257 freeBlock = (depotIndex * blocksPerDepot) +
4258 (depotBlockOffset/sizeof(ULONG));
4261 depotBlockOffset += sizeof(ULONG);
4265 depotIndex++;
4266 depotBlockOffset = 0;
4270 * make sure that the block physically exists before using it
4272 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4274 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4276 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4277 ILockBytes_SetSize(This->lockBytes, neededSize);
4279 This->prevFreeBlock = freeBlock;
4281 return freeBlock;
4284 /******************************************************************************
4285 * StorageImpl_FreeBigBlock
4287 * This method will flag the specified block as free in the big block depot.
4289 static void StorageImpl_FreeBigBlock(
4290 StorageImpl* This,
4291 ULONG blockIndex)
4293 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4295 if (blockIndex < This->prevFreeBlock)
4296 This->prevFreeBlock = blockIndex;
4300 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4301 DirRef index, const DirEntry *data)
4303 StorageImpl *This = (StorageImpl*)base;
4304 return StorageImpl_WriteDirEntry(This, index, data);
4307 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4308 DirRef index, DirEntry *data)
4310 StorageImpl *This = (StorageImpl*)base;
4311 return StorageImpl_ReadDirEntry(This, index, data);
4314 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4316 int i;
4318 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4320 if (!This->blockChainCache[i])
4322 return &This->blockChainCache[i];
4326 i = This->blockChainToEvict;
4328 BlockChainStream_Destroy(This->blockChainCache[i]);
4329 This->blockChainCache[i] = NULL;
4331 This->blockChainToEvict++;
4332 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4333 This->blockChainToEvict = 0;
4335 return &This->blockChainCache[i];
4338 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4339 DirRef index)
4341 int i, free_index=-1;
4343 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4345 if (!This->blockChainCache[i])
4347 if (free_index == -1) free_index = i;
4349 else if (This->blockChainCache[i]->ownerDirEntry == index)
4351 return &This->blockChainCache[i];
4355 if (free_index == -1)
4357 free_index = This->blockChainToEvict;
4359 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4360 This->blockChainCache[free_index] = NULL;
4362 This->blockChainToEvict++;
4363 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4364 This->blockChainToEvict = 0;
4367 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4368 return &This->blockChainCache[free_index];
4371 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4373 int i;
4375 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4377 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4379 BlockChainStream_Destroy(This->blockChainCache[i]);
4380 This->blockChainCache[i] = NULL;
4381 return;
4386 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4387 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4389 StorageImpl *This = (StorageImpl*)base;
4390 DirEntry data;
4391 HRESULT hr;
4392 ULONG bytesToRead;
4394 hr = StorageImpl_ReadDirEntry(This, index, &data);
4395 if (FAILED(hr)) return hr;
4397 if (data.size.QuadPart == 0)
4399 *bytesRead = 0;
4400 return S_OK;
4403 if (offset.QuadPart + size > data.size.QuadPart)
4405 bytesToRead = data.size.QuadPart - offset.QuadPart;
4407 else
4409 bytesToRead = size;
4412 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4414 SmallBlockChainStream *stream;
4416 stream = SmallBlockChainStream_Construct(This, NULL, index);
4417 if (!stream) return E_OUTOFMEMORY;
4419 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4421 SmallBlockChainStream_Destroy(stream);
4423 return hr;
4425 else
4427 BlockChainStream *stream = NULL;
4429 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4430 if (!stream) return E_OUTOFMEMORY;
4432 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4434 return hr;
4438 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4439 ULARGE_INTEGER newsize)
4441 StorageImpl *This = (StorageImpl*)base;
4442 DirEntry data;
4443 HRESULT hr;
4444 SmallBlockChainStream *smallblock=NULL;
4445 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4447 hr = StorageImpl_ReadDirEntry(This, index, &data);
4448 if (FAILED(hr)) return hr;
4450 /* In simple mode keep the stream size above the small block limit */
4451 if (This->base.openFlags & STGM_SIMPLE)
4452 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4454 if (data.size.QuadPart == newsize.QuadPart)
4455 return S_OK;
4457 /* Create a block chain object of the appropriate type */
4458 if (data.size.QuadPart == 0)
4460 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4462 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4463 if (!smallblock) return E_OUTOFMEMORY;
4465 else
4467 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4468 bigblock = *pbigblock;
4469 if (!bigblock) return E_OUTOFMEMORY;
4472 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4474 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4475 if (!smallblock) return E_OUTOFMEMORY;
4477 else
4479 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4480 bigblock = *pbigblock;
4481 if (!bigblock) return E_OUTOFMEMORY;
4484 /* Change the block chain type if necessary. */
4485 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4487 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4488 if (!bigblock)
4490 SmallBlockChainStream_Destroy(smallblock);
4491 return E_FAIL;
4494 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4495 *pbigblock = bigblock;
4497 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4499 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4500 if (!smallblock)
4501 return E_FAIL;
4504 /* Set the size of the block chain. */
4505 if (smallblock)
4507 SmallBlockChainStream_SetSize(smallblock, newsize);
4508 SmallBlockChainStream_Destroy(smallblock);
4510 else
4512 BlockChainStream_SetSize(bigblock, newsize);
4515 /* Set the size in the directory entry. */
4516 hr = StorageImpl_ReadDirEntry(This, index, &data);
4517 if (SUCCEEDED(hr))
4519 data.size = newsize;
4521 hr = StorageImpl_WriteDirEntry(This, index, &data);
4523 return hr;
4526 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4527 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4529 StorageImpl *This = (StorageImpl*)base;
4530 DirEntry data;
4531 HRESULT hr;
4532 ULARGE_INTEGER newSize;
4534 hr = StorageImpl_ReadDirEntry(This, index, &data);
4535 if (FAILED(hr)) return hr;
4537 /* Grow the stream if necessary */
4538 newSize.QuadPart = offset.QuadPart + size;
4540 if (newSize.QuadPart > data.size.QuadPart)
4542 hr = StorageImpl_StreamSetSize(base, index, newSize);
4543 if (FAILED(hr))
4544 return hr;
4546 hr = StorageImpl_ReadDirEntry(This, index, &data);
4547 if (FAILED(hr)) return hr;
4550 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4552 SmallBlockChainStream *stream;
4554 stream = SmallBlockChainStream_Construct(This, NULL, index);
4555 if (!stream) return E_OUTOFMEMORY;
4557 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4559 SmallBlockChainStream_Destroy(stream);
4561 return hr;
4563 else
4565 BlockChainStream *stream;
4567 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4568 if (!stream) return E_OUTOFMEMORY;
4570 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4574 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4575 DirRef src)
4577 StorageImpl *This = (StorageImpl*)base;
4578 DirEntry dst_data, src_data;
4579 HRESULT hr;
4581 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4583 if (SUCCEEDED(hr))
4584 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4586 if (SUCCEEDED(hr))
4588 StorageImpl_DeleteCachedBlockChainStream(This, src);
4589 dst_data.startingBlock = src_data.startingBlock;
4590 dst_data.size = src_data.size;
4592 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4595 return hr;
4598 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4600 HRESULT hr=S_OK;
4601 DirEntry currentEntry;
4602 DirRef currentEntryRef;
4603 BlockChainStream *blockChainStream;
4605 if (create)
4607 ULARGE_INTEGER size;
4608 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4610 /* Discard any existing data. */
4611 size.QuadPart = 0;
4612 ILockBytes_SetSize(This->lockBytes, size);
4615 * Initialize all header variables:
4616 * - The big block depot consists of one block and it is at block 0
4617 * - The directory table starts at block 1
4618 * - There is no small block depot
4620 memset( This->bigBlockDepotStart,
4621 BLOCK_UNUSED,
4622 sizeof(This->bigBlockDepotStart));
4624 This->bigBlockDepotCount = 1;
4625 This->bigBlockDepotStart[0] = 0;
4626 This->rootStartBlock = 1;
4627 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4628 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4629 if (This->bigBlockSize == 4096)
4630 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4631 else
4632 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4633 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4634 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4635 This->extBigBlockDepotCount = 0;
4637 StorageImpl_SaveFileHeader(This);
4640 * Add one block for the big block depot and one block for the directory table
4642 size.u.HighPart = 0;
4643 size.u.LowPart = This->bigBlockSize * 3;
4644 ILockBytes_SetSize(This->lockBytes, size);
4647 * Initialize the big block depot
4649 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4650 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4651 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4652 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4654 else
4657 * Load the header for the file.
4659 hr = StorageImpl_LoadFileHeader(This);
4661 if (FAILED(hr))
4663 return hr;
4668 * There is no block depot cached yet.
4670 This->indexBlockDepotCached = 0xFFFFFFFF;
4671 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4674 * Start searching for free blocks with block 0.
4676 This->prevFreeBlock = 0;
4678 This->firstFreeSmallBlock = 0;
4680 /* Read the extended big block depot locations. */
4681 if (This->extBigBlockDepotCount != 0)
4683 ULONG current_block = This->extBigBlockDepotStart;
4684 ULONG cache_size = This->extBigBlockDepotCount * 2;
4685 ULONG i;
4687 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4688 if (!This->extBigBlockDepotLocations)
4690 return E_OUTOFMEMORY;
4693 This->extBigBlockDepotLocationsSize = cache_size;
4695 for (i=0; i<This->extBigBlockDepotCount; i++)
4697 if (current_block == BLOCK_END_OF_CHAIN)
4699 WARN("File has too few extended big block depot blocks.\n");
4700 return STG_E_DOCFILECORRUPT;
4702 This->extBigBlockDepotLocations[i] = current_block;
4703 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4706 else
4708 This->extBigBlockDepotLocations = NULL;
4709 This->extBigBlockDepotLocationsSize = 0;
4713 * Create the block chain abstractions.
4715 if(!(blockChainStream =
4716 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4718 return STG_E_READFAULT;
4720 if (!new_object)
4721 BlockChainStream_Destroy(This->rootBlockChain);
4722 This->rootBlockChain = blockChainStream;
4724 if(!(blockChainStream =
4725 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4726 DIRENTRY_NULL)))
4728 return STG_E_READFAULT;
4730 if (!new_object)
4731 BlockChainStream_Destroy(This->smallBlockDepotChain);
4732 This->smallBlockDepotChain = blockChainStream;
4735 * Write the root storage entry (memory only)
4737 if (create)
4739 DirEntry rootEntry;
4741 * Initialize the directory table
4743 memset(&rootEntry, 0, sizeof(rootEntry));
4744 lstrcpyW(rootEntry.name, L"Root Entry");
4745 rootEntry.sizeOfNameString = sizeof(L"Root Entry");
4746 rootEntry.stgType = STGTY_ROOT;
4747 rootEntry.leftChild = DIRENTRY_NULL;
4748 rootEntry.rightChild = DIRENTRY_NULL;
4749 rootEntry.dirRootEntry = DIRENTRY_NULL;
4750 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4751 rootEntry.size.u.HighPart = 0;
4752 rootEntry.size.u.LowPart = 0;
4754 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4758 * Find the ID of the root storage.
4760 currentEntryRef = 0;
4764 hr = StorageImpl_ReadDirEntry(
4765 This,
4766 currentEntryRef,
4767 &currentEntry);
4769 if (SUCCEEDED(hr))
4771 if ( (currentEntry.sizeOfNameString != 0 ) &&
4772 (currentEntry.stgType == STGTY_ROOT) )
4774 This->base.storageDirEntry = currentEntryRef;
4778 currentEntryRef++;
4780 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4782 if (FAILED(hr))
4784 return STG_E_READFAULT;
4788 * Create the block chain abstraction for the small block root chain.
4790 if(!(blockChainStream =
4791 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4793 return STG_E_READFAULT;
4795 if (!new_object)
4796 BlockChainStream_Destroy(This->smallBlockRootChain);
4797 This->smallBlockRootChain = blockChainStream;
4799 if (!new_object)
4801 int i;
4802 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4804 BlockChainStream_Destroy(This->blockChainCache[i]);
4805 This->blockChainCache[i] = NULL;
4809 return hr;
4812 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4813 ULONG* result, BOOL refresh)
4815 StorageImpl *This = (StorageImpl*)base;
4816 HRESULT hr=S_OK;
4817 DWORD oldTransactionSig = This->transactionSig;
4819 if (refresh)
4821 ULARGE_INTEGER offset;
4822 ULONG bytes_read;
4823 BYTE data[4];
4825 offset.u.HighPart = 0;
4826 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4827 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4829 if (SUCCEEDED(hr))
4831 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4833 if (oldTransactionSig != This->transactionSig)
4835 /* Someone else wrote to this, so toss all cached information. */
4836 TRACE("signature changed\n");
4838 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4841 if (FAILED(hr))
4842 This->transactionSig = oldTransactionSig;
4846 *result = This->transactionSig;
4848 return hr;
4851 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4852 ULONG value)
4854 StorageImpl *This = (StorageImpl*)base;
4856 This->transactionSig = value;
4857 StorageImpl_SaveFileHeader(This);
4859 return S_OK;
4862 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4863 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4865 if ((dwLockType & This->locks_supported) == 0)
4867 if (supported) *supported = FALSE;
4868 return S_OK;
4871 if (supported) *supported = TRUE;
4872 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4875 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4876 ULARGE_INTEGER cb, DWORD dwLockType)
4878 if ((dwLockType & This->locks_supported) == 0)
4879 return S_OK;
4881 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4884 /* Internal function */
4885 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4886 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4888 HRESULT hr;
4889 int delay = 0;
4890 DWORD start_time = GetTickCount();
4891 DWORD last_sanity_check = start_time;
4892 ULARGE_INTEGER sanity_offset, sanity_cb;
4894 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4895 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4899 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4901 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4903 DWORD current_time = GetTickCount();
4904 if (current_time - start_time >= 20000)
4906 /* timeout */
4907 break;
4909 if (current_time - last_sanity_check >= 500)
4911 /* Any storage implementation with the file open in a
4912 * shared mode should not lock these bytes for writing. However,
4913 * some programs (LibreOffice Writer) will keep ALL bytes locked
4914 * when opening in exclusive mode. We can use a read lock to
4915 * detect this case early, and not hang a full 20 seconds.
4917 * This can collide with another attempt to open the file in
4918 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4919 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4920 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4921 break;
4922 if (SUCCEEDED(hr))
4924 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4925 hr = STG_E_ACCESSDENIED;
4928 last_sanity_check = current_time;
4930 Sleep(delay);
4931 if (delay < 150) delay++;
4933 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4935 return hr;
4938 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4940 StorageImpl *This = (StorageImpl*)base;
4941 HRESULT hr;
4942 ULARGE_INTEGER offset, cb;
4944 if (write)
4946 /* Synchronous grab of second priority range, the commit lock, and the
4947 * lock-checking lock. */
4948 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4949 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4951 else
4953 offset.QuadPart = RANGELOCK_COMMIT;
4954 cb.QuadPart = 1;
4957 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4959 return hr;
4962 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4964 StorageImpl *This = (StorageImpl*)base;
4965 HRESULT hr;
4966 ULARGE_INTEGER offset, cb;
4968 if (write)
4970 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4971 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4973 else
4975 offset.QuadPart = RANGELOCK_COMMIT;
4976 cb.QuadPart = 1;
4979 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4981 return hr;
4984 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4986 StorageImpl *This = (StorageImpl*) iface;
4987 STATSTG statstg;
4988 HRESULT hr;
4990 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4992 *result = statstg.pwcsName;
4994 return hr;
4997 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
4998 ULONG end, HRESULT fail_hr)
5000 HRESULT hr;
5001 ULARGE_INTEGER offset, cb;
5003 offset.QuadPart = start;
5004 cb.QuadPart = 1 + end - start;
5006 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5007 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5009 if (FAILED(hr))
5010 return fail_hr;
5011 else
5012 return S_OK;
5015 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
5017 HRESULT hr=S_OK;
5018 int i, j;
5019 ULARGE_INTEGER offset, cb;
5021 cb.QuadPart = 1;
5023 for (i=start; i<=end; i++)
5025 offset.QuadPart = i;
5026 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5027 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
5028 break;
5031 if (SUCCEEDED(hr))
5033 for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++)
5035 if (This->locked_bytes[j] == 0)
5037 This->locked_bytes[j] = i;
5038 break;
5043 return hr;
5046 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5048 HRESULT hr;
5049 ULARGE_INTEGER offset;
5050 ULARGE_INTEGER cb;
5051 DWORD share_mode = STGM_SHARE_MODE(openFlags);
5052 BOOL supported;
5054 if (openFlags & STGM_NOSNAPSHOT)
5056 /* STGM_NOSNAPSHOT implies deny write */
5057 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5058 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5061 /* Wrap all other locking inside a single lock so we can check ranges safely */
5062 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5063 cb.QuadPart = 1;
5064 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5066 /* If the ILockBytes doesn't support locking that's ok. */
5067 if (!supported) return S_OK;
5068 else if (FAILED(hr)) return hr;
5070 hr = S_OK;
5072 /* First check for any conflicting locks. */
5073 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5074 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5076 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5077 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5079 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5080 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5082 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5083 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5085 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5086 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5088 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5090 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5092 if (SUCCEEDED(hr))
5093 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5096 /* Then grab our locks. */
5097 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5099 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5100 if (SUCCEEDED(hr))
5101 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5104 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5105 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5107 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5108 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5110 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5111 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5113 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5114 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5116 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5117 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5119 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5120 cb.QuadPart = 1;
5121 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5123 return hr;
5126 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5128 StorageImpl *This = (StorageImpl*)storage;
5129 int i;
5130 HRESULT hr;
5131 TRACE("(%p)\n", This);
5133 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5135 if (SUCCEEDED(hr))
5136 hr = BlockChainStream_Flush(This->rootBlockChain);
5138 if (SUCCEEDED(hr))
5139 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5141 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5142 if (This->blockChainCache[i])
5143 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5145 if (SUCCEEDED(hr))
5146 hr = ILockBytes_Flush(This->lockBytes);
5148 return hr;
5151 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5153 StorageImpl *This = (StorageImpl*) iface;
5155 StorageBaseImpl_DeleteAll(&This->base);
5157 This->base.reverted = TRUE;
5160 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5162 StorageImpl *This = (StorageImpl*) iface;
5163 int i;
5164 TRACE("(%p)\n", This);
5166 StorageImpl_Flush(iface);
5168 StorageImpl_Invalidate(iface);
5170 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5172 BlockChainStream_Destroy(This->smallBlockRootChain);
5173 BlockChainStream_Destroy(This->rootBlockChain);
5174 BlockChainStream_Destroy(This->smallBlockDepotChain);
5176 for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++)
5177 BlockChainStream_Destroy(This->blockChainCache[i]);
5179 for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++)
5181 ULARGE_INTEGER offset, cb;
5182 cb.QuadPart = 1;
5183 if (This->locked_bytes[i] != 0)
5185 offset.QuadPart = This->locked_bytes[i];
5186 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5190 if (This->lockBytes)
5191 ILockBytes_Release(This->lockBytes);
5192 HeapFree(GetProcessHeap(), 0, This);
5196 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5198 StorageImpl_Destroy,
5199 StorageImpl_Invalidate,
5200 StorageImpl_Flush,
5201 StorageImpl_GetFilename,
5202 StorageImpl_CreateDirEntry,
5203 StorageImpl_BaseWriteDirEntry,
5204 StorageImpl_BaseReadDirEntry,
5205 StorageImpl_DestroyDirEntry,
5206 StorageImpl_StreamReadAt,
5207 StorageImpl_StreamWriteAt,
5208 StorageImpl_StreamSetSize,
5209 StorageImpl_StreamLink,
5210 StorageImpl_GetTransactionSig,
5211 StorageImpl_SetTransactionSig,
5212 StorageImpl_LockTransaction,
5213 StorageImpl_UnlockTransaction
5218 * Virtual function table for the IStorageBaseImpl class.
5220 static const IStorageVtbl StorageImpl_Vtbl =
5222 StorageBaseImpl_QueryInterface,
5223 StorageBaseImpl_AddRef,
5224 StorageBaseImpl_Release,
5225 StorageBaseImpl_CreateStream,
5226 StorageBaseImpl_OpenStream,
5227 StorageBaseImpl_CreateStorage,
5228 StorageBaseImpl_OpenStorage,
5229 StorageBaseImpl_CopyTo,
5230 StorageBaseImpl_MoveElementTo,
5231 StorageBaseImpl_Commit,
5232 StorageBaseImpl_Revert,
5233 StorageBaseImpl_EnumElements,
5234 StorageBaseImpl_DestroyElement,
5235 StorageBaseImpl_RenameElement,
5236 StorageBaseImpl_SetElementTimes,
5237 StorageBaseImpl_SetClass,
5238 StorageBaseImpl_SetStateBits,
5239 StorageBaseImpl_Stat
5242 static HRESULT StorageImpl_Construct(
5243 HANDLE hFile,
5244 LPCOLESTR pwcsName,
5245 ILockBytes* pLkbyt,
5246 DWORD openFlags,
5247 BOOL fileBased,
5248 BOOL create,
5249 ULONG sector_size,
5250 StorageImpl** result)
5252 StorageImpl* This;
5253 HRESULT hr = S_OK;
5254 STATSTG stat;
5256 if ( FAILED( validateSTGM(openFlags) ))
5257 return STG_E_INVALIDFLAG;
5259 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5260 if (!This)
5261 return E_OUTOFMEMORY;
5263 memset(This, 0, sizeof(StorageImpl));
5265 list_init(&This->base.strmHead);
5267 list_init(&This->base.storageHead);
5269 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5270 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5271 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5272 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5273 This->base.openFlags = (openFlags & ~STGM_CREATE);
5274 This->base.ref = 1;
5275 This->base.create = create;
5277 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5278 This->base.lockingrole = SWMR_Writer;
5279 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5280 This->base.lockingrole = SWMR_Reader;
5281 else
5282 This->base.lockingrole = SWMR_None;
5284 This->base.reverted = FALSE;
5287 * Initialize the big block cache.
5289 This->bigBlockSize = sector_size;
5290 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5291 if (hFile)
5292 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5293 else
5295 This->lockBytes = pLkbyt;
5296 ILockBytes_AddRef(pLkbyt);
5299 if (SUCCEEDED(hr))
5300 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5302 if (SUCCEEDED(hr))
5304 This->locks_supported = stat.grfLocksSupported;
5305 if (!hFile)
5306 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5307 This->locks_supported &= ~WINE_LOCK_READ;
5309 hr = StorageImpl_GrabLocks(This, openFlags);
5312 if (SUCCEEDED(hr))
5313 hr = StorageImpl_Refresh(This, TRUE, create);
5315 if (FAILED(hr))
5317 IStorage_Release(&This->base.IStorage_iface);
5318 *result = NULL;
5320 else
5322 StorageImpl_Flush(&This->base);
5323 *result = This;
5326 return hr;
5330 /************************************************************************
5331 * StorageInternalImpl implementation
5332 ***********************************************************************/
5334 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5336 StorageInternalImpl* This = (StorageInternalImpl*) base;
5338 if (!This->base.reverted)
5340 TRACE("Storage invalidated (stg=%p)\n", This);
5342 This->base.reverted = TRUE;
5344 This->parentStorage = NULL;
5346 StorageBaseImpl_DeleteAll(&This->base);
5348 list_remove(&This->ParentListEntry);
5352 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5354 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5356 StorageInternalImpl_Invalidate(&This->base);
5358 HeapFree(GetProcessHeap(), 0, This);
5361 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5363 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5365 return StorageBaseImpl_Flush(This->parentStorage);
5368 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5370 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5372 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5375 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5376 const DirEntry *newData, DirRef *index)
5378 StorageInternalImpl* This = (StorageInternalImpl*) base;
5380 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5381 newData, index);
5384 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5385 DirRef index, const DirEntry *data)
5387 StorageInternalImpl* This = (StorageInternalImpl*) base;
5389 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5390 index, data);
5393 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5394 DirRef index, DirEntry *data)
5396 StorageInternalImpl* This = (StorageInternalImpl*) base;
5398 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5399 index, data);
5402 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5403 DirRef index)
5405 StorageInternalImpl* This = (StorageInternalImpl*) base;
5407 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5408 index);
5411 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5412 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5414 StorageInternalImpl* This = (StorageInternalImpl*) base;
5416 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5417 index, offset, size, buffer, bytesRead);
5420 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5421 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5423 StorageInternalImpl* This = (StorageInternalImpl*) base;
5425 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5426 index, offset, size, buffer, bytesWritten);
5429 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5430 DirRef index, ULARGE_INTEGER newsize)
5432 StorageInternalImpl* This = (StorageInternalImpl*) base;
5434 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5435 index, newsize);
5438 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5439 DirRef dst, DirRef src)
5441 StorageInternalImpl* This = (StorageInternalImpl*) base;
5443 return StorageBaseImpl_StreamLink(This->parentStorage,
5444 dst, src);
5447 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5448 ULONG* result, BOOL refresh)
5450 return E_NOTIMPL;
5453 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5454 ULONG value)
5456 return E_NOTIMPL;
5459 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5461 return E_NOTIMPL;
5464 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5466 return E_NOTIMPL;
5469 /******************************************************************************
5471 ** StorageInternalImpl_Commit
5474 static HRESULT WINAPI StorageInternalImpl_Commit(
5475 IStorage* iface,
5476 DWORD grfCommitFlags) /* [in] */
5478 StorageBaseImpl* This = impl_from_IStorage(iface);
5479 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5480 return StorageBaseImpl_Flush(This);
5483 /******************************************************************************
5485 ** StorageInternalImpl_Revert
5488 static HRESULT WINAPI StorageInternalImpl_Revert(
5489 IStorage* iface)
5491 FIXME("(%p): stub\n", iface);
5492 return S_OK;
5496 * Virtual function table for the StorageInternalImpl class.
5498 static const IStorageVtbl StorageInternalImpl_Vtbl =
5500 StorageBaseImpl_QueryInterface,
5501 StorageBaseImpl_AddRef,
5502 StorageBaseImpl_Release,
5503 StorageBaseImpl_CreateStream,
5504 StorageBaseImpl_OpenStream,
5505 StorageBaseImpl_CreateStorage,
5506 StorageBaseImpl_OpenStorage,
5507 StorageBaseImpl_CopyTo,
5508 StorageBaseImpl_MoveElementTo,
5509 StorageInternalImpl_Commit,
5510 StorageInternalImpl_Revert,
5511 StorageBaseImpl_EnumElements,
5512 StorageBaseImpl_DestroyElement,
5513 StorageBaseImpl_RenameElement,
5514 StorageBaseImpl_SetElementTimes,
5515 StorageBaseImpl_SetClass,
5516 StorageBaseImpl_SetStateBits,
5517 StorageBaseImpl_Stat
5520 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5522 StorageInternalImpl_Destroy,
5523 StorageInternalImpl_Invalidate,
5524 StorageInternalImpl_Flush,
5525 StorageInternalImpl_GetFilename,
5526 StorageInternalImpl_CreateDirEntry,
5527 StorageInternalImpl_WriteDirEntry,
5528 StorageInternalImpl_ReadDirEntry,
5529 StorageInternalImpl_DestroyDirEntry,
5530 StorageInternalImpl_StreamReadAt,
5531 StorageInternalImpl_StreamWriteAt,
5532 StorageInternalImpl_StreamSetSize,
5533 StorageInternalImpl_StreamLink,
5534 StorageInternalImpl_GetTransactionSig,
5535 StorageInternalImpl_SetTransactionSig,
5536 StorageInternalImpl_LockTransaction,
5537 StorageInternalImpl_UnlockTransaction
5540 static StorageInternalImpl* StorageInternalImpl_Construct(
5541 StorageBaseImpl* parentStorage,
5542 DWORD openFlags,
5543 DirRef storageDirEntry)
5545 StorageInternalImpl* newStorage;
5547 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5549 if (newStorage!=0)
5551 list_init(&newStorage->base.strmHead);
5553 list_init(&newStorage->base.storageHead);
5556 * Initialize the virtual function table.
5558 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5559 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5560 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5561 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5563 newStorage->base.reverted = FALSE;
5565 newStorage->base.ref = 1;
5567 newStorage->parentStorage = parentStorage;
5570 * Keep a reference to the directory entry of this storage
5572 newStorage->base.storageDirEntry = storageDirEntry;
5574 newStorage->base.create = FALSE;
5576 return newStorage;
5579 return 0;
5583 /************************************************************************
5584 * TransactedSnapshotImpl implementation
5585 ***********************************************************************/
5587 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5589 DirRef result=This->firstFreeEntry;
5591 while (result < This->entries_size && This->entries[result].inuse)
5592 result++;
5594 if (result == This->entries_size)
5596 ULONG new_size = This->entries_size * 2;
5597 TransactedDirEntry *new_entries;
5599 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5600 if (!new_entries) return DIRENTRY_NULL;
5602 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5603 HeapFree(GetProcessHeap(), 0, This->entries);
5605 This->entries = new_entries;
5606 This->entries_size = new_size;
5609 This->entries[result].inuse = TRUE;
5611 This->firstFreeEntry = result+1;
5613 return result;
5616 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5617 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5619 DirRef stubEntryRef;
5620 TransactedDirEntry *entry;
5622 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5624 if (stubEntryRef != DIRENTRY_NULL)
5626 entry = &This->entries[stubEntryRef];
5628 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5630 entry->read = FALSE;
5633 return stubEntryRef;
5636 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5637 TransactedSnapshotImpl *This, DirRef entry)
5639 HRESULT hr=S_OK;
5640 DirEntry data;
5642 if (!This->entries[entry].read)
5644 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5645 This->entries[entry].transactedParentEntry,
5646 &data);
5648 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5650 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5652 if (data.leftChild == DIRENTRY_NULL)
5653 hr = E_OUTOFMEMORY;
5656 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5658 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5660 if (data.rightChild == DIRENTRY_NULL)
5661 hr = E_OUTOFMEMORY;
5664 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5666 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5668 if (data.dirRootEntry == DIRENTRY_NULL)
5669 hr = E_OUTOFMEMORY;
5672 if (SUCCEEDED(hr))
5674 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5675 This->entries[entry].read = TRUE;
5679 return hr;
5682 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5683 TransactedSnapshotImpl *This, DirRef entry)
5685 HRESULT hr = S_OK;
5687 if (!This->entries[entry].stream_dirty)
5689 DirEntry new_entrydata;
5691 memset(&new_entrydata, 0, sizeof(DirEntry));
5692 new_entrydata.name[0] = 'S';
5693 new_entrydata.sizeOfNameString = 1;
5694 new_entrydata.stgType = STGTY_STREAM;
5695 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5696 new_entrydata.leftChild = DIRENTRY_NULL;
5697 new_entrydata.rightChild = DIRENTRY_NULL;
5698 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5700 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5701 &This->entries[entry].stream_entry);
5703 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5705 hr = StorageBaseImpl_CopyStream(
5706 This->scratch, This->entries[entry].stream_entry,
5707 This->transactedParent, This->entries[entry].transactedParentEntry);
5709 if (FAILED(hr))
5710 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5713 if (SUCCEEDED(hr))
5714 This->entries[entry].stream_dirty = TRUE;
5716 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5718 /* Since this entry is modified, and we aren't using its stream data, we
5719 * no longer care about the original entry. */
5720 DirRef delete_ref;
5721 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5723 if (delete_ref != DIRENTRY_NULL)
5724 This->entries[delete_ref].deleted = TRUE;
5726 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5730 return hr;
5733 /* Find the first entry in a depth-first traversal. */
5734 static DirRef TransactedSnapshotImpl_FindFirstChild(
5735 TransactedSnapshotImpl* This, DirRef parent)
5737 DirRef cursor, prev;
5738 TransactedDirEntry *entry;
5740 cursor = parent;
5741 entry = &This->entries[cursor];
5742 while (entry->read)
5744 if (entry->data.leftChild != DIRENTRY_NULL)
5746 prev = cursor;
5747 cursor = entry->data.leftChild;
5748 entry = &This->entries[cursor];
5749 entry->parent = prev;
5751 else if (entry->data.rightChild != DIRENTRY_NULL)
5753 prev = cursor;
5754 cursor = entry->data.rightChild;
5755 entry = &This->entries[cursor];
5756 entry->parent = prev;
5758 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5760 prev = cursor;
5761 cursor = entry->data.dirRootEntry;
5762 entry = &This->entries[cursor];
5763 entry->parent = prev;
5765 else
5766 break;
5769 return cursor;
5772 /* Find the next entry in a depth-first traversal. */
5773 static DirRef TransactedSnapshotImpl_FindNextChild(
5774 TransactedSnapshotImpl* This, DirRef current)
5776 DirRef parent;
5777 TransactedDirEntry *parent_entry;
5779 parent = This->entries[current].parent;
5780 parent_entry = &This->entries[parent];
5782 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5784 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5786 This->entries[parent_entry->data.rightChild].parent = parent;
5787 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5790 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5792 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5793 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5797 return parent;
5800 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5801 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5802 TransactedSnapshotImpl* This, DirRef entry)
5804 return entry != DIRENTRY_NULL &&
5805 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5808 /* Destroy the entries created by CopyTree. */
5809 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5810 TransactedSnapshotImpl* This, DirRef stop)
5812 DirRef cursor;
5813 TransactedDirEntry *entry;
5814 ULARGE_INTEGER zero;
5816 zero.QuadPart = 0;
5818 if (!This->entries[This->base.storageDirEntry].read)
5819 return;
5821 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5823 if (cursor == DIRENTRY_NULL)
5824 return;
5826 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5828 while (cursor != DIRENTRY_NULL && cursor != stop)
5830 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5832 entry = &This->entries[cursor];
5834 if (entry->stream_dirty)
5835 StorageBaseImpl_StreamSetSize(This->transactedParent,
5836 entry->newTransactedParentEntry, zero);
5838 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5839 entry->newTransactedParentEntry);
5841 entry->newTransactedParentEntry = entry->transactedParentEntry;
5844 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5848 /* Make a copy of our edited tree that we can use in the parent. */
5849 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5851 DirRef cursor;
5852 TransactedDirEntry *entry;
5853 HRESULT hr = S_OK;
5855 cursor = This->base.storageDirEntry;
5856 entry = &This->entries[cursor];
5857 entry->parent = DIRENTRY_NULL;
5858 entry->newTransactedParentEntry = entry->transactedParentEntry;
5860 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5861 return S_OK;
5863 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5865 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5866 entry = &This->entries[cursor];
5868 while (cursor != DIRENTRY_NULL)
5870 /* Make a copy of this entry in the transacted parent. */
5871 if (!entry->read ||
5872 (!entry->dirty && !entry->stream_dirty &&
5873 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5874 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5875 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5876 entry->newTransactedParentEntry = entry->transactedParentEntry;
5877 else
5879 DirEntry newData;
5881 memcpy(&newData, &entry->data, sizeof(DirEntry));
5883 newData.size.QuadPart = 0;
5884 newData.startingBlock = BLOCK_END_OF_CHAIN;
5886 if (newData.leftChild != DIRENTRY_NULL)
5887 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5889 if (newData.rightChild != DIRENTRY_NULL)
5890 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5892 if (newData.dirRootEntry != DIRENTRY_NULL)
5893 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5895 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5896 &entry->newTransactedParentEntry);
5897 if (FAILED(hr))
5899 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5900 return hr;
5903 if (entry->stream_dirty)
5905 hr = StorageBaseImpl_CopyStream(
5906 This->transactedParent, entry->newTransactedParentEntry,
5907 This->scratch, entry->stream_entry);
5909 else if (entry->data.size.QuadPart)
5911 hr = StorageBaseImpl_StreamLink(
5912 This->transactedParent, entry->newTransactedParentEntry,
5913 entry->transactedParentEntry);
5916 if (FAILED(hr))
5918 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5919 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5920 return hr;
5924 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5925 entry = &This->entries[cursor];
5928 return hr;
5931 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5932 IStorage* iface,
5933 DWORD grfCommitFlags) /* [in] */
5935 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5936 TransactedDirEntry *root_entry;
5937 DirRef i, dir_root_ref;
5938 DirEntry data;
5939 ULARGE_INTEGER zero;
5940 HRESULT hr;
5941 ULONG transactionSig;
5943 zero.QuadPart = 0;
5945 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5947 /* Cannot commit a read-only transacted storage */
5948 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5949 return STG_E_ACCESSDENIED;
5951 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5952 if (hr == E_NOTIMPL) hr = S_OK;
5953 if (SUCCEEDED(hr))
5955 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5956 if (SUCCEEDED(hr))
5958 if (transactionSig != This->lastTransactionSig)
5960 ERR("file was externally modified\n");
5961 hr = STG_E_NOTCURRENT;
5964 if (SUCCEEDED(hr))
5966 This->lastTransactionSig = transactionSig+1;
5967 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5970 else if (hr == E_NOTIMPL)
5971 hr = S_OK;
5973 if (FAILED(hr)) goto end;
5975 /* To prevent data loss, we create the new structure in the file before we
5976 * delete the old one, so that in case of errors the old data is intact. We
5977 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5978 * needed in the rare situation where we have just enough free disk space to
5979 * overwrite the existing data. */
5981 root_entry = &This->entries[This->base.storageDirEntry];
5983 if (!root_entry->read)
5984 goto end;
5986 hr = TransactedSnapshotImpl_CopyTree(This);
5987 if (FAILED(hr)) goto end;
5989 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5990 dir_root_ref = DIRENTRY_NULL;
5991 else
5992 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5994 hr = StorageBaseImpl_Flush(This->transactedParent);
5996 /* Update the storage to use the new data in one step. */
5997 if (SUCCEEDED(hr))
5998 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5999 root_entry->transactedParentEntry, &data);
6001 if (SUCCEEDED(hr))
6003 data.dirRootEntry = dir_root_ref;
6004 data.clsid = root_entry->data.clsid;
6005 data.ctime = root_entry->data.ctime;
6006 data.mtime = root_entry->data.mtime;
6008 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
6009 root_entry->transactedParentEntry, &data);
6012 /* Try to flush after updating the root storage, but if the flush fails, keep
6013 * going, on the theory that it'll either succeed later or the subsequent
6014 * writes will fail. */
6015 StorageBaseImpl_Flush(This->transactedParent);
6017 if (SUCCEEDED(hr))
6019 /* Destroy the old now-orphaned data. */
6020 for (i=0; i<This->entries_size; i++)
6022 TransactedDirEntry *entry = &This->entries[i];
6023 if (entry->inuse)
6025 if (entry->deleted)
6027 StorageBaseImpl_StreamSetSize(This->transactedParent,
6028 entry->transactedParentEntry, zero);
6029 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6030 entry->transactedParentEntry);
6031 memset(entry, 0, sizeof(TransactedDirEntry));
6032 This->firstFreeEntry = min(i, This->firstFreeEntry);
6034 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6036 if (entry->transactedParentEntry != DIRENTRY_NULL)
6037 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6038 entry->transactedParentEntry);
6039 if (entry->stream_dirty)
6041 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6042 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6043 entry->stream_dirty = FALSE;
6045 entry->dirty = FALSE;
6046 entry->transactedParentEntry = entry->newTransactedParentEntry;
6051 else
6053 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6056 if (SUCCEEDED(hr))
6057 hr = StorageBaseImpl_Flush(This->transactedParent);
6058 end:
6059 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6062 TRACE("<-- %08x\n", hr);
6063 return hr;
6066 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6067 IStorage* iface)
6069 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6070 ULARGE_INTEGER zero;
6071 ULONG i;
6073 TRACE("(%p)\n", iface);
6075 /* Destroy the open objects. */
6076 StorageBaseImpl_DeleteAll(&This->base);
6078 /* Clear out the scratch file. */
6079 zero.QuadPart = 0;
6080 for (i=0; i<This->entries_size; i++)
6082 if (This->entries[i].stream_dirty)
6084 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6085 zero);
6087 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6091 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6093 This->firstFreeEntry = 0;
6094 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6096 return S_OK;
6099 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6101 if (!This->reverted)
6103 TRACE("Storage invalidated (stg=%p)\n", This);
6105 This->reverted = TRUE;
6107 StorageBaseImpl_DeleteAll(This);
6111 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6113 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6115 IStorage_Revert(&This->base.IStorage_iface);
6116 IStorage_Release(&This->transactedParent->IStorage_iface);
6117 IStorage_Release(&This->scratch->IStorage_iface);
6118 HeapFree(GetProcessHeap(), 0, This->entries);
6119 HeapFree(GetProcessHeap(), 0, This);
6122 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6124 /* We only need to flush when committing. */
6125 return S_OK;
6128 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6130 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6132 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6135 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6136 const DirEntry *newData, DirRef *index)
6138 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6139 DirRef new_ref;
6140 TransactedDirEntry *new_entry;
6142 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6143 if (new_ref == DIRENTRY_NULL)
6144 return E_OUTOFMEMORY;
6146 new_entry = &This->entries[new_ref];
6148 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6149 new_entry->read = TRUE;
6150 new_entry->dirty = TRUE;
6151 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6153 *index = new_ref;
6155 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6157 return S_OK;
6160 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6161 DirRef index, const DirEntry *data)
6163 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6164 HRESULT hr;
6166 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6168 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6169 if (FAILED(hr))
6171 TRACE("<-- %08x\n", hr);
6172 return hr;
6175 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6177 if (index != This->base.storageDirEntry)
6179 This->entries[index].dirty = TRUE;
6181 if (data->size.QuadPart == 0 &&
6182 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6184 /* Since this entry is modified, and we aren't using its stream data, we
6185 * no longer care about the original entry. */
6186 DirRef delete_ref;
6187 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6189 if (delete_ref != DIRENTRY_NULL)
6190 This->entries[delete_ref].deleted = TRUE;
6192 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6195 TRACE("<-- S_OK\n");
6196 return S_OK;
6199 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6200 DirRef index, DirEntry *data)
6202 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6203 HRESULT hr;
6205 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6206 if (FAILED(hr))
6208 TRACE("<-- %08x\n", hr);
6209 return hr;
6212 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6214 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6216 return S_OK;
6219 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6220 DirRef index)
6222 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6224 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6225 This->entries[index].data.size.QuadPart != 0)
6227 /* If we deleted this entry while it has stream data. We must have left the
6228 * data because some other entry is using it, and we need to leave the
6229 * original entry alone. */
6230 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6231 This->firstFreeEntry = min(index, This->firstFreeEntry);
6233 else
6235 This->entries[index].deleted = TRUE;
6238 return S_OK;
6241 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6242 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6244 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6246 if (This->entries[index].stream_dirty)
6248 return StorageBaseImpl_StreamReadAt(This->scratch,
6249 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6251 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6253 /* This stream doesn't live in the parent, and we haven't allocated storage
6254 * for it yet */
6255 *bytesRead = 0;
6256 return S_OK;
6258 else
6260 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6261 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6265 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6266 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6268 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6269 HRESULT hr;
6271 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6272 if (FAILED(hr))
6274 TRACE("<-- %08x\n", hr);
6275 return hr;
6278 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6279 if (FAILED(hr))
6281 TRACE("<-- %08x\n", hr);
6282 return hr;
6285 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6286 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6288 if (SUCCEEDED(hr) && size != 0)
6289 This->entries[index].data.size.QuadPart = max(
6290 This->entries[index].data.size.QuadPart,
6291 offset.QuadPart + size);
6293 TRACE("<-- %08x\n", hr);
6294 return hr;
6297 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6298 DirRef index, ULARGE_INTEGER newsize)
6300 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6301 HRESULT hr;
6303 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6304 if (FAILED(hr))
6306 TRACE("<-- %08x\n", hr);
6307 return hr;
6310 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6311 return S_OK;
6313 if (newsize.QuadPart == 0)
6315 /* Destroy any parent references or entries in the scratch file. */
6316 if (This->entries[index].stream_dirty)
6318 ULARGE_INTEGER zero;
6319 zero.QuadPart = 0;
6320 StorageBaseImpl_StreamSetSize(This->scratch,
6321 This->entries[index].stream_entry, zero);
6322 StorageBaseImpl_DestroyDirEntry(This->scratch,
6323 This->entries[index].stream_entry);
6324 This->entries[index].stream_dirty = FALSE;
6326 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6328 DirRef delete_ref;
6329 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6331 if (delete_ref != DIRENTRY_NULL)
6332 This->entries[delete_ref].deleted = TRUE;
6334 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6337 else
6339 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6340 if (FAILED(hr)) return hr;
6342 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6343 This->entries[index].stream_entry, newsize);
6346 if (SUCCEEDED(hr))
6347 This->entries[index].data.size = newsize;
6349 TRACE("<-- %08x\n", hr);
6350 return hr;
6353 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6354 DirRef dst, DirRef src)
6356 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6357 HRESULT hr;
6358 TransactedDirEntry *dst_entry, *src_entry;
6360 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6361 if (FAILED(hr))
6363 TRACE("<-- %08x\n", hr);
6364 return hr;
6367 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6368 if (FAILED(hr))
6370 TRACE("<-- %08x\n", hr);
6371 return hr;
6374 dst_entry = &This->entries[dst];
6375 src_entry = &This->entries[src];
6377 dst_entry->stream_dirty = src_entry->stream_dirty;
6378 dst_entry->stream_entry = src_entry->stream_entry;
6379 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6380 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6381 dst_entry->data.size = src_entry->data.size;
6383 return S_OK;
6386 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6387 ULONG* result, BOOL refresh)
6389 return E_NOTIMPL;
6392 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6393 ULONG value)
6395 return E_NOTIMPL;
6398 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6400 return E_NOTIMPL;
6403 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6405 return E_NOTIMPL;
6408 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6410 StorageBaseImpl_QueryInterface,
6411 StorageBaseImpl_AddRef,
6412 StorageBaseImpl_Release,
6413 StorageBaseImpl_CreateStream,
6414 StorageBaseImpl_OpenStream,
6415 StorageBaseImpl_CreateStorage,
6416 StorageBaseImpl_OpenStorage,
6417 StorageBaseImpl_CopyTo,
6418 StorageBaseImpl_MoveElementTo,
6419 TransactedSnapshotImpl_Commit,
6420 TransactedSnapshotImpl_Revert,
6421 StorageBaseImpl_EnumElements,
6422 StorageBaseImpl_DestroyElement,
6423 StorageBaseImpl_RenameElement,
6424 StorageBaseImpl_SetElementTimes,
6425 StorageBaseImpl_SetClass,
6426 StorageBaseImpl_SetStateBits,
6427 StorageBaseImpl_Stat
6430 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6432 TransactedSnapshotImpl_Destroy,
6433 TransactedSnapshotImpl_Invalidate,
6434 TransactedSnapshotImpl_Flush,
6435 TransactedSnapshotImpl_GetFilename,
6436 TransactedSnapshotImpl_CreateDirEntry,
6437 TransactedSnapshotImpl_WriteDirEntry,
6438 TransactedSnapshotImpl_ReadDirEntry,
6439 TransactedSnapshotImpl_DestroyDirEntry,
6440 TransactedSnapshotImpl_StreamReadAt,
6441 TransactedSnapshotImpl_StreamWriteAt,
6442 TransactedSnapshotImpl_StreamSetSize,
6443 TransactedSnapshotImpl_StreamLink,
6444 TransactedSnapshotImpl_GetTransactionSig,
6445 TransactedSnapshotImpl_SetTransactionSig,
6446 TransactedSnapshotImpl_LockTransaction,
6447 TransactedSnapshotImpl_UnlockTransaction
6450 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6451 TransactedSnapshotImpl** result)
6453 HRESULT hr;
6455 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6456 if (*result)
6458 IStorage *scratch;
6460 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6462 /* This is OK because the property set storage functions use the IStorage functions. */
6463 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6464 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6466 list_init(&(*result)->base.strmHead);
6468 list_init(&(*result)->base.storageHead);
6470 (*result)->base.ref = 1;
6472 (*result)->base.openFlags = parentStorage->openFlags;
6474 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6475 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6477 /* Create a new temporary storage to act as the scratch file. */
6478 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6479 0, &scratch);
6480 (*result)->scratch = impl_from_IStorage(scratch);
6482 if (SUCCEEDED(hr))
6484 ULONG num_entries = 20;
6486 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6487 (*result)->entries_size = num_entries;
6488 (*result)->firstFreeEntry = 0;
6490 if ((*result)->entries)
6492 /* parentStorage already has 1 reference, which we take over here. */
6493 (*result)->transactedParent = parentStorage;
6495 parentStorage->transactedChild = &(*result)->base;
6497 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6499 else
6501 IStorage_Release(scratch);
6503 hr = E_OUTOFMEMORY;
6507 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6509 return hr;
6511 else
6512 return E_OUTOFMEMORY;
6516 /************************************************************************
6517 * TransactedSharedImpl implementation
6518 ***********************************************************************/
6520 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6522 if (!This->reverted)
6524 TRACE("Storage invalidated (stg=%p)\n", This);
6526 This->reverted = TRUE;
6528 StorageBaseImpl_DeleteAll(This);
6532 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6534 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6536 TransactedSharedImpl_Invalidate(&This->base);
6537 IStorage_Release(&This->transactedParent->IStorage_iface);
6538 IStorage_Release(&This->scratch->base.IStorage_iface);
6539 HeapFree(GetProcessHeap(), 0, This);
6542 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6544 /* We only need to flush when committing. */
6545 return S_OK;
6548 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6550 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6552 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6555 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6556 const DirEntry *newData, DirRef *index)
6558 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6560 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6561 newData, index);
6564 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6565 DirRef index, const DirEntry *data)
6567 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6569 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6570 index, data);
6573 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6574 DirRef index, DirEntry *data)
6576 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6578 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6579 index, data);
6582 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6583 DirRef index)
6585 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6587 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6588 index);
6591 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6592 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6594 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6596 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6597 index, offset, size, buffer, bytesRead);
6600 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6601 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6603 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6605 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6606 index, offset, size, buffer, bytesWritten);
6609 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6610 DirRef index, ULARGE_INTEGER newsize)
6612 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6614 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6615 index, newsize);
6618 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6619 DirRef dst, DirRef src)
6621 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6623 return StorageBaseImpl_StreamLink(&This->scratch->base,
6624 dst, src);
6627 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6628 ULONG* result, BOOL refresh)
6630 return E_NOTIMPL;
6633 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6634 ULONG value)
6636 return E_NOTIMPL;
6639 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6641 return E_NOTIMPL;
6644 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6646 return E_NOTIMPL;
6649 static HRESULT WINAPI TransactedSharedImpl_Commit(
6650 IStorage* iface,
6651 DWORD grfCommitFlags) /* [in] */
6653 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6654 DirRef new_storage_ref, prev_storage_ref;
6655 DirEntry src_data, dst_data;
6656 HRESULT hr;
6657 ULONG transactionSig;
6659 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6661 /* Cannot commit a read-only transacted storage */
6662 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6663 return STG_E_ACCESSDENIED;
6665 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6666 if (hr == E_NOTIMPL) hr = S_OK;
6667 if (SUCCEEDED(hr))
6669 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6670 if (SUCCEEDED(hr))
6672 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6673 hr = STG_E_NOTCURRENT;
6675 if (SUCCEEDED(hr))
6676 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6678 else if (hr == E_NOTIMPL)
6679 hr = S_OK;
6681 if (SUCCEEDED(hr))
6682 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6684 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6685 if (SUCCEEDED(hr))
6686 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6688 if (SUCCEEDED(hr))
6689 hr = StorageBaseImpl_Flush(This->transactedParent);
6691 if (SUCCEEDED(hr))
6692 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6694 if (SUCCEEDED(hr))
6696 prev_storage_ref = dst_data.dirRootEntry;
6697 dst_data.dirRootEntry = new_storage_ref;
6698 dst_data.clsid = src_data.clsid;
6699 dst_data.ctime = src_data.ctime;
6700 dst_data.mtime = src_data.mtime;
6701 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6704 if (SUCCEEDED(hr))
6706 /* Try to flush after updating the root storage, but if the flush fails, keep
6707 * going, on the theory that it'll either succeed later or the subsequent
6708 * writes will fail. */
6709 StorageBaseImpl_Flush(This->transactedParent);
6711 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6714 if (SUCCEEDED(hr))
6715 hr = StorageBaseImpl_Flush(This->transactedParent);
6717 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6719 if (SUCCEEDED(hr))
6720 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6722 if (SUCCEEDED(hr))
6724 This->lastTransactionSig = transactionSig+1;
6727 TRACE("<-- %08x\n", hr);
6728 return hr;
6731 static HRESULT WINAPI TransactedSharedImpl_Revert(
6732 IStorage* iface)
6734 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6736 TRACE("(%p)\n", iface);
6738 /* Destroy the open objects. */
6739 StorageBaseImpl_DeleteAll(&This->base);
6741 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6744 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6746 StorageBaseImpl_QueryInterface,
6747 StorageBaseImpl_AddRef,
6748 StorageBaseImpl_Release,
6749 StorageBaseImpl_CreateStream,
6750 StorageBaseImpl_OpenStream,
6751 StorageBaseImpl_CreateStorage,
6752 StorageBaseImpl_OpenStorage,
6753 StorageBaseImpl_CopyTo,
6754 StorageBaseImpl_MoveElementTo,
6755 TransactedSharedImpl_Commit,
6756 TransactedSharedImpl_Revert,
6757 StorageBaseImpl_EnumElements,
6758 StorageBaseImpl_DestroyElement,
6759 StorageBaseImpl_RenameElement,
6760 StorageBaseImpl_SetElementTimes,
6761 StorageBaseImpl_SetClass,
6762 StorageBaseImpl_SetStateBits,
6763 StorageBaseImpl_Stat
6766 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6768 TransactedSharedImpl_Destroy,
6769 TransactedSharedImpl_Invalidate,
6770 TransactedSharedImpl_Flush,
6771 TransactedSharedImpl_GetFilename,
6772 TransactedSharedImpl_CreateDirEntry,
6773 TransactedSharedImpl_WriteDirEntry,
6774 TransactedSharedImpl_ReadDirEntry,
6775 TransactedSharedImpl_DestroyDirEntry,
6776 TransactedSharedImpl_StreamReadAt,
6777 TransactedSharedImpl_StreamWriteAt,
6778 TransactedSharedImpl_StreamSetSize,
6779 TransactedSharedImpl_StreamLink,
6780 TransactedSharedImpl_GetTransactionSig,
6781 TransactedSharedImpl_SetTransactionSig,
6782 TransactedSharedImpl_LockTransaction,
6783 TransactedSharedImpl_UnlockTransaction
6786 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6787 TransactedSharedImpl** result)
6789 HRESULT hr;
6791 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6792 if (*result)
6794 IStorage *scratch;
6796 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6798 /* This is OK because the property set storage functions use the IStorage functions. */
6799 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6800 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6802 list_init(&(*result)->base.strmHead);
6804 list_init(&(*result)->base.storageHead);
6806 (*result)->base.ref = 1;
6808 (*result)->base.openFlags = parentStorage->openFlags;
6810 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6812 if (SUCCEEDED(hr))
6814 STGOPTIONS stgo;
6816 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6817 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6819 stgo.usVersion = 1;
6820 stgo.reserved = 0;
6821 stgo.ulSectorSize = 4096;
6822 stgo.pwcsTemplateFile = NULL;
6824 /* Create a new temporary storage to act as the scratch file. */
6825 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6826 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6827 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6829 if (SUCCEEDED(hr))
6831 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6832 parentStorage, parentStorage->storageDirEntry);
6834 if (SUCCEEDED(hr))
6836 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6838 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6839 (*result)->transactedParent = parentStorage;
6842 if (FAILED(hr))
6843 IStorage_Release(scratch);
6846 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6849 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6851 return hr;
6853 else
6854 return E_OUTOFMEMORY;
6857 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6858 BOOL toplevel, StorageBaseImpl** result)
6860 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6862 if (parentStorage->openFlags & fixme_flags)
6864 fixme_flags &= ~parentStorage->openFlags;
6865 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6868 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6869 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6870 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6872 /* Need to create a temp file for the snapshot */
6873 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6876 return TransactedSnapshotImpl_Construct(parentStorage,
6877 (TransactedSnapshotImpl**)result);
6880 static HRESULT Storage_Construct(
6881 HANDLE hFile,
6882 LPCOLESTR pwcsName,
6883 ILockBytes* pLkbyt,
6884 DWORD openFlags,
6885 BOOL fileBased,
6886 BOOL create,
6887 ULONG sector_size,
6888 StorageBaseImpl** result)
6890 StorageImpl *newStorage;
6891 StorageBaseImpl *newTransactedStorage;
6892 HRESULT hr;
6894 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6895 if (FAILED(hr)) goto end;
6897 if (openFlags & STGM_TRANSACTED)
6899 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6900 if (FAILED(hr))
6901 IStorage_Release(&newStorage->base.IStorage_iface);
6902 else
6903 *result = newTransactedStorage;
6905 else
6906 *result = &newStorage->base;
6908 end:
6909 return hr;
6913 /************************************************************************
6914 * StorageUtl helper functions
6915 ***********************************************************************/
6917 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6919 WORD tmp;
6921 memcpy(&tmp, buffer+offset, sizeof(WORD));
6922 *value = lendian16toh(tmp);
6925 void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value)
6927 value = htole16(value);
6928 memcpy((BYTE *)buffer + offset, &value, sizeof(WORD));
6931 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6933 DWORD tmp;
6935 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6936 *value = lendian32toh(tmp);
6939 void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value)
6941 value = htole32(value);
6942 memcpy((BYTE *)buffer + offset, &value, sizeof(DWORD));
6945 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6946 ULARGE_INTEGER* value)
6948 #ifdef WORDS_BIGENDIAN
6949 ULARGE_INTEGER tmp;
6951 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6952 value->u.LowPart = htole32(tmp.u.HighPart);
6953 value->u.HighPart = htole32(tmp.u.LowPart);
6954 #else
6955 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6956 #endif
6959 void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value)
6961 #ifdef WORDS_BIGENDIAN
6962 ULARGE_INTEGER tmp;
6964 tmp.u.LowPart = htole32(value->u.HighPart);
6965 tmp.u.HighPart = htole32(value->u.LowPart);
6966 memcpy((BYTE *)buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6967 #else
6968 memcpy((BYTE *)buffer + offset, value, sizeof(ULARGE_INTEGER));
6969 #endif
6972 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6974 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6975 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6976 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6978 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6981 void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value)
6983 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6984 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6985 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6987 memcpy((BYTE *)buffer + offset + 8, value->Data4, sizeof(value->Data4));
6990 void StorageUtl_CopyDirEntryToSTATSTG(
6991 StorageBaseImpl* storage,
6992 STATSTG* destination,
6993 const DirEntry* source,
6994 int statFlags)
6997 * The copy of the string occurs only when the flag is not set
6999 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
7001 /* Use the filename for the root storage. */
7002 destination->pwcsName = 0;
7003 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
7005 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
7006 (source->name[0] == 0) )
7008 destination->pwcsName = 0;
7010 else
7012 destination->pwcsName =
7013 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
7015 lstrcpyW(destination->pwcsName, source->name);
7018 switch (source->stgType)
7020 case STGTY_STORAGE:
7021 case STGTY_ROOT:
7022 destination->type = STGTY_STORAGE;
7023 break;
7024 case STGTY_STREAM:
7025 destination->type = STGTY_STREAM;
7026 break;
7027 default:
7028 destination->type = STGTY_STREAM;
7029 break;
7032 destination->cbSize = source->size;
7034 currentReturnStruct->mtime = {0}; TODO
7035 currentReturnStruct->ctime = {0};
7036 currentReturnStruct->atime = {0};
7038 destination->grfMode = 0;
7039 destination->grfLocksSupported = 0;
7040 destination->clsid = source->clsid;
7041 destination->grfStateBits = 0;
7042 destination->reserved = 0;
7046 /************************************************************************
7047 * BlockChainStream implementation
7048 ***********************************************************************/
7050 /******************************************************************************
7051 * BlockChainStream_GetHeadOfChain
7053 * Returns the head of this stream chain.
7054 * Some special chains don't have directory entries, their heads are kept in
7055 * This->headOfStreamPlaceHolder.
7058 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7060 DirEntry chainEntry;
7061 HRESULT hr;
7063 if (This->headOfStreamPlaceHolder != 0)
7064 return *(This->headOfStreamPlaceHolder);
7066 if (This->ownerDirEntry != DIRENTRY_NULL)
7068 hr = StorageImpl_ReadDirEntry(
7069 This->parentStorage,
7070 This->ownerDirEntry,
7071 &chainEntry);
7073 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7074 return chainEntry.startingBlock;
7077 return BLOCK_END_OF_CHAIN;
7080 /* Read and save the index of all blocks in this stream. */
7081 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7083 ULONG next_sector, next_offset;
7084 HRESULT hr;
7085 struct BlockChainRun *last_run;
7087 if (This->indexCacheLen == 0)
7089 last_run = NULL;
7090 next_offset = 0;
7091 next_sector = BlockChainStream_GetHeadOfChain(This);
7093 else
7095 last_run = &This->indexCache[This->indexCacheLen-1];
7096 next_offset = last_run->lastOffset+1;
7097 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7098 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7099 &next_sector);
7100 if (FAILED(hr)) return hr;
7103 while (next_sector != BLOCK_END_OF_CHAIN)
7105 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7107 /* Add the current block to the cache. */
7108 if (This->indexCacheSize == 0)
7110 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7111 if (!This->indexCache) return E_OUTOFMEMORY;
7112 This->indexCacheSize = 16;
7114 else if (This->indexCacheSize == This->indexCacheLen)
7116 struct BlockChainRun *new_cache;
7117 ULONG new_size;
7119 new_size = This->indexCacheSize * 2;
7120 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7121 if (!new_cache) return E_OUTOFMEMORY;
7122 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7124 HeapFree(GetProcessHeap(), 0, This->indexCache);
7125 This->indexCache = new_cache;
7126 This->indexCacheSize = new_size;
7129 This->indexCacheLen++;
7130 last_run = &This->indexCache[This->indexCacheLen-1];
7131 last_run->firstSector = next_sector;
7132 last_run->firstOffset = next_offset;
7135 last_run->lastOffset = next_offset;
7137 /* Find the next block. */
7138 next_offset++;
7139 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7140 if (FAILED(hr)) return hr;
7143 if (This->indexCacheLen)
7145 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7146 This->numBlocks = last_run->lastOffset+1;
7148 else
7150 This->tailIndex = BLOCK_END_OF_CHAIN;
7151 This->numBlocks = 0;
7154 return S_OK;
7157 /* Locate the nth block in this stream. */
7158 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7160 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7161 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7163 if (offset >= This->numBlocks)
7164 return BLOCK_END_OF_CHAIN;
7166 while (min_run < max_run)
7168 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7169 if (offset < This->indexCache[run_to_check].firstOffset)
7171 max_offset = This->indexCache[run_to_check].firstOffset-1;
7172 max_run = run_to_check-1;
7174 else if (offset > This->indexCache[run_to_check].lastOffset)
7176 min_offset = This->indexCache[run_to_check].lastOffset+1;
7177 min_run = run_to_check+1;
7179 else
7180 /* Block is in this run. */
7181 min_run = max_run = run_to_check;
7184 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7187 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7188 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7190 BlockChainBlock *result=NULL;
7191 int i;
7193 for (i=0; i<2; i++)
7194 if (This->cachedBlocks[i].index == index)
7196 *sector = This->cachedBlocks[i].sector;
7197 *block = &This->cachedBlocks[i];
7198 return S_OK;
7201 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7202 if (*sector == BLOCK_END_OF_CHAIN)
7203 return STG_E_DOCFILECORRUPT;
7205 if (create)
7207 if (This->cachedBlocks[0].index == 0xffffffff)
7208 result = &This->cachedBlocks[0];
7209 else if (This->cachedBlocks[1].index == 0xffffffff)
7210 result = &This->cachedBlocks[1];
7211 else
7213 result = &This->cachedBlocks[This->blockToEvict++];
7214 if (This->blockToEvict == 2)
7215 This->blockToEvict = 0;
7218 if (result->dirty)
7220 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7221 return STG_E_WRITEFAULT;
7222 result->dirty = FALSE;
7225 result->read = FALSE;
7226 result->index = index;
7227 result->sector = *sector;
7230 *block = result;
7231 return S_OK;
7234 BlockChainStream* BlockChainStream_Construct(
7235 StorageImpl* parentStorage,
7236 ULONG* headOfStreamPlaceHolder,
7237 DirRef dirEntry)
7239 BlockChainStream* newStream;
7241 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7242 if(!newStream)
7243 return NULL;
7245 newStream->parentStorage = parentStorage;
7246 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7247 newStream->ownerDirEntry = dirEntry;
7248 newStream->indexCache = NULL;
7249 newStream->indexCacheLen = 0;
7250 newStream->indexCacheSize = 0;
7251 newStream->cachedBlocks[0].index = 0xffffffff;
7252 newStream->cachedBlocks[0].dirty = FALSE;
7253 newStream->cachedBlocks[1].index = 0xffffffff;
7254 newStream->cachedBlocks[1].dirty = FALSE;
7255 newStream->blockToEvict = 0;
7257 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7259 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7260 HeapFree(GetProcessHeap(), 0, newStream);
7261 return NULL;
7264 return newStream;
7267 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7269 int i;
7270 if (!This) return S_OK;
7271 for (i=0; i<2; i++)
7273 if (This->cachedBlocks[i].dirty)
7275 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7276 This->cachedBlocks[i].dirty = FALSE;
7277 else
7278 return STG_E_WRITEFAULT;
7281 return S_OK;
7284 void BlockChainStream_Destroy(BlockChainStream* This)
7286 if (This)
7288 BlockChainStream_Flush(This);
7289 HeapFree(GetProcessHeap(), 0, This->indexCache);
7291 HeapFree(GetProcessHeap(), 0, This);
7294 /******************************************************************************
7295 * BlockChainStream_Shrink
7297 * Shrinks this chain in the big block depot.
7299 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7300 ULARGE_INTEGER newSize)
7302 ULONG blockIndex;
7303 ULONG numBlocks;
7304 int i;
7307 * Figure out how many blocks are needed to contain the new size
7309 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7311 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7312 numBlocks++;
7314 if (numBlocks)
7317 * Go to the new end of chain
7319 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7321 /* Mark the new end of chain */
7322 StorageImpl_SetNextBlockInChain(
7323 This->parentStorage,
7324 blockIndex,
7325 BLOCK_END_OF_CHAIN);
7327 This->tailIndex = blockIndex;
7329 else
7331 if (This->headOfStreamPlaceHolder != 0)
7333 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7335 else
7337 DirEntry chainEntry;
7338 assert(This->ownerDirEntry != DIRENTRY_NULL);
7340 StorageImpl_ReadDirEntry(
7341 This->parentStorage,
7342 This->ownerDirEntry,
7343 &chainEntry);
7345 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7347 StorageImpl_WriteDirEntry(
7348 This->parentStorage,
7349 This->ownerDirEntry,
7350 &chainEntry);
7353 This->tailIndex = BLOCK_END_OF_CHAIN;
7356 This->numBlocks = numBlocks;
7359 * Mark the extra blocks as free
7361 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7363 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7364 StorageImpl_FreeBigBlock(This->parentStorage,
7365 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7366 if (last_run->lastOffset == last_run->firstOffset)
7367 This->indexCacheLen--;
7368 else
7369 last_run->lastOffset--;
7373 * Reset the last accessed block cache.
7375 for (i=0; i<2; i++)
7377 if (This->cachedBlocks[i].index >= numBlocks)
7379 This->cachedBlocks[i].index = 0xffffffff;
7380 This->cachedBlocks[i].dirty = FALSE;
7384 return TRUE;
7387 /******************************************************************************
7388 * BlockChainStream_Enlarge
7390 * Grows this chain in the big block depot.
7392 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7393 ULARGE_INTEGER newSize)
7395 ULONG blockIndex, currentBlock;
7396 ULONG newNumBlocks;
7397 ULONG oldNumBlocks = 0;
7399 blockIndex = BlockChainStream_GetHeadOfChain(This);
7402 * Empty chain. Create the head.
7404 if (blockIndex == BLOCK_END_OF_CHAIN)
7406 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7407 StorageImpl_SetNextBlockInChain(This->parentStorage,
7408 blockIndex,
7409 BLOCK_END_OF_CHAIN);
7411 if (This->headOfStreamPlaceHolder != 0)
7413 *(This->headOfStreamPlaceHolder) = blockIndex;
7415 else
7417 DirEntry chainEntry;
7418 assert(This->ownerDirEntry != DIRENTRY_NULL);
7420 StorageImpl_ReadDirEntry(
7421 This->parentStorage,
7422 This->ownerDirEntry,
7423 &chainEntry);
7425 chainEntry.startingBlock = blockIndex;
7427 StorageImpl_WriteDirEntry(
7428 This->parentStorage,
7429 This->ownerDirEntry,
7430 &chainEntry);
7433 This->tailIndex = blockIndex;
7434 This->numBlocks = 1;
7438 * Figure out how many blocks are needed to contain this stream
7440 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7442 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7443 newNumBlocks++;
7446 * Go to the current end of chain
7448 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7450 currentBlock = blockIndex;
7452 while (blockIndex != BLOCK_END_OF_CHAIN)
7454 This->numBlocks++;
7455 currentBlock = blockIndex;
7457 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7458 &blockIndex)))
7459 return FALSE;
7462 This->tailIndex = currentBlock;
7465 currentBlock = This->tailIndex;
7466 oldNumBlocks = This->numBlocks;
7469 * Add new blocks to the chain
7471 if (oldNumBlocks < newNumBlocks)
7473 while (oldNumBlocks < newNumBlocks)
7475 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7477 StorageImpl_SetNextBlockInChain(
7478 This->parentStorage,
7479 currentBlock,
7480 blockIndex);
7482 StorageImpl_SetNextBlockInChain(
7483 This->parentStorage,
7484 blockIndex,
7485 BLOCK_END_OF_CHAIN);
7487 currentBlock = blockIndex;
7488 oldNumBlocks++;
7491 This->tailIndex = blockIndex;
7492 This->numBlocks = newNumBlocks;
7495 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7496 return FALSE;
7498 return TRUE;
7502 /******************************************************************************
7503 * BlockChainStream_GetSize
7505 * Returns the size of this chain.
7506 * Will return the block count if this chain doesn't have a directory entry.
7508 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7510 DirEntry chainEntry;
7512 if(This->headOfStreamPlaceHolder == NULL)
7515 * This chain has a directory entry so use the size value from there.
7517 StorageImpl_ReadDirEntry(
7518 This->parentStorage,
7519 This->ownerDirEntry,
7520 &chainEntry);
7522 return chainEntry.size;
7524 else
7527 * this chain is a chain that does not have a directory entry, figure out the
7528 * size by making the product number of used blocks times the
7529 * size of them
7531 ULARGE_INTEGER result;
7532 result.QuadPart =
7533 (ULONGLONG)BlockChainStream_GetCount(This) *
7534 This->parentStorage->bigBlockSize;
7536 return result;
7540 /******************************************************************************
7541 * BlockChainStream_SetSize
7543 * Sets the size of this stream. The big block depot will be updated.
7544 * The file will grow if we grow the chain.
7546 * TODO: Free the actual blocks in the file when we shrink the chain.
7547 * Currently, the blocks are still in the file. So the file size
7548 * doesn't shrink even if we shrink streams.
7550 BOOL BlockChainStream_SetSize(
7551 BlockChainStream* This,
7552 ULARGE_INTEGER newSize)
7554 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7556 if (newSize.QuadPart == size.QuadPart)
7557 return TRUE;
7559 if (newSize.QuadPart < size.QuadPart)
7561 BlockChainStream_Shrink(This, newSize);
7563 else
7565 BlockChainStream_Enlarge(This, newSize);
7568 return TRUE;
7571 /******************************************************************************
7572 * BlockChainStream_ReadAt
7574 * Reads a specified number of bytes from this chain at the specified offset.
7575 * bytesRead may be NULL.
7576 * Failure will be returned if the specified number of bytes has not been read.
7578 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7579 ULARGE_INTEGER offset,
7580 ULONG size,
7581 void* buffer,
7582 ULONG* bytesRead)
7584 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7585 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7586 ULONG bytesToReadInBuffer;
7587 ULONG blockIndex;
7588 BYTE* bufferWalker;
7589 ULARGE_INTEGER stream_size;
7590 HRESULT hr;
7591 BlockChainBlock *cachedBlock;
7593 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7596 * Find the first block in the stream that contains part of the buffer.
7598 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7600 *bytesRead = 0;
7602 stream_size = BlockChainStream_GetSize(This);
7603 if (stream_size.QuadPart > offset.QuadPart)
7604 size = min(stream_size.QuadPart - offset.QuadPart, size);
7605 else
7606 return S_OK;
7609 * Start reading the buffer.
7611 bufferWalker = buffer;
7613 while (size > 0)
7615 ULARGE_INTEGER ulOffset;
7616 DWORD bytesReadAt;
7619 * Calculate how many bytes we can copy from this big block.
7621 bytesToReadInBuffer =
7622 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7624 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7626 if (FAILED(hr))
7627 return hr;
7629 if (!cachedBlock)
7631 /* Not in cache, and we're going to read past the end of the block. */
7632 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7633 offsetInBlock;
7635 StorageImpl_ReadAt(This->parentStorage,
7636 ulOffset,
7637 bufferWalker,
7638 bytesToReadInBuffer,
7639 &bytesReadAt);
7641 else
7643 if (!cachedBlock->read)
7645 ULONG read;
7646 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7647 return STG_E_READFAULT;
7649 cachedBlock->read = TRUE;
7652 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7653 bytesReadAt = bytesToReadInBuffer;
7656 blockNoInSequence++;
7657 bufferWalker += bytesReadAt;
7658 size -= bytesReadAt;
7659 *bytesRead += bytesReadAt;
7660 offsetInBlock = 0; /* There is no offset on the next block */
7662 if (bytesToReadInBuffer != bytesReadAt)
7663 break;
7666 return S_OK;
7669 /******************************************************************************
7670 * BlockChainStream_WriteAt
7672 * Writes the specified number of bytes to this chain at the specified offset.
7673 * Will fail if not all specified number of bytes have been written.
7675 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7676 ULARGE_INTEGER offset,
7677 ULONG size,
7678 const void* buffer,
7679 ULONG* bytesWritten)
7681 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7682 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7683 ULONG bytesToWrite;
7684 ULONG blockIndex;
7685 const BYTE* bufferWalker;
7686 HRESULT hr;
7687 BlockChainBlock *cachedBlock;
7689 *bytesWritten = 0;
7690 bufferWalker = buffer;
7692 while (size > 0)
7694 ULARGE_INTEGER ulOffset;
7695 DWORD bytesWrittenAt;
7698 * Calculate how many bytes we can copy to this big block.
7700 bytesToWrite =
7701 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7703 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7705 /* BlockChainStream_SetSize should have already been called to ensure we have
7706 * enough blocks in the chain to write into */
7707 if (FAILED(hr))
7709 ERR("not enough blocks in chain to write data\n");
7710 return hr;
7713 if (!cachedBlock)
7715 /* Not in cache, and we're going to write past the end of the block. */
7716 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7717 offsetInBlock;
7719 StorageImpl_WriteAt(This->parentStorage,
7720 ulOffset,
7721 bufferWalker,
7722 bytesToWrite,
7723 &bytesWrittenAt);
7725 else
7727 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7729 ULONG read;
7730 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7731 return STG_E_READFAULT;
7734 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7735 bytesWrittenAt = bytesToWrite;
7736 cachedBlock->read = TRUE;
7737 cachedBlock->dirty = TRUE;
7740 blockNoInSequence++;
7741 bufferWalker += bytesWrittenAt;
7742 size -= bytesWrittenAt;
7743 *bytesWritten += bytesWrittenAt;
7744 offsetInBlock = 0; /* There is no offset on the next block */
7746 if (bytesWrittenAt != bytesToWrite)
7747 break;
7750 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7754 /************************************************************************
7755 * SmallBlockChainStream implementation
7756 ***********************************************************************/
7758 SmallBlockChainStream* SmallBlockChainStream_Construct(
7759 StorageImpl* parentStorage,
7760 ULONG* headOfStreamPlaceHolder,
7761 DirRef dirEntry)
7763 SmallBlockChainStream* newStream;
7765 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7767 newStream->parentStorage = parentStorage;
7768 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7769 newStream->ownerDirEntry = dirEntry;
7771 return newStream;
7774 void SmallBlockChainStream_Destroy(
7775 SmallBlockChainStream* This)
7777 HeapFree(GetProcessHeap(), 0, This);
7780 /******************************************************************************
7781 * SmallBlockChainStream_GetHeadOfChain
7783 * Returns the head of this chain of small blocks.
7785 static ULONG SmallBlockChainStream_GetHeadOfChain(
7786 SmallBlockChainStream* This)
7788 DirEntry chainEntry;
7789 HRESULT hr;
7791 if (This->headOfStreamPlaceHolder != NULL)
7792 return *(This->headOfStreamPlaceHolder);
7794 if (This->ownerDirEntry)
7796 hr = StorageImpl_ReadDirEntry(
7797 This->parentStorage,
7798 This->ownerDirEntry,
7799 &chainEntry);
7801 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7802 return chainEntry.startingBlock;
7805 return BLOCK_END_OF_CHAIN;
7808 /******************************************************************************
7809 * SmallBlockChainStream_GetNextBlockInChain
7811 * Returns the index of the next small block in this chain.
7813 * Return Values:
7814 * - BLOCK_END_OF_CHAIN: end of this chain
7815 * - BLOCK_UNUSED: small block 'blockIndex' is free
7817 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7818 SmallBlockChainStream* This,
7819 ULONG blockIndex,
7820 ULONG* nextBlockInChain)
7822 ULARGE_INTEGER offsetOfBlockInDepot;
7823 DWORD buffer;
7824 ULONG bytesRead;
7825 HRESULT res;
7827 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7829 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7832 * Read those bytes in the buffer from the small block file.
7834 res = BlockChainStream_ReadAt(
7835 This->parentStorage->smallBlockDepotChain,
7836 offsetOfBlockInDepot,
7837 sizeof(DWORD),
7838 &buffer,
7839 &bytesRead);
7841 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7842 res = STG_E_READFAULT;
7844 if (SUCCEEDED(res))
7846 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7847 return S_OK;
7850 return res;
7853 /******************************************************************************
7854 * SmallBlockChainStream_SetNextBlockInChain
7856 * Writes the index of the next block of the specified block in the small
7857 * block depot.
7858 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7859 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7861 static void SmallBlockChainStream_SetNextBlockInChain(
7862 SmallBlockChainStream* This,
7863 ULONG blockIndex,
7864 ULONG nextBlock)
7866 ULARGE_INTEGER offsetOfBlockInDepot;
7867 DWORD buffer;
7868 ULONG bytesWritten;
7870 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7872 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
7875 * Read those bytes in the buffer from the small block file.
7877 BlockChainStream_WriteAt(
7878 This->parentStorage->smallBlockDepotChain,
7879 offsetOfBlockInDepot,
7880 sizeof(DWORD),
7881 &buffer,
7882 &bytesWritten);
7885 /******************************************************************************
7886 * SmallBlockChainStream_FreeBlock
7888 * Flag small block 'blockIndex' as free in the small block depot.
7890 static void SmallBlockChainStream_FreeBlock(
7891 SmallBlockChainStream* This,
7892 ULONG blockIndex)
7894 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7897 /******************************************************************************
7898 * SmallBlockChainStream_GetNextFreeBlock
7900 * Returns the index of a free small block. The small block depot will be
7901 * enlarged if necessary. The small block chain will also be enlarged if
7902 * necessary.
7904 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7905 SmallBlockChainStream* This)
7907 ULARGE_INTEGER offsetOfBlockInDepot;
7908 DWORD buffer;
7909 ULONG bytesRead;
7910 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7911 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7912 HRESULT res = S_OK;
7913 ULONG smallBlocksPerBigBlock;
7914 DirEntry rootEntry;
7915 ULONG blocksRequired;
7916 ULARGE_INTEGER old_size, size_required;
7918 offsetOfBlockInDepot.u.HighPart = 0;
7921 * Scan the small block depot for a free block
7923 while (nextBlockIndex != BLOCK_UNUSED)
7925 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7927 res = BlockChainStream_ReadAt(
7928 This->parentStorage->smallBlockDepotChain,
7929 offsetOfBlockInDepot,
7930 sizeof(DWORD),
7931 &buffer,
7932 &bytesRead);
7935 * If we run out of space for the small block depot, enlarge it
7937 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7939 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7941 if (nextBlockIndex != BLOCK_UNUSED)
7942 blockIndex++;
7944 else
7946 ULONG count =
7947 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7949 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7950 ULARGE_INTEGER newSize, offset;
7951 ULONG bytesWritten;
7953 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7954 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7957 * Initialize all the small blocks to free
7959 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7960 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7961 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7962 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7964 StorageImpl_SaveFileHeader(This->parentStorage);
7968 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7970 smallBlocksPerBigBlock =
7971 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7974 * Verify if we have to allocate big blocks to contain small blocks
7976 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7978 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7980 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7982 if (size_required.QuadPart > old_size.QuadPart)
7984 BlockChainStream_SetSize(
7985 This->parentStorage->smallBlockRootChain,
7986 size_required);
7988 StorageImpl_ReadDirEntry(
7989 This->parentStorage,
7990 This->parentStorage->base.storageDirEntry,
7991 &rootEntry);
7993 rootEntry.size = size_required;
7995 StorageImpl_WriteDirEntry(
7996 This->parentStorage,
7997 This->parentStorage->base.storageDirEntry,
7998 &rootEntry);
8001 return blockIndex;
8004 /******************************************************************************
8005 * SmallBlockChainStream_ReadAt
8007 * Reads a specified number of bytes from this chain at the specified offset.
8008 * bytesRead may be NULL.
8009 * Failure will be returned if the specified number of bytes has not been read.
8011 HRESULT SmallBlockChainStream_ReadAt(
8012 SmallBlockChainStream* This,
8013 ULARGE_INTEGER offset,
8014 ULONG size,
8015 void* buffer,
8016 ULONG* bytesRead)
8018 HRESULT rc = S_OK;
8019 ULARGE_INTEGER offsetInBigBlockFile;
8020 ULONG blockNoInSequence =
8021 offset.u.LowPart / This->parentStorage->smallBlockSize;
8023 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8024 ULONG bytesToReadInBuffer;
8025 ULONG blockIndex;
8026 ULONG bytesReadFromBigBlockFile;
8027 BYTE* bufferWalker;
8028 ULARGE_INTEGER stream_size;
8031 * This should never happen on a small block file.
8033 assert(offset.u.HighPart==0);
8035 *bytesRead = 0;
8037 stream_size = SmallBlockChainStream_GetSize(This);
8038 if (stream_size.QuadPart > offset.QuadPart)
8039 size = min(stream_size.QuadPart - offset.QuadPart, size);
8040 else
8041 return S_OK;
8044 * Find the first block in the stream that contains part of the buffer.
8046 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8048 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8050 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8051 if(FAILED(rc))
8052 return rc;
8053 blockNoInSequence--;
8057 * Start reading the buffer.
8059 bufferWalker = buffer;
8061 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8064 * Calculate how many bytes we can copy from this small block.
8066 bytesToReadInBuffer =
8067 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8070 * Calculate the offset of the small block in the small block file.
8072 offsetInBigBlockFile.QuadPart =
8073 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8075 offsetInBigBlockFile.QuadPart += offsetInBlock;
8078 * Read those bytes in the buffer from the small block file.
8079 * The small block has already been identified so it shouldn't fail
8080 * unless the file is corrupt.
8082 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8083 offsetInBigBlockFile,
8084 bytesToReadInBuffer,
8085 bufferWalker,
8086 &bytesReadFromBigBlockFile);
8088 if (FAILED(rc))
8089 return rc;
8091 if (!bytesReadFromBigBlockFile)
8092 return STG_E_DOCFILECORRUPT;
8095 * Step to the next big block.
8097 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8098 if(FAILED(rc))
8099 return STG_E_DOCFILECORRUPT;
8101 bufferWalker += bytesReadFromBigBlockFile;
8102 size -= bytesReadFromBigBlockFile;
8103 *bytesRead += bytesReadFromBigBlockFile;
8104 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8107 return S_OK;
8110 /******************************************************************************
8111 * SmallBlockChainStream_WriteAt
8113 * Writes the specified number of bytes to this chain at the specified offset.
8114 * Will fail if not all specified number of bytes have been written.
8116 HRESULT SmallBlockChainStream_WriteAt(
8117 SmallBlockChainStream* This,
8118 ULARGE_INTEGER offset,
8119 ULONG size,
8120 const void* buffer,
8121 ULONG* bytesWritten)
8123 ULARGE_INTEGER offsetInBigBlockFile;
8124 ULONG blockNoInSequence =
8125 offset.u.LowPart / This->parentStorage->smallBlockSize;
8127 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8128 ULONG bytesToWriteInBuffer;
8129 ULONG blockIndex;
8130 ULONG bytesWrittenToBigBlockFile;
8131 const BYTE* bufferWalker;
8132 HRESULT res;
8135 * This should never happen on a small block file.
8137 assert(offset.u.HighPart==0);
8140 * Find the first block in the stream that contains part of the buffer.
8142 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8144 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8146 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8147 return STG_E_DOCFILECORRUPT;
8148 blockNoInSequence--;
8152 * Start writing the buffer.
8154 *bytesWritten = 0;
8155 bufferWalker = buffer;
8156 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8159 * Calculate how many bytes we can copy to this small block.
8161 bytesToWriteInBuffer =
8162 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8165 * Calculate the offset of the small block in the small block file.
8167 offsetInBigBlockFile.QuadPart =
8168 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8170 offsetInBigBlockFile.QuadPart += offsetInBlock;
8173 * Write those bytes in the buffer to the small block file.
8175 res = BlockChainStream_WriteAt(
8176 This->parentStorage->smallBlockRootChain,
8177 offsetInBigBlockFile,
8178 bytesToWriteInBuffer,
8179 bufferWalker,
8180 &bytesWrittenToBigBlockFile);
8181 if (FAILED(res))
8182 return res;
8185 * Step to the next big block.
8187 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8188 if (FAILED(res))
8189 return res;
8190 bufferWalker += bytesWrittenToBigBlockFile;
8191 size -= bytesWrittenToBigBlockFile;
8192 *bytesWritten += bytesWrittenToBigBlockFile;
8193 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8196 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8199 /******************************************************************************
8200 * SmallBlockChainStream_Shrink
8202 * Shrinks this chain in the small block depot.
8204 static BOOL SmallBlockChainStream_Shrink(
8205 SmallBlockChainStream* This,
8206 ULARGE_INTEGER newSize)
8208 ULONG blockIndex, extraBlock;
8209 ULONG numBlocks;
8210 ULONG count = 0;
8212 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8214 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8215 numBlocks++;
8217 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8220 * Go to the new end of chain
8222 while (count < numBlocks)
8224 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8225 &blockIndex)))
8226 return FALSE;
8227 count++;
8231 * If the count is 0, we have a special case, the head of the chain was
8232 * just freed.
8234 if (count == 0)
8236 DirEntry chainEntry;
8238 StorageImpl_ReadDirEntry(This->parentStorage,
8239 This->ownerDirEntry,
8240 &chainEntry);
8242 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8244 StorageImpl_WriteDirEntry(This->parentStorage,
8245 This->ownerDirEntry,
8246 &chainEntry);
8249 * We start freeing the chain at the head block.
8251 extraBlock = blockIndex;
8253 else
8255 /* Get the next block before marking the new end */
8256 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8257 &extraBlock)))
8258 return FALSE;
8260 /* Mark the new end of chain */
8261 SmallBlockChainStream_SetNextBlockInChain(
8262 This,
8263 blockIndex,
8264 BLOCK_END_OF_CHAIN);
8268 * Mark the extra blocks as free
8270 while (extraBlock != BLOCK_END_OF_CHAIN)
8272 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8273 &blockIndex)))
8274 return FALSE;
8275 SmallBlockChainStream_FreeBlock(This, extraBlock);
8276 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8277 extraBlock = blockIndex;
8280 return TRUE;
8283 /******************************************************************************
8284 * SmallBlockChainStream_Enlarge
8286 * Grows this chain in the small block depot.
8288 static BOOL SmallBlockChainStream_Enlarge(
8289 SmallBlockChainStream* This,
8290 ULARGE_INTEGER newSize)
8292 ULONG blockIndex, currentBlock;
8293 ULONG newNumBlocks;
8294 ULONG oldNumBlocks = 0;
8296 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8299 * Empty chain. Create the head.
8301 if (blockIndex == BLOCK_END_OF_CHAIN)
8303 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8304 SmallBlockChainStream_SetNextBlockInChain(
8305 This,
8306 blockIndex,
8307 BLOCK_END_OF_CHAIN);
8309 if (This->headOfStreamPlaceHolder != NULL)
8311 *(This->headOfStreamPlaceHolder) = blockIndex;
8313 else
8315 DirEntry chainEntry;
8317 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8318 &chainEntry);
8320 chainEntry.startingBlock = blockIndex;
8322 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8323 &chainEntry);
8327 currentBlock = blockIndex;
8330 * Figure out how many blocks are needed to contain this stream
8332 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8334 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8335 newNumBlocks++;
8338 * Go to the current end of chain
8340 while (blockIndex != BLOCK_END_OF_CHAIN)
8342 oldNumBlocks++;
8343 currentBlock = blockIndex;
8344 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8345 return FALSE;
8349 * Add new blocks to the chain
8351 while (oldNumBlocks < newNumBlocks)
8353 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8354 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8356 SmallBlockChainStream_SetNextBlockInChain(
8357 This,
8358 blockIndex,
8359 BLOCK_END_OF_CHAIN);
8361 currentBlock = blockIndex;
8362 oldNumBlocks++;
8365 return TRUE;
8368 /******************************************************************************
8369 * SmallBlockChainStream_SetSize
8371 * Sets the size of this stream.
8372 * The file will grow if we grow the chain.
8374 * TODO: Free the actual blocks in the file when we shrink the chain.
8375 * Currently, the blocks are still in the file. So the file size
8376 * doesn't shrink even if we shrink streams.
8378 BOOL SmallBlockChainStream_SetSize(
8379 SmallBlockChainStream* This,
8380 ULARGE_INTEGER newSize)
8382 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8384 if (newSize.u.LowPart == size.u.LowPart)
8385 return TRUE;
8387 if (newSize.u.LowPart < size.u.LowPart)
8389 SmallBlockChainStream_Shrink(This, newSize);
8391 else
8393 SmallBlockChainStream_Enlarge(This, newSize);
8396 return TRUE;
8399 /******************************************************************************
8400 * SmallBlockChainStream_GetCount
8402 * Returns the number of small blocks that comprises this chain.
8403 * This is not the size of the stream as the last block may not be full!
8406 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8408 ULONG blockIndex;
8409 ULONG count = 0;
8411 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8413 while(blockIndex != BLOCK_END_OF_CHAIN)
8415 count++;
8417 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8418 blockIndex, &blockIndex)))
8419 return 0;
8422 return count;
8425 /******************************************************************************
8426 * SmallBlockChainStream_GetSize
8428 * Returns the size of this chain.
8430 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8432 DirEntry chainEntry;
8434 if(This->headOfStreamPlaceHolder != NULL)
8436 ULARGE_INTEGER result;
8437 result.u.HighPart = 0;
8439 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8440 This->parentStorage->smallBlockSize;
8442 return result;
8445 StorageImpl_ReadDirEntry(
8446 This->parentStorage,
8447 This->ownerDirEntry,
8448 &chainEntry);
8450 return chainEntry.size;
8454 /************************************************************************
8455 * Miscellaneous storage functions
8456 ***********************************************************************/
8458 static HRESULT create_storagefile(
8459 LPCOLESTR pwcsName,
8460 DWORD grfMode,
8461 DWORD grfAttrs,
8462 STGOPTIONS* pStgOptions,
8463 REFIID riid,
8464 void** ppstgOpen)
8466 StorageBaseImpl* newStorage = 0;
8467 HANDLE hFile = INVALID_HANDLE_VALUE;
8468 HRESULT hr = STG_E_INVALIDFLAG;
8469 DWORD shareMode;
8470 DWORD accessMode;
8471 DWORD creationMode;
8472 DWORD fileAttributes;
8473 WCHAR tempFileName[MAX_PATH];
8475 if (ppstgOpen == 0)
8476 return STG_E_INVALIDPOINTER;
8478 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8479 return STG_E_INVALIDPARAMETER;
8481 /* if no share mode given then DENY_NONE is the default */
8482 if (STGM_SHARE_MODE(grfMode) == 0)
8483 grfMode |= STGM_SHARE_DENY_NONE;
8485 if ( FAILED( validateSTGM(grfMode) ))
8486 goto end;
8488 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8489 switch(STGM_ACCESS_MODE(grfMode))
8491 case STGM_WRITE:
8492 case STGM_READWRITE:
8493 break;
8494 default:
8495 goto end;
8498 /* in direct mode, can only use SHARE_EXCLUSIVE */
8499 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8500 goto end;
8502 /* but in transacted mode, any share mode is valid */
8505 * Generate a unique name.
8507 if (pwcsName == 0)
8509 WCHAR tempPath[MAX_PATH];
8511 memset(tempPath, 0, sizeof(tempPath));
8512 memset(tempFileName, 0, sizeof(tempFileName));
8514 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8515 tempPath[0] = '.';
8517 if (GetTempFileNameW(tempPath, L"STO", 0, tempFileName) != 0)
8518 pwcsName = tempFileName;
8519 else
8521 hr = STG_E_INSUFFICIENTMEMORY;
8522 goto end;
8525 creationMode = TRUNCATE_EXISTING;
8527 else
8529 creationMode = GetCreationModeFromSTGM(grfMode);
8533 * Interpret the STGM value grfMode
8535 shareMode = GetShareModeFromSTGM(grfMode);
8536 accessMode = GetAccessModeFromSTGM(grfMode);
8538 if (grfMode & STGM_DELETEONRELEASE)
8539 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8540 else
8541 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8543 *ppstgOpen = 0;
8545 hFile = CreateFileW(pwcsName,
8546 accessMode,
8547 shareMode,
8548 NULL,
8549 creationMode,
8550 fileAttributes,
8553 if (hFile == INVALID_HANDLE_VALUE)
8555 if(GetLastError() == ERROR_FILE_EXISTS)
8556 hr = STG_E_FILEALREADYEXISTS;
8557 else
8558 hr = E_FAIL;
8559 goto end;
8563 * Allocate and initialize the new IStorage object.
8565 hr = Storage_Construct(
8566 hFile,
8567 pwcsName,
8568 NULL,
8569 grfMode,
8570 TRUE,
8571 TRUE,
8572 pStgOptions->ulSectorSize,
8573 &newStorage);
8575 if (FAILED(hr))
8577 goto end;
8580 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8581 IStorage_Release(&newStorage->IStorage_iface);
8583 end:
8584 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8586 return hr;
8589 /******************************************************************************
8590 * StgCreateDocfile [OLE32.@]
8591 * Creates a new compound file storage object
8593 * PARAMS
8594 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8595 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8596 * reserved [ ?] unused?, usually 0
8597 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8599 * RETURNS
8600 * S_OK if the file was successfully created
8601 * some STG_E_ value if error
8602 * NOTES
8603 * if pwcsName is NULL, create file with new unique name
8604 * the function can returns
8605 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8606 * (unrealized now)
8608 HRESULT WINAPI StgCreateDocfile(
8609 LPCOLESTR pwcsName,
8610 DWORD grfMode,
8611 DWORD reserved,
8612 IStorage **ppstgOpen)
8614 STGOPTIONS stgoptions = {1, 0, 512};
8616 TRACE("(%s, %x, %d, %p)\n",
8617 debugstr_w(pwcsName), grfMode,
8618 reserved, ppstgOpen);
8620 if (ppstgOpen == 0)
8621 return STG_E_INVALIDPOINTER;
8622 if (reserved != 0)
8623 return STG_E_INVALIDPARAMETER;
8625 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8628 /******************************************************************************
8629 * StgCreateStorageEx [OLE32.@]
8631 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8633 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8634 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8636 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8638 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8639 return STG_E_INVALIDPARAMETER;
8642 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8644 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8645 return STG_E_INVALIDPARAMETER;
8648 if (stgfmt == STGFMT_FILE)
8650 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8651 return STG_E_INVALIDPARAMETER;
8654 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8656 STGOPTIONS defaultOptions = {1, 0, 512};
8658 if (!pStgOptions) pStgOptions = &defaultOptions;
8659 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8663 ERR("Invalid stgfmt argument\n");
8664 return STG_E_INVALIDPARAMETER;
8667 /******************************************************************************
8668 * StgCreatePropSetStg [OLE32.@]
8670 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8671 IPropertySetStorage **propset)
8673 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8674 if (reserved)
8675 return STG_E_INVALIDPARAMETER;
8677 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8680 /******************************************************************************
8681 * StgOpenStorageEx [OLE32.@]
8683 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8685 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8686 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8688 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8690 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8691 return STG_E_INVALIDPARAMETER;
8694 switch (stgfmt)
8696 case STGFMT_FILE:
8697 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8698 return STG_E_INVALIDPARAMETER;
8700 case STGFMT_STORAGE:
8701 break;
8703 case STGFMT_DOCFILE:
8704 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8706 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8707 return STG_E_INVALIDPARAMETER;
8709 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8710 break;
8712 case STGFMT_ANY:
8713 WARN("STGFMT_ANY assuming storage\n");
8714 break;
8716 default:
8717 return STG_E_INVALIDPARAMETER;
8720 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8724 /******************************************************************************
8725 * StgOpenStorage [OLE32.@]
8727 HRESULT WINAPI StgOpenStorage(
8728 const OLECHAR *pwcsName,
8729 IStorage *pstgPriority,
8730 DWORD grfMode,
8731 SNB snbExclude,
8732 DWORD reserved,
8733 IStorage **ppstgOpen)
8735 StorageBaseImpl* newStorage = 0;
8736 HRESULT hr = S_OK;
8737 HANDLE hFile = 0;
8738 DWORD shareMode;
8739 DWORD accessMode;
8740 LPWSTR temp_name = NULL;
8742 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8743 debugstr_w(pwcsName), pstgPriority, grfMode,
8744 snbExclude, reserved, ppstgOpen);
8746 if (pstgPriority)
8748 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8749 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8750 if (FAILED(hr)) goto end;
8751 pwcsName = temp_name;
8752 TRACE("using filename %s\n", debugstr_w(temp_name));
8755 if (pwcsName == 0)
8757 hr = STG_E_INVALIDNAME;
8758 goto end;
8761 if (ppstgOpen == 0)
8763 hr = STG_E_INVALIDPOINTER;
8764 goto end;
8767 if (reserved)
8769 hr = STG_E_INVALIDPARAMETER;
8770 goto end;
8773 if (grfMode & STGM_PRIORITY)
8775 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8776 return STG_E_INVALIDFLAG;
8777 if (grfMode & STGM_DELETEONRELEASE)
8778 return STG_E_INVALIDFUNCTION;
8779 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8780 return STG_E_INVALIDFLAG;
8781 grfMode &= ~0xf0; /* remove the existing sharing mode */
8782 grfMode |= STGM_SHARE_DENY_NONE;
8786 * Validate the sharing mode
8788 if (grfMode & STGM_DIRECT_SWMR)
8790 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8791 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8793 hr = STG_E_INVALIDFLAG;
8794 goto end;
8797 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8798 switch(STGM_SHARE_MODE(grfMode))
8800 case STGM_SHARE_EXCLUSIVE:
8801 case STGM_SHARE_DENY_WRITE:
8802 break;
8803 default:
8804 hr = STG_E_INVALIDFLAG;
8805 goto end;
8808 if ( FAILED( validateSTGM(grfMode) ) ||
8809 (grfMode&STGM_CREATE))
8811 hr = STG_E_INVALIDFLAG;
8812 goto end;
8815 /* shared reading requires transacted or single writer mode */
8816 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8817 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8818 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8820 hr = STG_E_INVALIDFLAG;
8821 goto end;
8825 * Interpret the STGM value grfMode
8827 shareMode = GetShareModeFromSTGM(grfMode);
8828 accessMode = GetAccessModeFromSTGM(grfMode);
8830 *ppstgOpen = 0;
8832 hFile = CreateFileW( pwcsName,
8833 accessMode,
8834 shareMode,
8835 NULL,
8836 OPEN_EXISTING,
8837 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8840 if (hFile==INVALID_HANDLE_VALUE)
8842 DWORD last_error = GetLastError();
8844 hr = E_FAIL;
8846 switch (last_error)
8848 case ERROR_FILE_NOT_FOUND:
8849 hr = STG_E_FILENOTFOUND;
8850 break;
8852 case ERROR_PATH_NOT_FOUND:
8853 hr = STG_E_PATHNOTFOUND;
8854 break;
8856 case ERROR_ACCESS_DENIED:
8857 case ERROR_WRITE_PROTECT:
8858 hr = STG_E_ACCESSDENIED;
8859 break;
8861 case ERROR_SHARING_VIOLATION:
8862 hr = STG_E_SHAREVIOLATION;
8863 break;
8865 default:
8866 hr = E_FAIL;
8869 goto end;
8873 * Refuse to open the file if it's too small to be a structured storage file
8874 * FIXME: verify the file when reading instead of here
8876 if (GetFileSize(hFile, NULL) < HEADER_SIZE)
8878 CloseHandle(hFile);
8879 hr = STG_E_FILEALREADYEXISTS;
8880 goto end;
8884 * Allocate and initialize the new IStorage object.
8886 hr = Storage_Construct(
8887 hFile,
8888 pwcsName,
8889 NULL,
8890 grfMode,
8891 TRUE,
8892 FALSE,
8893 512,
8894 &newStorage);
8896 if (FAILED(hr))
8899 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8901 if(hr == STG_E_INVALIDHEADER)
8902 hr = STG_E_FILEALREADYEXISTS;
8903 goto end;
8906 *ppstgOpen = &newStorage->IStorage_iface;
8908 end:
8909 CoTaskMemFree(temp_name);
8910 if (pstgPriority) IStorage_Release(pstgPriority);
8911 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8912 return hr;
8915 /******************************************************************************
8916 * StgCreateDocfileOnILockBytes [OLE32.@]
8918 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8919 ILockBytes *plkbyt,
8920 DWORD grfMode,
8921 DWORD reserved,
8922 IStorage** ppstgOpen)
8924 StorageBaseImpl* newStorage = 0;
8925 HRESULT hr = S_OK;
8927 if ((ppstgOpen == 0) || (plkbyt == 0))
8928 return STG_E_INVALIDPOINTER;
8931 * Allocate and initialize the new IStorage object.
8933 hr = Storage_Construct(
8936 plkbyt,
8937 grfMode,
8938 FALSE,
8939 TRUE,
8940 512,
8941 &newStorage);
8943 if (FAILED(hr))
8945 return hr;
8948 *ppstgOpen = &newStorage->IStorage_iface;
8950 return hr;
8953 /******************************************************************************
8954 * StgOpenStorageOnILockBytes [OLE32.@]
8956 HRESULT WINAPI StgOpenStorageOnILockBytes(
8957 ILockBytes *plkbyt,
8958 IStorage *pstgPriority,
8959 DWORD grfMode,
8960 SNB snbExclude,
8961 DWORD reserved,
8962 IStorage **ppstgOpen)
8964 StorageBaseImpl* newStorage = 0;
8965 HRESULT hr = S_OK;
8967 if ((plkbyt == 0) || (ppstgOpen == 0))
8968 return STG_E_INVALIDPOINTER;
8970 if ( FAILED( validateSTGM(grfMode) ))
8971 return STG_E_INVALIDFLAG;
8973 *ppstgOpen = 0;
8976 * Allocate and initialize the new IStorage object.
8978 hr = Storage_Construct(
8981 plkbyt,
8982 grfMode,
8983 FALSE,
8984 FALSE,
8985 512,
8986 &newStorage);
8988 if (FAILED(hr))
8990 return hr;
8993 *ppstgOpen = &newStorage->IStorage_iface;
8995 return hr;
8998 /******************************************************************************
8999 * StgSetTimes [ole32.@]
9000 * StgSetTimes [OLE32.@]
9004 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
9005 FILETIME const *patime, FILETIME const *pmtime)
9007 IStorage *stg = NULL;
9008 HRESULT r;
9010 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
9012 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
9013 0, 0, &stg);
9014 if( SUCCEEDED(r) )
9016 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9017 IStorage_Release(stg);
9020 return r;
9023 /******************************************************************************
9024 * StgIsStorageILockBytes [OLE32.@]
9026 * Determines if the ILockBytes contains a storage object.
9028 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9030 BYTE sig[sizeof(STORAGE_magic)];
9031 ULARGE_INTEGER offset;
9032 ULONG read = 0;
9034 offset.u.HighPart = 0;
9035 offset.u.LowPart = 0;
9037 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9039 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9040 return S_OK;
9042 return S_FALSE;
9045 /******************************************************************************
9046 * WriteClassStg [OLE32.@]
9048 * This method will store the specified CLSID in the specified storage object
9050 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9052 if(!pStg)
9053 return E_INVALIDARG;
9055 if(!rclsid)
9056 return STG_E_INVALIDPOINTER;
9058 return IStorage_SetClass(pStg, rclsid);
9061 /***********************************************************************
9062 * ReadClassStg (OLE32.@)
9064 * This method reads the CLSID previously written to a storage object with
9065 * the WriteClassStg.
9067 * PARAMS
9068 * pstg [I] IStorage pointer
9069 * pclsid [O] Pointer to where the CLSID is written
9071 * RETURNS
9072 * Success: S_OK.
9073 * Failure: HRESULT code.
9075 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9077 STATSTG pstatstg;
9078 HRESULT hRes;
9080 TRACE("(%p, %p)\n", pstg, pclsid);
9082 if(!pstg || !pclsid)
9083 return E_INVALIDARG;
9086 * read a STATSTG structure (contains the clsid) from the storage
9088 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9090 if(SUCCEEDED(hRes))
9091 *pclsid=pstatstg.clsid;
9093 return hRes;
9096 /***********************************************************************
9097 * OleLoadFromStream (OLE32.@)
9099 * This function loads an object from stream
9101 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9103 CLSID clsid;
9104 HRESULT res;
9105 LPPERSISTSTREAM xstm;
9107 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9109 res=ReadClassStm(pStm,&clsid);
9110 if (FAILED(res))
9111 return res;
9112 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9113 if (FAILED(res))
9114 return res;
9115 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9116 if (FAILED(res)) {
9117 IUnknown_Release((IUnknown*)*ppvObj);
9118 return res;
9120 res=IPersistStream_Load(xstm,pStm);
9121 IPersistStream_Release(xstm);
9122 /* FIXME: all refcounts ok at this point? I think they should be:
9123 * pStm : unchanged
9124 * ppvObj : 1
9125 * xstm : 0 (released)
9127 return res;
9130 /***********************************************************************
9131 * OleSaveToStream (OLE32.@)
9133 * This function saves an object with the IPersistStream interface on it
9134 * to the specified stream.
9136 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9139 CLSID clsid;
9140 HRESULT res;
9142 TRACE("(%p,%p)\n",pPStm,pStm);
9144 res=IPersistStream_GetClassID(pPStm,&clsid);
9146 if (SUCCEEDED(res)){
9148 res=WriteClassStm(pStm,&clsid);
9150 if (SUCCEEDED(res))
9152 res=IPersistStream_Save(pPStm,pStm,TRUE);
9155 TRACE("Finished Save\n");
9156 return res;
9159 /*************************************************************************
9160 * STORAGE_CreateOleStream [Internal]
9162 * Creates the "\001OLE" stream in the IStorage if necessary.
9164 * PARAMS
9165 * storage [I] Dest storage to create the stream in
9166 * flags [I] flags to be set for newly created stream
9168 * RETURNS
9169 * HRESULT return value
9171 * NOTES
9173 * This stream is still unknown, MS Word seems to have extra data
9174 * but since the data is stored in the OLESTREAM there should be
9175 * no need to recreate the stream. If the stream is manually
9176 * deleted it will create it with this default data.
9179 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9181 static const DWORD version_magic = 0x02000001;
9182 IStream *stream;
9183 HRESULT hr;
9185 hr = IStorage_CreateStream(storage, L"\1Ole", STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9186 if (hr == S_OK)
9188 struct empty_1ole_stream {
9189 DWORD version_magic;
9190 DWORD flags;
9191 DWORD update_options;
9192 DWORD reserved;
9193 DWORD mon_stream_size;
9195 struct empty_1ole_stream stream_data;
9197 stream_data.version_magic = version_magic;
9198 stream_data.flags = flags;
9199 stream_data.update_options = 0;
9200 stream_data.reserved = 0;
9201 stream_data.mon_stream_size = 0;
9203 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9204 IStream_Release(stream);
9207 return hr;
9210 /* write a string to a stream, preceded by its length */
9211 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9213 HRESULT r;
9214 LPSTR str;
9215 DWORD len = 0;
9217 if( string )
9218 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9219 r = IStream_Write( stm, &len, sizeof(len), NULL);
9220 if( FAILED( r ) )
9221 return r;
9222 if(len == 0)
9223 return r;
9224 str = CoTaskMemAlloc( len );
9225 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9226 r = IStream_Write( stm, str, len, NULL);
9227 CoTaskMemFree( str );
9228 return r;
9231 /* read a string preceded by its length from a stream */
9232 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9234 HRESULT r;
9235 DWORD len, count = 0;
9236 LPSTR str;
9237 LPWSTR wstr;
9239 r = IStream_Read( stm, &len, sizeof(len), &count );
9240 if( FAILED( r ) )
9241 return r;
9242 if( count != sizeof(len) )
9243 return E_OUTOFMEMORY;
9245 TRACE("%d bytes\n",len);
9247 str = CoTaskMemAlloc( len );
9248 if( !str )
9249 return E_OUTOFMEMORY;
9250 count = 0;
9251 r = IStream_Read( stm, str, len, &count );
9252 if( FAILED( r ) )
9254 CoTaskMemFree( str );
9255 return r;
9257 if( count != len )
9259 CoTaskMemFree( str );
9260 return E_OUTOFMEMORY;
9263 TRACE("Read string %s\n",debugstr_an(str,len));
9265 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9266 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9267 if( wstr )
9269 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9270 wstr[len] = 0;
9272 CoTaskMemFree( str );
9274 *string = wstr;
9276 return r;
9280 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9281 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9283 IStream *pstm;
9284 HRESULT r = S_OK;
9286 static const BYTE unknown1[12] =
9287 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9288 0xFF, 0xFF, 0xFF, 0xFF};
9289 static const BYTE unknown2[16] =
9290 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9293 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9294 debugstr_w(lpszUserType), debugstr_w(szClipName),
9295 debugstr_w(szProgIDName));
9297 /* Create a CompObj stream */
9298 r = IStorage_CreateStream(pstg, L"\1CompObj",
9299 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9300 if( FAILED (r) )
9301 return r;
9303 /* Write CompObj Structure to stream */
9304 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9306 if( SUCCEEDED( r ) )
9307 r = WriteClassStm( pstm, clsid );
9309 if( SUCCEEDED( r ) )
9310 r = STREAM_WriteString( pstm, lpszUserType );
9311 if( SUCCEEDED( r ) )
9312 r = STREAM_WriteString( pstm, szClipName );
9313 if( SUCCEEDED( r ) )
9314 r = STREAM_WriteString( pstm, szProgIDName );
9315 if( SUCCEEDED( r ) )
9316 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9318 IStream_Release( pstm );
9320 return r;
9323 /***********************************************************************
9324 * WriteFmtUserTypeStg (OLE32.@)
9326 HRESULT WINAPI WriteFmtUserTypeStg(
9327 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9329 STATSTG stat;
9330 HRESULT r;
9331 WCHAR szwClipName[0x40];
9332 CLSID clsid;
9333 LPWSTR wstrProgID = NULL;
9334 DWORD n;
9336 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9338 /* get the clipboard format name */
9339 if( cf )
9341 n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName));
9342 szwClipName[n]=0;
9345 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9347 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9348 if(SUCCEEDED(r))
9349 clsid = stat.clsid;
9350 else
9351 clsid = CLSID_NULL;
9353 ProgIDFromCLSID(&clsid, &wstrProgID);
9355 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9357 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9358 cf ? szwClipName : NULL, wstrProgID );
9360 CoTaskMemFree(wstrProgID);
9362 return r;
9366 /******************************************************************************
9367 * ReadFmtUserTypeStg [OLE32.@]
9369 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9371 HRESULT r;
9372 IStream *stm = 0;
9373 unsigned char unknown1[12];
9374 unsigned char unknown2[16];
9375 DWORD count;
9376 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9377 CLSID clsid;
9379 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9381 r = IStorage_OpenStream( pstg, L"\1CompObj", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9382 if( FAILED ( r ) )
9384 WARN("Failed to open stream r = %08x\n", r);
9385 return r;
9388 /* read the various parts of the structure */
9389 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9390 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9391 goto end;
9392 r = ReadClassStm( stm, &clsid );
9393 if( FAILED( r ) )
9394 goto end;
9396 r = STREAM_ReadString( stm, &szCLSIDName );
9397 if( FAILED( r ) )
9398 goto end;
9400 r = STREAM_ReadString( stm, &szOleTypeName );
9401 if( FAILED( r ) )
9402 goto end;
9404 r = STREAM_ReadString( stm, &szProgIDName );
9405 if( FAILED( r ) )
9406 goto end;
9408 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9409 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9410 goto end;
9412 /* ok, success... now we just need to store what we found */
9413 if( pcf )
9414 *pcf = RegisterClipboardFormatW( szOleTypeName );
9416 if( lplpszUserType )
9418 *lplpszUserType = szCLSIDName;
9419 szCLSIDName = NULL;
9422 end:
9423 CoTaskMemFree( szCLSIDName );
9424 CoTaskMemFree( szOleTypeName );
9425 CoTaskMemFree( szProgIDName );
9426 IStream_Release( stm );
9428 return r;
9431 /******************************************************************************
9432 * StgIsStorageFile [OLE32.@]
9433 * Verify if the file contains a storage object
9435 * PARAMS
9436 * fn [ I] Filename
9438 * RETURNS
9439 * S_OK if file has magic bytes as a storage object
9440 * S_FALSE if file is not storage
9442 HRESULT WINAPI
9443 StgIsStorageFile(LPCOLESTR fn)
9445 HANDLE hf;
9446 BYTE magic[8];
9447 DWORD bytes_read;
9449 TRACE("%s\n", debugstr_w(fn));
9450 hf = CreateFileW(fn, GENERIC_READ,
9451 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9452 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9454 if (hf == INVALID_HANDLE_VALUE)
9455 return STG_E_FILENOTFOUND;
9457 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9459 WARN(" unable to read file\n");
9460 CloseHandle(hf);
9461 return S_FALSE;
9464 CloseHandle(hf);
9466 if (bytes_read != 8) {
9467 TRACE(" too short\n");
9468 return S_FALSE;
9471 if (!memcmp(magic,STORAGE_magic,8)) {
9472 TRACE(" -> YES\n");
9473 return S_OK;
9476 TRACE(" -> Invalid header.\n");
9477 return S_FALSE;
9480 /***********************************************************************
9481 * WriteClassStm (OLE32.@)
9483 * Writes a CLSID to a stream.
9485 * PARAMS
9486 * pStm [I] Stream to write to.
9487 * rclsid [I] CLSID to write.
9489 * RETURNS
9490 * Success: S_OK.
9491 * Failure: HRESULT code.
9493 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9495 TRACE("(%p,%p)\n",pStm,rclsid);
9497 if (!pStm || !rclsid)
9498 return E_INVALIDARG;
9500 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9503 /***********************************************************************
9504 * ReadClassStm (OLE32.@)
9506 * Reads a CLSID from a stream.
9508 * PARAMS
9509 * pStm [I] Stream to read from.
9510 * rclsid [O] CLSID to read.
9512 * RETURNS
9513 * Success: S_OK.
9514 * Failure: HRESULT code.
9516 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9518 ULONG nbByte;
9519 HRESULT res;
9521 TRACE("(%p,%p)\n",pStm,pclsid);
9523 if (!pStm || !pclsid)
9524 return E_INVALIDARG;
9526 /* clear the output args */
9527 *pclsid = CLSID_NULL;
9529 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9531 if (FAILED(res))
9532 return res;
9534 if (nbByte != sizeof(CLSID))
9535 return STG_E_READFAULT;
9536 else
9537 return S_OK;
9541 /************************************************************************
9542 * OleConvert Functions
9543 ***********************************************************************/
9545 #define OLESTREAM_ID 0x501
9546 #define OLESTREAM_MAX_STR_LEN 255
9548 /* OLESTREAM memory structure to use for Get and Put Routines */
9549 typedef struct
9551 DWORD dwOleID;
9552 DWORD dwTypeID;
9553 DWORD dwOleTypeNameLength;
9554 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9555 CHAR *pstrOleObjFileName;
9556 DWORD dwOleObjFileNameLength;
9557 DWORD dwMetaFileWidth;
9558 DWORD dwMetaFileHeight;
9559 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9560 DWORD dwDataLength;
9561 BYTE *pData;
9562 } OLECONVERT_OLESTREAM_DATA;
9564 /* CompObj Stream structure */
9565 typedef struct
9567 BYTE byUnknown1[12];
9568 CLSID clsid;
9569 DWORD dwCLSIDNameLength;
9570 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9571 DWORD dwOleTypeNameLength;
9572 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9573 DWORD dwProgIDNameLength;
9574 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9575 BYTE byUnknown2[16];
9576 } OLECONVERT_ISTORAGE_COMPOBJ;
9578 /* Ole Presentation Stream structure */
9579 typedef struct
9581 BYTE byUnknown1[28];
9582 DWORD dwExtentX;
9583 DWORD dwExtentY;
9584 DWORD dwSize;
9585 BYTE *pData;
9586 } OLECONVERT_ISTORAGE_OLEPRES;
9589 /*************************************************************************
9590 * OLECONVERT_LoadOLE10 [Internal]
9592 * Loads the OLE10 STREAM to memory
9594 * PARAMS
9595 * pOleStream [I] The OLESTREAM
9596 * pData [I] Data Structure for the OLESTREAM Data
9598 * RETURNS
9599 * Success: S_OK
9600 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9601 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9603 * NOTES
9604 * This function is used by OleConvertOLESTREAMToIStorage only.
9606 * Memory allocated for pData must be freed by the caller
9608 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStream1)
9610 DWORD dwSize;
9611 HRESULT hRes = S_OK;
9612 int nTryCnt=0;
9613 int max_try = 6;
9615 pData->pData = NULL;
9616 pData->pstrOleObjFileName = NULL;
9618 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9620 /* Get the OleID */
9621 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9622 if(dwSize != sizeof(pData->dwOleID))
9624 hRes = CONVERT10_E_OLESTREAM_GET;
9626 else if(pData->dwOleID != OLESTREAM_ID)
9628 hRes = CONVERT10_E_OLESTREAM_FMT;
9630 else
9632 hRes = S_OK;
9633 break;
9637 if(hRes == S_OK)
9639 /* Get the TypeID... more info needed for this field */
9640 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9641 if(dwSize != sizeof(pData->dwTypeID))
9643 hRes = CONVERT10_E_OLESTREAM_GET;
9646 if(hRes == S_OK)
9648 if(pData->dwTypeID != 0)
9650 /* Get the length of the OleTypeName */
9651 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9652 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9654 hRes = CONVERT10_E_OLESTREAM_GET;
9657 if(hRes == S_OK)
9659 if(pData->dwOleTypeNameLength > 0)
9661 /* Get the OleTypeName */
9662 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9663 if(dwSize != pData->dwOleTypeNameLength)
9665 hRes = CONVERT10_E_OLESTREAM_GET;
9669 if(bStream1)
9671 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9672 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9674 hRes = CONVERT10_E_OLESTREAM_GET;
9676 if(hRes == S_OK)
9678 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9679 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9680 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9681 if(pData->pstrOleObjFileName)
9683 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9684 if(dwSize != pData->dwOleObjFileNameLength)
9686 hRes = CONVERT10_E_OLESTREAM_GET;
9689 else
9690 hRes = CONVERT10_E_OLESTREAM_GET;
9693 else
9695 /* Get the Width of the Metafile */
9696 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9697 if(dwSize != sizeof(pData->dwMetaFileWidth))
9699 hRes = CONVERT10_E_OLESTREAM_GET;
9701 if(hRes == S_OK)
9703 /* Get the Height of the Metafile */
9704 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9705 if(dwSize != sizeof(pData->dwMetaFileHeight))
9707 hRes = CONVERT10_E_OLESTREAM_GET;
9711 if(hRes == S_OK)
9713 /* Get the Length of the Data */
9714 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9715 if(dwSize != sizeof(pData->dwDataLength))
9717 hRes = CONVERT10_E_OLESTREAM_GET;
9721 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9723 if(!bStream1) /* if it is a second OLE stream data */
9725 pData->dwDataLength -= 8;
9726 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9727 if(dwSize != sizeof(pData->strUnknown))
9729 hRes = CONVERT10_E_OLESTREAM_GET;
9733 if(hRes == S_OK)
9735 if(pData->dwDataLength > 0)
9737 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9739 /* Get Data (ex. IStorage, Metafile, or BMP) */
9740 if(pData->pData)
9742 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9743 if(dwSize != pData->dwDataLength)
9745 hRes = CONVERT10_E_OLESTREAM_GET;
9748 else
9750 hRes = CONVERT10_E_OLESTREAM_GET;
9756 return hRes;
9759 /*************************************************************************
9760 * OLECONVERT_SaveOLE10 [Internal]
9762 * Saves the OLE10 STREAM From memory
9764 * PARAMS
9765 * pData [I] Data Structure for the OLESTREAM Data
9766 * pOleStream [I] The OLESTREAM to save
9768 * RETURNS
9769 * Success: S_OK
9770 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9772 * NOTES
9773 * This function is used by OleConvertIStorageToOLESTREAM only.
9776 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9778 DWORD dwSize;
9779 HRESULT hRes = S_OK;
9782 /* Set the OleID */
9783 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9784 if(dwSize != sizeof(pData->dwOleID))
9786 hRes = CONVERT10_E_OLESTREAM_PUT;
9789 if(hRes == S_OK)
9791 /* Set the TypeID */
9792 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9793 if(dwSize != sizeof(pData->dwTypeID))
9795 hRes = CONVERT10_E_OLESTREAM_PUT;
9799 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9801 /* Set the Length of the OleTypeName */
9802 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9803 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9805 hRes = CONVERT10_E_OLESTREAM_PUT;
9808 if(hRes == S_OK)
9810 if(pData->dwOleTypeNameLength > 0)
9812 /* Set the OleTypeName */
9813 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9814 if(dwSize != pData->dwOleTypeNameLength)
9816 hRes = CONVERT10_E_OLESTREAM_PUT;
9821 if(hRes == S_OK)
9823 /* Set the width of the Metafile */
9824 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9825 if(dwSize != sizeof(pData->dwMetaFileWidth))
9827 hRes = CONVERT10_E_OLESTREAM_PUT;
9831 if(hRes == S_OK)
9833 /* Set the height of the Metafile */
9834 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9835 if(dwSize != sizeof(pData->dwMetaFileHeight))
9837 hRes = CONVERT10_E_OLESTREAM_PUT;
9841 if(hRes == S_OK)
9843 /* Set the length of the Data */
9844 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9845 if(dwSize != sizeof(pData->dwDataLength))
9847 hRes = CONVERT10_E_OLESTREAM_PUT;
9851 if(hRes == S_OK)
9853 if(pData->dwDataLength > 0)
9855 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9856 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9857 if(dwSize != pData->dwDataLength)
9859 hRes = CONVERT10_E_OLESTREAM_PUT;
9864 return hRes;
9867 /*************************************************************************
9868 * OLECONVERT_GetOLE20FromOLE10[Internal]
9870 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9871 * opens it, and copies the content to the dest IStorage for
9872 * OleConvertOLESTREAMToIStorage
9875 * PARAMS
9876 * pDestStorage [I] The IStorage to copy the data to
9877 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9878 * nBufferLength [I] The size of the buffer
9880 * RETURNS
9881 * Nothing
9883 * NOTES
9887 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9889 HRESULT hRes;
9890 HANDLE hFile;
9891 IStorage *pTempStorage;
9892 DWORD dwNumOfBytesWritten;
9893 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9895 /* Create a temp File */
9896 GetTempPathW(MAX_PATH, wstrTempDir);
9897 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9898 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9900 if(hFile != INVALID_HANDLE_VALUE)
9902 /* Write IStorage Data to File */
9903 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9904 CloseHandle(hFile);
9906 /* Open and copy temp storage to the Dest Storage */
9907 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9908 if(hRes == S_OK)
9910 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9911 IStorage_Release(pTempStorage);
9913 DeleteFileW(wstrTempFile);
9918 /*************************************************************************
9919 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9921 * Saves the OLE10 STREAM From memory
9923 * PARAMS
9924 * pStorage [I] The Src IStorage to copy
9925 * pData [I] The Dest Memory to write to.
9927 * RETURNS
9928 * The size in bytes allocated for pData
9930 * NOTES
9931 * Memory allocated for pData must be freed by the caller
9933 * Used by OleConvertIStorageToOLESTREAM only.
9936 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9938 HANDLE hFile;
9939 HRESULT hRes;
9940 DWORD nDataLength = 0;
9941 IStorage *pTempStorage;
9942 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9944 *pData = NULL;
9946 /* Create temp Storage */
9947 GetTempPathW(MAX_PATH, wstrTempDir);
9948 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9949 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9951 if(hRes == S_OK)
9953 /* Copy Src Storage to the Temp Storage */
9954 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9955 IStorage_Release(pTempStorage);
9957 /* Open Temp Storage as a file and copy to memory */
9958 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9959 if(hFile != INVALID_HANDLE_VALUE)
9961 nDataLength = GetFileSize(hFile, NULL);
9962 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9963 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9964 CloseHandle(hFile);
9966 DeleteFileW(wstrTempFile);
9968 return nDataLength;
9971 /*************************************************************************
9972 * OLECONVERT_CreateCompObjStream [Internal]
9974 * Creates a "\001CompObj" is the destination IStorage if necessary.
9976 * PARAMS
9977 * pStorage [I] The dest IStorage to create the CompObj Stream
9978 * if necessary.
9979 * strOleTypeName [I] The ProgID
9981 * RETURNS
9982 * Success: S_OK
9983 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9985 * NOTES
9986 * This function is used by OleConvertOLESTREAMToIStorage only.
9988 * The stream data is stored in the OLESTREAM and there should be
9989 * no need to recreate the stream. If the stream is manually
9990 * deleted it will attempt to create it by querying the registry.
9994 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9996 IStream *pStream;
9997 HRESULT hStorageRes, hRes = S_OK;
9998 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9999 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
10001 static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10002 static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
10004 /* Initialize the CompObj structure */
10005 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
10006 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
10007 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
10010 /* Create a CompObj stream if it doesn't exist */
10011 hStorageRes = IStorage_CreateStream(pStorage, L"\1CompObj",
10012 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10013 if(hStorageRes == S_OK)
10015 /* copy the OleTypeName to the compobj struct */
10016 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10017 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10019 /* copy the OleTypeName to the compobj struct */
10020 /* Note: in the test made, these were Identical */
10021 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10022 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10024 /* Get the CLSID */
10025 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10026 bufferW, OLESTREAM_MAX_STR_LEN );
10027 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10029 if(hRes == S_OK)
10031 HKEY hKey;
10032 LONG hErr;
10033 /* Get the CLSID Default Name from the Registry */
10034 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10035 if(hErr == ERROR_SUCCESS)
10037 char strTemp[OLESTREAM_MAX_STR_LEN];
10038 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10039 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10040 if(hErr == ERROR_SUCCESS)
10042 strcpy(IStorageCompObj.strCLSIDName, strTemp);
10044 RegCloseKey(hKey);
10048 /* Write CompObj Structure to stream */
10049 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10051 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10053 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10054 if(IStorageCompObj.dwCLSIDNameLength > 0)
10056 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10058 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10059 if(IStorageCompObj.dwOleTypeNameLength > 0)
10061 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10063 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10064 if(IStorageCompObj.dwProgIDNameLength > 0)
10066 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10068 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10069 IStream_Release(pStream);
10071 return hRes;
10075 /*************************************************************************
10076 * OLECONVERT_CreateOlePresStream[Internal]
10078 * Creates the "\002OlePres000" Stream with the Metafile data
10080 * PARAMS
10081 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10082 * dwExtentX [I] Width of the Metafile
10083 * dwExtentY [I] Height of the Metafile
10084 * pData [I] Metafile data
10085 * dwDataLength [I] Size of the Metafile data
10087 * RETURNS
10088 * Success: S_OK
10089 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10091 * NOTES
10092 * This function is used by OleConvertOLESTREAMToIStorage only.
10095 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10097 HRESULT hRes;
10098 IStream *pStream;
10099 static const BYTE pOlePresStreamHeader[] =
10101 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10102 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10103 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10104 0x00, 0x00, 0x00, 0x00
10107 static const BYTE pOlePresStreamHeaderEmpty[] =
10109 0x00, 0x00, 0x00, 0x00,
10110 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10111 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10112 0x00, 0x00, 0x00, 0x00
10115 /* Create the OlePres000 Stream */
10116 hRes = IStorage_CreateStream(pStorage, L"\2OlePres000",
10117 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10119 if(hRes == S_OK)
10121 DWORD nHeaderSize;
10122 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10124 memset(&OlePres, 0, sizeof(OlePres));
10125 /* Do we have any metafile data to save */
10126 if(dwDataLength > 0)
10128 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10129 nHeaderSize = sizeof(pOlePresStreamHeader);
10131 else
10133 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10134 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10136 /* Set width and height of the metafile */
10137 OlePres.dwExtentX = dwExtentX;
10138 OlePres.dwExtentY = -dwExtentY;
10140 /* Set Data and Length */
10141 if(dwDataLength > sizeof(METAFILEPICT16))
10143 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10144 OlePres.pData = &(pData[8]);
10146 /* Save OlePres000 Data to Stream */
10147 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10148 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10149 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10150 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10151 if(OlePres.dwSize > 0)
10153 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10155 IStream_Release(pStream);
10159 /*************************************************************************
10160 * OLECONVERT_CreateOle10NativeStream [Internal]
10162 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10164 * PARAMS
10165 * pStorage [I] Dest storage to create the stream in
10166 * pData [I] Ole10 Native Data (ex. bmp)
10167 * dwDataLength [I] Size of the Ole10 Native Data
10169 * RETURNS
10170 * Nothing
10172 * NOTES
10173 * This function is used by OleConvertOLESTREAMToIStorage only.
10175 * Might need to verify the data and return appropriate error message
10178 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10180 HRESULT hRes;
10181 IStream *pStream;
10183 /* Create the Ole10Native Stream */
10184 hRes = IStorage_CreateStream(pStorage, L"\1Ole10Native",
10185 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10187 if(hRes == S_OK)
10189 /* Write info to stream */
10190 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10191 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10192 IStream_Release(pStream);
10197 /*************************************************************************
10198 * OLECONVERT_GetOLE10ProgID [Internal]
10200 * Finds the ProgID (or OleTypeID) from the IStorage
10202 * PARAMS
10203 * pStorage [I] The Src IStorage to get the ProgID
10204 * strProgID [I] the ProgID string to get
10205 * dwSize [I] the size of the string
10207 * RETURNS
10208 * Success: S_OK
10209 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10211 * NOTES
10212 * This function is used by OleConvertIStorageToOLESTREAM only.
10216 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10218 HRESULT hRes;
10219 IStream *pStream;
10220 LARGE_INTEGER iSeekPos;
10221 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10223 /* Open the CompObj Stream */
10224 hRes = IStorage_OpenStream(pStorage, L"\1CompObj", NULL,
10225 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10226 if(hRes == S_OK)
10229 /*Get the OleType from the CompObj Stream */
10230 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10231 iSeekPos.u.HighPart = 0;
10233 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10234 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10235 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10236 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10237 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10238 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10239 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10241 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10242 if(*dwSize > 0)
10244 IStream_Read(pStream, strProgID, *dwSize, NULL);
10246 IStream_Release(pStream);
10248 else
10250 STATSTG stat;
10251 LPOLESTR wstrProgID;
10253 /* Get the OleType from the registry */
10254 REFCLSID clsid = &(stat.clsid);
10255 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10256 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10257 if(hRes == S_OK)
10259 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10260 CoTaskMemFree(wstrProgID);
10264 return hRes;
10267 /*************************************************************************
10268 * OLECONVERT_GetOle10PresData [Internal]
10270 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10272 * PARAMS
10273 * pStorage [I] Src IStroage
10274 * pOleStream [I] Dest OleStream Mem Struct
10276 * RETURNS
10277 * Nothing
10279 * NOTES
10280 * This function is used by OleConvertIStorageToOLESTREAM only.
10282 * Memory allocated for pData must be freed by the caller
10286 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10289 HRESULT hRes;
10290 IStream *pStream;
10292 /* Initialize Default data for OLESTREAM */
10293 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10294 pOleStreamData[0].dwTypeID = 2;
10295 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10296 pOleStreamData[1].dwTypeID = 0;
10297 pOleStreamData[0].dwMetaFileWidth = 0;
10298 pOleStreamData[0].dwMetaFileHeight = 0;
10299 pOleStreamData[0].pData = NULL;
10300 pOleStreamData[1].pData = NULL;
10302 /* Open Ole10Native Stream */
10303 hRes = IStorage_OpenStream(pStorage, L"\1Ole10Native", NULL,
10304 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10305 if(hRes == S_OK)
10308 /* Read Size and Data */
10309 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10310 if(pOleStreamData->dwDataLength > 0)
10312 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10313 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10315 IStream_Release(pStream);
10321 /*************************************************************************
10322 * OLECONVERT_GetOle20PresData[Internal]
10324 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10326 * PARAMS
10327 * pStorage [I] Src IStroage
10328 * pOleStreamData [I] Dest OleStream Mem Struct
10330 * RETURNS
10331 * Nothing
10333 * NOTES
10334 * This function is used by OleConvertIStorageToOLESTREAM only.
10336 * Memory allocated for pData must be freed by the caller
10338 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10340 HRESULT hRes;
10341 IStream *pStream;
10342 OLECONVERT_ISTORAGE_OLEPRES olePress;
10344 /* Initialize Default data for OLESTREAM */
10345 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10346 pOleStreamData[0].dwTypeID = 2;
10347 pOleStreamData[0].dwMetaFileWidth = 0;
10348 pOleStreamData[0].dwMetaFileHeight = 0;
10349 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10350 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10351 pOleStreamData[1].dwTypeID = 0;
10352 pOleStreamData[1].dwOleTypeNameLength = 0;
10353 pOleStreamData[1].strOleTypeName[0] = 0;
10354 pOleStreamData[1].dwMetaFileWidth = 0;
10355 pOleStreamData[1].dwMetaFileHeight = 0;
10356 pOleStreamData[1].pData = NULL;
10357 pOleStreamData[1].dwDataLength = 0;
10360 /* Open OlePress000 stream */
10361 hRes = IStorage_OpenStream(pStorage, L"\2OlePres000", NULL,
10362 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10363 if(hRes == S_OK)
10365 LARGE_INTEGER iSeekPos;
10366 METAFILEPICT16 MetaFilePict;
10367 static const char strMetafilePictName[] = "METAFILEPICT";
10369 /* Set the TypeID for a Metafile */
10370 pOleStreamData[1].dwTypeID = 5;
10372 /* Set the OleTypeName to Metafile */
10373 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10374 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10376 iSeekPos.u.HighPart = 0;
10377 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10379 /* Get Presentation Data */
10380 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10381 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10382 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10383 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10385 /*Set width and Height */
10386 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10387 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10388 if(olePress.dwSize > 0)
10390 /* Set Length */
10391 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10393 /* Set MetaFilePict struct */
10394 MetaFilePict.mm = 8;
10395 MetaFilePict.xExt = olePress.dwExtentX;
10396 MetaFilePict.yExt = olePress.dwExtentY;
10397 MetaFilePict.hMF = 0;
10399 /* Get Metafile Data */
10400 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10401 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10402 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10404 IStream_Release(pStream);
10408 /*************************************************************************
10409 * OleConvertOLESTREAMToIStorage [OLE32.@]
10411 * Read info on MSDN
10413 * TODO
10414 * DVTARGETDEVICE parameter is not handled
10415 * Still unsure of some mem fields for OLE 10 Stream
10416 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10417 * and "\001OLE" streams
10420 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10421 LPOLESTREAM pOleStream,
10422 LPSTORAGE pstg,
10423 const DVTARGETDEVICE* ptd)
10425 int i;
10426 HRESULT hRes=S_OK;
10427 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10429 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10431 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10433 if(ptd != NULL)
10435 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10438 if(pstg == NULL || pOleStream == NULL)
10440 hRes = E_INVALIDARG;
10443 if(hRes == S_OK)
10445 /* Load the OLESTREAM to Memory */
10446 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10449 if(hRes == S_OK)
10451 /* Load the OLESTREAM to Memory (part 2)*/
10452 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10455 if(hRes == S_OK)
10458 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10460 /* Do we have the IStorage Data in the OLESTREAM */
10461 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10463 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10464 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10466 else
10468 /* It must be an original OLE 1.0 source */
10469 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10472 else
10474 /* It must be an original OLE 1.0 source */
10475 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10478 /* Create CompObj Stream if necessary */
10479 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10480 if(hRes == S_OK)
10482 /*Create the Ole Stream if necessary */
10483 STORAGE_CreateOleStream(pstg, 0);
10488 /* Free allocated memory */
10489 for(i=0; i < 2; i++)
10491 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10492 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10493 pOleStreamData[i].pstrOleObjFileName = NULL;
10495 return hRes;
10498 /*************************************************************************
10499 * OleConvertIStorageToOLESTREAM [OLE32.@]
10501 * Read info on MSDN
10503 * Read info on MSDN
10505 * TODO
10506 * Still unsure of some mem fields for OLE 10 Stream
10507 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10508 * and "\001OLE" streams.
10511 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10512 LPSTORAGE pstg,
10513 LPOLESTREAM pOleStream)
10515 int i;
10516 HRESULT hRes = S_OK;
10517 IStream *pStream;
10518 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10520 TRACE("%p %p\n", pstg, pOleStream);
10522 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10524 if(pstg == NULL || pOleStream == NULL)
10526 hRes = E_INVALIDARG;
10528 if(hRes == S_OK)
10530 /* Get the ProgID */
10531 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10532 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10534 if(hRes == S_OK)
10536 /* Was it originally Ole10 */
10537 hRes = IStorage_OpenStream(pstg, L"\1Ole10Native", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10538 if(hRes == S_OK)
10540 IStream_Release(pStream);
10541 /* Get Presentation Data for Ole10Native */
10542 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10544 else
10546 /* Get Presentation Data (OLE20) */
10547 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10550 /* Save OLESTREAM */
10551 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10552 if(hRes == S_OK)
10554 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10559 /* Free allocated memory */
10560 for(i=0; i < 2; i++)
10562 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10565 return hRes;
10568 enum stream_1ole_flags {
10569 OleStream_LinkedObject = 0x00000001,
10570 OleStream_Convert = 0x00000004
10573 /***********************************************************************
10574 * GetConvertStg (OLE32.@)
10576 HRESULT WINAPI GetConvertStg(IStorage *stg)
10578 static const DWORD version_magic = 0x02000001;
10579 DWORD header[2];
10580 IStream *stream;
10581 HRESULT hr;
10583 TRACE("%p\n", stg);
10585 if (!stg) return E_INVALIDARG;
10587 hr = IStorage_OpenStream(stg, L"\1Ole", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10588 if (FAILED(hr)) return hr;
10590 hr = IStream_Read(stream, header, sizeof(header), NULL);
10591 IStream_Release(stream);
10592 if (FAILED(hr)) return hr;
10594 if (header[0] != version_magic)
10596 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10597 return E_FAIL;
10600 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10603 /***********************************************************************
10604 * SetConvertStg (OLE32.@)
10606 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10608 DWORD flags = convert ? OleStream_Convert : 0;
10609 IStream *stream;
10610 DWORD header[2];
10611 HRESULT hr;
10613 TRACE("(%p, %d)\n", storage, convert);
10615 hr = IStorage_OpenStream(storage, L"\1Ole", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10616 if (FAILED(hr))
10618 if (hr != STG_E_FILENOTFOUND)
10619 return hr;
10621 return STORAGE_CreateOleStream(storage, flags);
10624 hr = IStream_Read(stream, header, sizeof(header), NULL);
10625 if (FAILED(hr))
10627 IStream_Release(stream);
10628 return hr;
10631 /* update flag if differs */
10632 if ((header[1] ^ flags) & OleStream_Convert)
10634 LARGE_INTEGER pos = {{0}};
10636 if (header[1] & OleStream_Convert)
10637 flags = header[1] & ~OleStream_Convert;
10638 else
10639 flags = header[1] | OleStream_Convert;
10641 pos.QuadPart = sizeof(DWORD);
10642 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10643 if (FAILED(hr))
10645 IStream_Release(stream);
10646 return hr;
10649 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10652 IStream_Release(stream);
10653 return hr;