winealsa: Use dedicated macros to call interface functions.
[wine.git] / dlls / ole32 / storage32.c
blobfa7158bad1b69c86d242697dafa87f3e76be634c
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 %#lx\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, %ld: 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("<-- %#lx\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, %lu, %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("<-- %#lx (asked %lu, got %lu)\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, %lu.\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("<-- %#lx\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, refcount %lu.\n", iface, 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, refcount %lu.\n", iface, 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("<-- %#lx\n", hr);
1451 return hr;
1454 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1456 StgStreamImpl *strm;
1458 TRACE("%p, %ld.\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, %ld.\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, %#lx, %ld, %p.\n", iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1511 if ( (pwcsName==NULL) || (ppstm==0) )
1513 res = E_INVALIDARG;
1514 goto end;
1517 *ppstm = NULL;
1519 if ( FAILED( validateSTGM(grfMode) ) ||
1520 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1522 res = STG_E_INVALIDFLAG;
1523 goto end;
1527 * As documented.
1529 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1531 res = STG_E_INVALIDFUNCTION;
1532 goto end;
1535 if (This->reverted)
1537 res = STG_E_REVERTED;
1538 goto end;
1542 * Check that we're compatible with the parent's storage mode, but
1543 * only if we are not in transacted mode
1545 if(!(This->openFlags & STGM_TRANSACTED)) {
1546 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1548 res = STG_E_INVALIDFLAG;
1549 goto end;
1554 * Search for the element with the given name
1556 streamEntryRef = findElement(
1557 This,
1558 This->storageDirEntry,
1559 pwcsName,
1560 &currentEntry);
1563 * If it was found, construct the stream object and return a pointer to it.
1565 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1566 (currentEntry.stgType==STGTY_STREAM) )
1568 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1570 /* A single stream cannot be opened a second time. */
1571 res = STG_E_ACCESSDENIED;
1572 goto end;
1575 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1577 if (newStream)
1579 newStream->grfMode = grfMode;
1580 *ppstm = &newStream->IStream_iface;
1582 IStream_AddRef(*ppstm);
1584 res = S_OK;
1585 goto end;
1588 res = E_OUTOFMEMORY;
1589 goto end;
1592 res = STG_E_FILENOTFOUND;
1594 end:
1595 if (res == S_OK)
1596 TRACE("<-- IStream %p\n", *ppstm);
1597 TRACE("<-- %#lx\n", res);
1598 return res;
1601 /************************************************************************
1602 * StorageBaseImpl_OpenStorage (IStorage)
1604 * This method will open a new storage object from the current storage.
1606 * See Windows documentation for more details on IStorage methods.
1608 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
1609 IStorage* iface,
1610 const OLECHAR* pwcsName, /* [string][unique][in] */
1611 IStorage* pstgPriority, /* [unique][in] */
1612 DWORD grfMode, /* [in] */
1613 SNB snbExclude, /* [unique][in] */
1614 DWORD reserved, /* [in] */
1615 IStorage** ppstg) /* [out] */
1617 StorageBaseImpl *This = impl_from_IStorage(iface);
1618 StorageInternalImpl* newStorage;
1619 StorageBaseImpl* newTransactedStorage;
1620 DirEntry currentEntry;
1621 DirRef storageEntryRef;
1622 HRESULT res = STG_E_UNKNOWN;
1624 TRACE("%p, %s, %p, %#lx, %p, %ld, %p.\n", iface, debugstr_w(pwcsName), pstgPriority,
1625 grfMode, snbExclude, reserved, ppstg);
1627 if ((pwcsName==NULL) || (ppstg==0) )
1629 res = E_INVALIDARG;
1630 goto end;
1633 if (This->openFlags & STGM_SIMPLE)
1635 res = STG_E_INVALIDFUNCTION;
1636 goto end;
1639 /* as documented */
1640 if (snbExclude != NULL)
1642 res = STG_E_INVALIDPARAMETER;
1643 goto end;
1646 if ( FAILED( validateSTGM(grfMode) ))
1648 res = STG_E_INVALIDFLAG;
1649 goto end;
1653 * As documented.
1655 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1656 (grfMode & STGM_DELETEONRELEASE) ||
1657 (grfMode & STGM_PRIORITY) )
1659 res = STG_E_INVALIDFUNCTION;
1660 goto end;
1663 if (This->reverted)
1664 return STG_E_REVERTED;
1667 * Check that we're compatible with the parent's storage mode,
1668 * but only if we are not transacted
1670 if(!(This->openFlags & STGM_TRANSACTED)) {
1671 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1673 res = STG_E_ACCESSDENIED;
1674 goto end;
1678 *ppstg = NULL;
1680 storageEntryRef = findElement(
1681 This,
1682 This->storageDirEntry,
1683 pwcsName,
1684 &currentEntry);
1686 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1687 (currentEntry.stgType==STGTY_STORAGE) )
1689 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1691 /* A single storage cannot be opened a second time. */
1692 res = STG_E_ACCESSDENIED;
1693 goto end;
1696 newStorage = StorageInternalImpl_Construct(
1697 This,
1698 grfMode,
1699 storageEntryRef);
1701 if (newStorage != 0)
1703 if (grfMode & STGM_TRANSACTED)
1705 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1707 if (FAILED(res))
1709 HeapFree(GetProcessHeap(), 0, newStorage);
1710 goto end;
1713 *ppstg = &newTransactedStorage->IStorage_iface;
1715 else
1717 *ppstg = &newStorage->base.IStorage_iface;
1720 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1722 res = S_OK;
1723 goto end;
1726 res = STG_E_INSUFFICIENTMEMORY;
1727 goto end;
1730 res = STG_E_FILENOTFOUND;
1732 end:
1733 TRACE("<-- %#lx\n", res);
1734 return res;
1737 /************************************************************************
1738 * StorageBaseImpl_EnumElements (IStorage)
1740 * This method will create an enumerator object that can be used to
1741 * retrieve information about all the elements in the storage object.
1743 * See Windows documentation for more details on IStorage methods.
1745 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1746 IStorage* iface,
1747 DWORD reserved1, /* [in] */
1748 void* reserved2, /* [size_is][unique][in] */
1749 DWORD reserved3, /* [in] */
1750 IEnumSTATSTG** ppenum) /* [out] */
1752 StorageBaseImpl *This = impl_from_IStorage(iface);
1753 IEnumSTATSTGImpl* newEnum;
1755 TRACE("%p, %ld, %p, %ld, %p.\n", iface, reserved1, reserved2, reserved3, ppenum);
1757 if (!ppenum)
1758 return E_INVALIDARG;
1760 if (This->reverted)
1761 return STG_E_REVERTED;
1763 newEnum = IEnumSTATSTGImpl_Construct(
1764 This,
1765 This->storageDirEntry);
1767 if (newEnum)
1769 *ppenum = &newEnum->IEnumSTATSTG_iface;
1770 return S_OK;
1773 return E_OUTOFMEMORY;
1776 /************************************************************************
1777 * StorageBaseImpl_Stat (IStorage)
1779 * This method will retrieve information about this storage object.
1781 * See Windows documentation for more details on IStorage methods.
1783 static HRESULT WINAPI StorageBaseImpl_Stat(
1784 IStorage* iface,
1785 STATSTG* pstatstg, /* [out] */
1786 DWORD grfStatFlag) /* [in] */
1788 StorageBaseImpl *This = impl_from_IStorage(iface);
1789 DirEntry currentEntry;
1790 HRESULT res = STG_E_UNKNOWN;
1792 TRACE("%p, %p, %#lx.\n", iface, pstatstg, grfStatFlag);
1794 if (!pstatstg)
1796 res = E_INVALIDARG;
1797 goto end;
1800 if (This->reverted)
1802 res = STG_E_REVERTED;
1803 goto end;
1806 res = StorageBaseImpl_ReadDirEntry(
1807 This,
1808 This->storageDirEntry,
1809 &currentEntry);
1811 if (SUCCEEDED(res))
1813 StorageUtl_CopyDirEntryToSTATSTG(
1814 This,
1815 pstatstg,
1816 &currentEntry,
1817 grfStatFlag);
1819 pstatstg->grfMode = This->openFlags;
1820 pstatstg->grfStateBits = This->stateBits;
1823 end:
1824 if (res == S_OK)
1826 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
1828 TRACE("<-- %#lx\n", res);
1829 return res;
1832 /************************************************************************
1833 * StorageBaseImpl_RenameElement (IStorage)
1835 * This method will rename the specified element.
1837 * See Windows documentation for more details on IStorage methods.
1839 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1840 IStorage* iface,
1841 const OLECHAR* pwcsOldName, /* [in] */
1842 const OLECHAR* pwcsNewName) /* [in] */
1844 StorageBaseImpl *This = impl_from_IStorage(iface);
1845 DirEntry currentEntry;
1846 DirRef currentEntryRef;
1848 TRACE("(%p, %s, %s)\n",
1849 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1851 if (This->reverted)
1852 return STG_E_REVERTED;
1854 currentEntryRef = findElement(This,
1855 This->storageDirEntry,
1856 pwcsNewName,
1857 &currentEntry);
1859 if (currentEntryRef != DIRENTRY_NULL)
1862 * There is already an element with the new name
1864 return STG_E_FILEALREADYEXISTS;
1868 * Search for the old element name
1870 currentEntryRef = findElement(This,
1871 This->storageDirEntry,
1872 pwcsOldName,
1873 &currentEntry);
1875 if (currentEntryRef != DIRENTRY_NULL)
1877 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1878 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1880 WARN("Element is already open; cannot rename.\n");
1881 return STG_E_ACCESSDENIED;
1884 /* Remove the element from its current position in the tree */
1885 removeFromTree(This, This->storageDirEntry,
1886 currentEntryRef);
1888 /* Change the name of the element */
1889 lstrcpyW(currentEntry.name, pwcsNewName);
1891 /* Delete any sibling links */
1892 currentEntry.leftChild = DIRENTRY_NULL;
1893 currentEntry.rightChild = DIRENTRY_NULL;
1895 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1896 &currentEntry);
1898 /* Insert the element in a new position in the tree */
1899 insertIntoTree(This, This->storageDirEntry,
1900 currentEntryRef);
1902 else
1905 * There is no element with the old name
1907 return STG_E_FILENOTFOUND;
1910 return StorageBaseImpl_Flush(This);
1913 /************************************************************************
1914 * StorageBaseImpl_CreateStream (IStorage)
1916 * This method will create a stream object within this storage
1918 * See Windows documentation for more details on IStorage methods.
1920 static HRESULT WINAPI StorageBaseImpl_CreateStream(
1921 IStorage* iface,
1922 const OLECHAR* pwcsName, /* [string][in] */
1923 DWORD grfMode, /* [in] */
1924 DWORD reserved1, /* [in] */
1925 DWORD reserved2, /* [in] */
1926 IStream** ppstm) /* [out] */
1928 StorageBaseImpl *This = impl_from_IStorage(iface);
1929 StgStreamImpl* newStream;
1930 DirEntry currentEntry, newStreamEntry;
1931 DirRef currentEntryRef, newStreamEntryRef;
1932 HRESULT hr;
1934 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode, reserved1, reserved2, ppstm);
1936 if (ppstm == 0)
1937 return STG_E_INVALIDPOINTER;
1939 if (pwcsName == 0)
1940 return STG_E_INVALIDNAME;
1942 if (reserved1 || reserved2)
1943 return STG_E_INVALIDPARAMETER;
1945 if ( FAILED( validateSTGM(grfMode) ))
1946 return STG_E_INVALIDFLAG;
1948 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1949 return STG_E_INVALIDFLAG;
1951 if (This->reverted)
1952 return STG_E_REVERTED;
1955 * As documented.
1957 if ((grfMode & STGM_DELETEONRELEASE) ||
1958 (grfMode & STGM_TRANSACTED))
1959 return STG_E_INVALIDFUNCTION;
1962 * Don't worry about permissions in transacted mode, as we can always write
1963 * changes; we just can't always commit them.
1965 if(!(This->openFlags & STGM_TRANSACTED)) {
1966 /* Can't create a stream on read-only storage */
1967 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1968 return STG_E_ACCESSDENIED;
1970 /* Can't create a stream with greater access than the parent. */
1971 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1972 return STG_E_ACCESSDENIED;
1975 if(This->openFlags & STGM_SIMPLE)
1976 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1978 *ppstm = 0;
1980 currentEntryRef = findElement(This,
1981 This->storageDirEntry,
1982 pwcsName,
1983 &currentEntry);
1985 if (currentEntryRef != DIRENTRY_NULL)
1988 * An element with this name already exists
1990 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1992 IStorage_DestroyElement(iface, pwcsName);
1994 else
1995 return STG_E_FILEALREADYEXISTS;
1999 * memset the empty entry
2001 memset(&newStreamEntry, 0, sizeof(DirEntry));
2003 newStreamEntry.sizeOfNameString =
2004 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2006 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2007 return STG_E_INVALIDNAME;
2009 lstrcpyW(newStreamEntry.name, pwcsName);
2011 newStreamEntry.stgType = STGTY_STREAM;
2012 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2013 newStreamEntry.size.u.LowPart = 0;
2014 newStreamEntry.size.u.HighPart = 0;
2016 newStreamEntry.leftChild = DIRENTRY_NULL;
2017 newStreamEntry.rightChild = DIRENTRY_NULL;
2018 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2020 /* call CoFileTime to get the current time
2021 newStreamEntry.ctime
2022 newStreamEntry.mtime
2025 /* newStreamEntry.clsid */
2028 * Create an entry with the new data
2030 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2031 if (FAILED(hr))
2032 return hr;
2035 * Insert the new entry in the parent storage's tree.
2037 hr = insertIntoTree(
2038 This,
2039 This->storageDirEntry,
2040 newStreamEntryRef);
2041 if (FAILED(hr))
2043 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2044 return hr;
2048 * Open the stream to return it.
2050 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2052 if (newStream)
2054 *ppstm = &newStream->IStream_iface;
2055 IStream_AddRef(*ppstm);
2057 else
2059 return STG_E_INSUFFICIENTMEMORY;
2062 return StorageBaseImpl_Flush(This);
2065 /************************************************************************
2066 * StorageBaseImpl_SetClass (IStorage)
2068 * This method will write the specified CLSID in the directory entry of this
2069 * storage.
2071 * See Windows documentation for more details on IStorage methods.
2073 static HRESULT WINAPI StorageBaseImpl_SetClass(
2074 IStorage* iface,
2075 REFCLSID clsid) /* [in] */
2077 StorageBaseImpl *This = impl_from_IStorage(iface);
2078 HRESULT hRes;
2079 DirEntry currentEntry;
2081 TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid));
2083 if (This->reverted)
2084 return STG_E_REVERTED;
2086 hRes = StorageBaseImpl_ReadDirEntry(This,
2087 This->storageDirEntry,
2088 &currentEntry);
2089 if (SUCCEEDED(hRes))
2091 currentEntry.clsid = *clsid;
2093 hRes = StorageBaseImpl_WriteDirEntry(This,
2094 This->storageDirEntry,
2095 &currentEntry);
2098 if (SUCCEEDED(hRes))
2099 hRes = StorageBaseImpl_Flush(This);
2101 return hRes;
2104 /************************************************************************
2105 * StorageBaseImpl_CreateStorage (IStorage)
2107 * This method will create the storage object within the provided storage.
2109 * See Windows documentation for more details on IStorage methods.
2111 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
2112 IStorage* iface,
2113 const OLECHAR *pwcsName, /* [string][in] */
2114 DWORD grfMode, /* [in] */
2115 DWORD reserved1, /* [in] */
2116 DWORD reserved2, /* [in] */
2117 IStorage **ppstg) /* [out] */
2119 StorageBaseImpl* This = impl_from_IStorage(iface);
2121 DirEntry currentEntry;
2122 DirEntry newEntry;
2123 DirRef currentEntryRef;
2124 DirRef newEntryRef;
2125 HRESULT hr;
2127 TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode,
2128 reserved1, reserved2, ppstg);
2130 if (ppstg == 0)
2131 return STG_E_INVALIDPOINTER;
2133 if (This->openFlags & STGM_SIMPLE)
2135 return STG_E_INVALIDFUNCTION;
2138 if (pwcsName == 0)
2139 return STG_E_INVALIDNAME;
2141 *ppstg = NULL;
2143 if ( FAILED( validateSTGM(grfMode) ) ||
2144 (grfMode & STGM_DELETEONRELEASE) )
2146 WARN("bad grfMode: %#lx\n", grfMode);
2147 return STG_E_INVALIDFLAG;
2150 if (This->reverted)
2151 return STG_E_REVERTED;
2154 * Check that we're compatible with the parent's storage mode
2156 if ( !(This->openFlags & STGM_TRANSACTED) &&
2157 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2159 WARN("access denied\n");
2160 return STG_E_ACCESSDENIED;
2163 currentEntryRef = findElement(This,
2164 This->storageDirEntry,
2165 pwcsName,
2166 &currentEntry);
2168 if (currentEntryRef != DIRENTRY_NULL)
2171 * An element with this name already exists
2173 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2174 ((This->openFlags & STGM_TRANSACTED) ||
2175 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2177 hr = IStorage_DestroyElement(iface, pwcsName);
2178 if (FAILED(hr))
2179 return hr;
2181 else
2183 WARN("file already exists\n");
2184 return STG_E_FILEALREADYEXISTS;
2187 else if (!(This->openFlags & STGM_TRANSACTED) &&
2188 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2190 WARN("read-only storage\n");
2191 return STG_E_ACCESSDENIED;
2194 memset(&newEntry, 0, sizeof(DirEntry));
2196 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2198 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2200 FIXME("name too long\n");
2201 return STG_E_INVALIDNAME;
2204 lstrcpyW(newEntry.name, pwcsName);
2206 newEntry.stgType = STGTY_STORAGE;
2207 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2208 newEntry.size.u.LowPart = 0;
2209 newEntry.size.u.HighPart = 0;
2211 newEntry.leftChild = DIRENTRY_NULL;
2212 newEntry.rightChild = DIRENTRY_NULL;
2213 newEntry.dirRootEntry = DIRENTRY_NULL;
2215 /* call CoFileTime to get the current time
2216 newEntry.ctime
2217 newEntry.mtime
2220 /* newEntry.clsid */
2223 * Create a new directory entry for the storage
2225 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2226 if (FAILED(hr))
2227 return hr;
2230 * Insert the new directory entry into the parent storage's tree
2232 hr = insertIntoTree(
2233 This,
2234 This->storageDirEntry,
2235 newEntryRef);
2236 if (FAILED(hr))
2238 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2239 return hr;
2243 * Open it to get a pointer to return.
2245 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2247 if( (hr != S_OK) || (*ppstg == NULL))
2249 return hr;
2252 if (SUCCEEDED(hr))
2253 hr = StorageBaseImpl_Flush(This);
2255 return S_OK;
2258 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2259 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2260 SNB snbExclude, IStorage *pstgDest)
2262 DirEntry data;
2263 HRESULT hr;
2265 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2267 if (SUCCEEDED(hr))
2268 hr = IStorage_SetClass( pstgDest, &data.clsid );
2270 if (SUCCEEDED(hr))
2271 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2272 skip_stream, snbExclude, pstgDest );
2274 TRACE("<-- %#lx\n", hr);
2275 return hr;
2278 /*************************************************************************
2279 * CopyTo (IStorage)
2281 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2282 IStorage* iface,
2283 DWORD ciidExclude, /* [in] */
2284 const IID* rgiidExclude, /* [size_is][unique][in] */
2285 SNB snbExclude, /* [unique][in] */
2286 IStorage* pstgDest) /* [unique][in] */
2288 StorageBaseImpl *This = impl_from_IStorage(iface);
2290 BOOL skip_storage = FALSE, skip_stream = FALSE;
2291 DWORD i;
2293 TRACE("%p, %ld, %p, %p, %p.\n", iface, ciidExclude, rgiidExclude, snbExclude, pstgDest);
2295 if ( pstgDest == 0 )
2296 return STG_E_INVALIDPOINTER;
2298 for(i = 0; i < ciidExclude; ++i)
2300 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2301 skip_storage = TRUE;
2302 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2303 skip_stream = TRUE;
2304 else
2305 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2308 if (!skip_storage)
2310 /* Give up early if it looks like this would be infinitely recursive.
2311 * Oddly enough, this includes some cases that aren't really recursive, like
2312 * copying to a transacted child. */
2313 IStorage *pstgDestAncestor = pstgDest;
2314 IStorage *pstgDestAncestorChild = NULL;
2316 /* Go up the chain from the destination until we find the source storage. */
2317 while (pstgDestAncestor != iface) {
2318 pstgDestAncestorChild = pstgDest;
2320 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2322 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2324 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2326 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2328 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2330 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2332 else
2333 break;
2336 if (pstgDestAncestor == iface)
2338 BOOL fail = TRUE;
2340 if (pstgDestAncestorChild && snbExclude)
2342 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2343 DirEntry data;
2344 WCHAR **snb = snbExclude;
2346 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2348 while ( *snb != NULL && fail )
2350 if ( wcscmp(data.name, *snb) == 0 )
2351 fail = FALSE;
2352 ++snb;
2356 if (fail)
2357 return STG_E_ACCESSDENIED;
2361 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2362 skip_storage, skip_stream, snbExclude, pstgDest );
2365 /*************************************************************************
2366 * MoveElementTo (IStorage)
2368 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2369 IStorage* iface,
2370 const OLECHAR *pwcsName, /* [string][in] */
2371 IStorage *pstgDest, /* [unique][in] */
2372 const OLECHAR *pwcsNewName,/* [string][in] */
2373 DWORD grfFlags) /* [in] */
2375 FIXME("%p, %s, %p, %s, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest,
2376 debugstr_w(pwcsNewName), grfFlags);
2377 return E_NOTIMPL;
2380 /*************************************************************************
2381 * Commit (IStorage)
2383 * Ensures that any changes made to a storage object open in transacted mode
2384 * are reflected in the parent storage
2386 * In a non-transacted mode, this ensures all cached writes are completed.
2388 static HRESULT WINAPI StorageBaseImpl_Commit(
2389 IStorage* iface,
2390 DWORD grfCommitFlags)/* [in] */
2392 StorageBaseImpl* This = impl_from_IStorage(iface);
2393 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
2394 return StorageBaseImpl_Flush(This);
2397 /*************************************************************************
2398 * Revert (IStorage)
2400 * Discard all changes that have been made since the last commit operation
2402 static HRESULT WINAPI StorageBaseImpl_Revert(
2403 IStorage* iface)
2405 TRACE("(%p)\n", iface);
2406 return S_OK;
2409 /*********************************************************************
2411 * Internal helper function for StorageBaseImpl_DestroyElement()
2413 * Delete the contents of a storage entry.
2416 static HRESULT deleteStorageContents(
2417 StorageBaseImpl *parentStorage,
2418 DirRef indexToDelete,
2419 DirEntry entryDataToDelete)
2421 IEnumSTATSTG *elements = 0;
2422 IStorage *childStorage = 0;
2423 STATSTG currentElement;
2424 HRESULT hr;
2425 HRESULT destroyHr = S_OK;
2426 StorageInternalImpl *stg, *stg2;
2428 TRACE("%p, %ld.\n", parentStorage, indexToDelete);
2430 /* Invalidate any open storage objects. */
2431 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2433 if (stg->base.storageDirEntry == indexToDelete)
2435 StorageBaseImpl_Invalidate(&stg->base);
2440 * Open the storage and enumerate it
2442 hr = IStorage_OpenStorage(
2443 &parentStorage->IStorage_iface,
2444 entryDataToDelete.name,
2446 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2449 &childStorage);
2451 if (hr != S_OK)
2453 TRACE("<-- %#lx\n", hr);
2454 return hr;
2458 * Enumerate the elements
2460 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2461 if (FAILED(hr))
2463 IStorage_Release(childStorage);
2464 TRACE("<-- %#lx\n", hr);
2465 return hr;
2471 * Obtain the next element
2473 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2474 if (hr==S_OK)
2476 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2478 CoTaskMemFree(currentElement.pwcsName);
2482 * We need to Reset the enumeration every time because we delete elements
2483 * and the enumeration could be invalid
2485 IEnumSTATSTG_Reset(elements);
2487 } while ((hr == S_OK) && (destroyHr == S_OK));
2489 IStorage_Release(childStorage);
2490 IEnumSTATSTG_Release(elements);
2492 TRACE("%#lx\n", hr);
2493 return destroyHr;
2496 /*********************************************************************
2498 * Internal helper function for StorageBaseImpl_DestroyElement()
2500 * Perform the deletion of a stream's data
2503 static HRESULT deleteStreamContents(
2504 StorageBaseImpl *parentStorage,
2505 DirRef indexToDelete,
2506 DirEntry entryDataToDelete)
2508 IStream *pis;
2509 HRESULT hr;
2510 ULARGE_INTEGER size;
2511 StgStreamImpl *strm, *strm2;
2513 /* Invalidate any open stream objects. */
2514 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2516 if (strm->dirEntry == indexToDelete)
2518 TRACE("Stream deleted %p\n", strm);
2519 strm->parentStorage = NULL;
2520 list_remove(&strm->StrmListEntry);
2524 size.u.HighPart = 0;
2525 size.u.LowPart = 0;
2527 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2528 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2530 if (hr!=S_OK)
2532 TRACE("<-- %#lx\n", hr);
2533 return(hr);
2537 * Zap the stream
2539 hr = IStream_SetSize(pis, size);
2541 if(hr != S_OK)
2543 TRACE("<-- %#lx\n", hr);
2544 return hr;
2548 * Release the stream object.
2550 IStream_Release(pis);
2551 TRACE("<-- %#lx\n", hr);
2552 return S_OK;
2555 /*************************************************************************
2556 * DestroyElement (IStorage)
2558 * Strategy: This implementation is built this way for simplicity not for speed.
2559 * I always delete the topmost element of the enumeration and adjust
2560 * the deleted element pointer all the time. This takes longer to
2561 * do but allows reinvoking DestroyElement whenever we encounter a
2562 * storage object. The optimisation resides in the usage of another
2563 * enumeration strategy that would give all the leaves of a storage
2564 * first. (postfix order)
2566 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2567 IStorage* iface,
2568 const OLECHAR *pwcsName)/* [string][in] */
2570 StorageBaseImpl *This = impl_from_IStorage(iface);
2572 HRESULT hr = S_OK;
2573 DirEntry entryToDelete;
2574 DirRef entryToDeleteRef;
2576 TRACE("(%p, %s)\n",
2577 iface, debugstr_w(pwcsName));
2579 if (pwcsName==NULL)
2580 return STG_E_INVALIDPOINTER;
2582 if (This->reverted)
2583 return STG_E_REVERTED;
2585 if ( !(This->openFlags & STGM_TRANSACTED) &&
2586 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2587 return STG_E_ACCESSDENIED;
2589 entryToDeleteRef = findElement(
2590 This,
2591 This->storageDirEntry,
2592 pwcsName,
2593 &entryToDelete);
2595 if ( entryToDeleteRef == DIRENTRY_NULL )
2597 TRACE("<-- STG_E_FILENOTFOUND\n");
2598 return STG_E_FILENOTFOUND;
2601 if ( entryToDelete.stgType == STGTY_STORAGE )
2603 hr = deleteStorageContents(
2604 This,
2605 entryToDeleteRef,
2606 entryToDelete);
2608 else if ( entryToDelete.stgType == STGTY_STREAM )
2610 hr = deleteStreamContents(
2611 This,
2612 entryToDeleteRef,
2613 entryToDelete);
2616 if (hr!=S_OK)
2618 TRACE("<-- %#lx\n", hr);
2619 return hr;
2623 * Remove the entry from its parent storage
2625 hr = removeFromTree(
2626 This,
2627 This->storageDirEntry,
2628 entryToDeleteRef);
2631 * Invalidate the entry
2633 if (SUCCEEDED(hr))
2634 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2636 if (SUCCEEDED(hr))
2637 hr = StorageBaseImpl_Flush(This);
2639 TRACE("<-- %#lx\n", hr);
2640 return hr;
2643 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2645 struct list *cur, *cur2;
2646 StgStreamImpl *strm=NULL;
2647 StorageInternalImpl *childstg=NULL;
2649 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2650 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2651 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2652 strm->parentStorage = NULL;
2653 list_remove(cur);
2656 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2657 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2658 StorageBaseImpl_Invalidate( &childstg->base );
2661 if (stg->transactedChild)
2663 StorageBaseImpl_Invalidate(stg->transactedChild);
2665 stg->transactedChild = NULL;
2669 /******************************************************************************
2670 * SetElementTimes (IStorage)
2672 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2673 IStorage* iface,
2674 const OLECHAR *pwcsName,/* [string][in] */
2675 const FILETIME *pctime, /* [in] */
2676 const FILETIME *patime, /* [in] */
2677 const FILETIME *pmtime) /* [in] */
2679 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2680 return S_OK;
2683 /******************************************************************************
2684 * SetStateBits (IStorage)
2686 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2687 IStorage* iface,
2688 DWORD grfStateBits,/* [in] */
2689 DWORD grfMask) /* [in] */
2691 StorageBaseImpl *This = impl_from_IStorage(iface);
2693 if (This->reverted)
2694 return STG_E_REVERTED;
2696 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2697 return S_OK;
2700 /******************************************************************************
2701 * Internal stream list handlers
2704 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2706 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2707 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2710 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2712 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2713 list_remove(&(strm->StrmListEntry));
2716 static HRESULT StorageBaseImpl_CopyStream(
2717 StorageBaseImpl *dst, DirRef dst_entry,
2718 StorageBaseImpl *src, DirRef src_entry)
2720 HRESULT hr;
2721 BYTE data[4096];
2722 DirEntry srcdata;
2723 ULARGE_INTEGER bytes_copied;
2724 ULONG bytestocopy, bytesread, byteswritten;
2726 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2728 if (SUCCEEDED(hr))
2730 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2732 bytes_copied.QuadPart = 0;
2733 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2735 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2737 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2738 data, &bytesread);
2739 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2741 if (SUCCEEDED(hr))
2742 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2743 data, &byteswritten);
2744 if (SUCCEEDED(hr))
2746 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2747 bytes_copied.QuadPart += byteswritten;
2752 return hr;
2755 static HRESULT StorageBaseImpl_DupStorageTree(
2756 StorageBaseImpl *dst, DirRef *dst_entry,
2757 StorageBaseImpl *src, DirRef src_entry)
2759 HRESULT hr;
2760 DirEntry data;
2761 BOOL has_stream=FALSE;
2763 if (src_entry == DIRENTRY_NULL)
2765 *dst_entry = DIRENTRY_NULL;
2766 return S_OK;
2769 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2770 if (SUCCEEDED(hr))
2772 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2773 data.startingBlock = BLOCK_END_OF_CHAIN;
2774 data.size.QuadPart = 0;
2776 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2779 if (SUCCEEDED(hr))
2780 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2782 if (SUCCEEDED(hr))
2783 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2785 if (SUCCEEDED(hr))
2786 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2788 if (SUCCEEDED(hr) && has_stream)
2789 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2791 return hr;
2794 static HRESULT StorageBaseImpl_CopyStorageTree(
2795 StorageBaseImpl *dst, DirRef dst_entry,
2796 StorageBaseImpl *src, DirRef src_entry)
2798 HRESULT hr;
2799 DirEntry src_data, dst_data;
2800 DirRef new_root_entry;
2802 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2804 if (SUCCEEDED(hr))
2806 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2809 if (SUCCEEDED(hr))
2811 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
2812 dst_data.clsid = src_data.clsid;
2813 dst_data.ctime = src_data.ctime;
2814 dst_data.mtime = src_data.mtime;
2815 dst_data.dirRootEntry = new_root_entry;
2818 if (SUCCEEDED(hr))
2819 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2821 return hr;
2824 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2826 HRESULT hr;
2827 DirEntry data;
2828 ULARGE_INTEGER zero;
2830 if (entry == DIRENTRY_NULL)
2831 return S_OK;
2833 zero.QuadPart = 0;
2835 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2837 if (SUCCEEDED(hr) && include_siblings)
2838 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2840 if (SUCCEEDED(hr) && include_siblings)
2841 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2843 if (SUCCEEDED(hr))
2844 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2846 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2847 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2849 if (SUCCEEDED(hr))
2850 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2852 return hr;
2856 /************************************************************************
2857 * StorageImpl implementation
2858 ***********************************************************************/
2860 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2861 ULARGE_INTEGER offset,
2862 void* buffer,
2863 ULONG size,
2864 ULONG* bytesRead)
2866 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2869 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2870 ULARGE_INTEGER offset,
2871 const void* buffer,
2872 const ULONG size,
2873 ULONG* bytesWritten)
2875 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2878 /******************************************************************************
2879 * StorageImpl_LoadFileHeader
2881 * This method will read in the file header
2883 static HRESULT StorageImpl_LoadFileHeader(
2884 StorageImpl* This)
2886 HRESULT hr;
2887 BYTE headerBigBlock[HEADER_SIZE];
2888 int index;
2889 ULARGE_INTEGER offset;
2890 DWORD bytes_read;
2892 TRACE("\n");
2894 * Get a pointer to the big block of data containing the header.
2896 offset.u.HighPart = 0;
2897 offset.u.LowPart = 0;
2898 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2899 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2900 hr = STG_E_FILENOTFOUND;
2903 * Extract the information from the header.
2905 if (SUCCEEDED(hr))
2908 * Check for the "magic number" signature and return an error if it is not
2909 * found.
2911 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2913 return STG_E_OLDFORMAT;
2916 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2918 return STG_E_INVALIDHEADER;
2921 StorageUtl_ReadWord(
2922 headerBigBlock,
2923 OFFSET_BIGBLOCKSIZEBITS,
2924 &This->bigBlockSizeBits);
2926 StorageUtl_ReadWord(
2927 headerBigBlock,
2928 OFFSET_SMALLBLOCKSIZEBITS,
2929 &This->smallBlockSizeBits);
2931 StorageUtl_ReadDWord(
2932 headerBigBlock,
2933 OFFSET_BBDEPOTCOUNT,
2934 &This->bigBlockDepotCount);
2936 StorageUtl_ReadDWord(
2937 headerBigBlock,
2938 OFFSET_ROOTSTARTBLOCK,
2939 &This->rootStartBlock);
2941 StorageUtl_ReadDWord(
2942 headerBigBlock,
2943 OFFSET_TRANSACTIONSIG,
2944 &This->transactionSig);
2946 StorageUtl_ReadDWord(
2947 headerBigBlock,
2948 OFFSET_SMALLBLOCKLIMIT,
2949 &This->smallBlockLimit);
2951 StorageUtl_ReadDWord(
2952 headerBigBlock,
2953 OFFSET_SBDEPOTSTART,
2954 &This->smallBlockDepotStart);
2956 StorageUtl_ReadDWord(
2957 headerBigBlock,
2958 OFFSET_EXTBBDEPOTSTART,
2959 &This->extBigBlockDepotStart);
2961 StorageUtl_ReadDWord(
2962 headerBigBlock,
2963 OFFSET_EXTBBDEPOTCOUNT,
2964 &This->extBigBlockDepotCount);
2966 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2968 StorageUtl_ReadDWord(
2969 headerBigBlock,
2970 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2971 &(This->bigBlockDepotStart[index]));
2975 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2977 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2978 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2981 * Right now, the code is making some assumptions about the size of the
2982 * blocks, just make sure they are what we're expecting.
2984 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2985 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2986 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2988 FIXME("Broken OLE storage file? bigblock=%#lx, smallblock=%#lx, sblimit=%#lx\n",
2989 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
2990 hr = STG_E_INVALIDHEADER;
2992 else
2993 hr = S_OK;
2996 return hr;
2999 /******************************************************************************
3000 * StorageImpl_SaveFileHeader
3002 * This method will save to the file the header
3004 static void StorageImpl_SaveFileHeader(
3005 StorageImpl* This)
3007 BYTE headerBigBlock[HEADER_SIZE];
3008 int index;
3009 ULARGE_INTEGER offset;
3010 DWORD bytes_written;
3011 DWORD major_version, dirsectorcount;
3013 if (This->bigBlockSizeBits == 0x9)
3014 major_version = 3;
3015 else if (This->bigBlockSizeBits == 0xc)
3016 major_version = 4;
3017 else
3019 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3020 major_version = 4;
3023 memset(headerBigBlock, 0, HEADER_SIZE);
3024 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3027 * Write the information to the header.
3029 StorageUtl_WriteWord(
3030 headerBigBlock,
3031 OFFSET_MINORVERSION,
3032 0x3e);
3034 StorageUtl_WriteWord(
3035 headerBigBlock,
3036 OFFSET_MAJORVERSION,
3037 major_version);
3039 StorageUtl_WriteWord(
3040 headerBigBlock,
3041 OFFSET_BYTEORDERMARKER,
3042 (WORD)-2);
3044 StorageUtl_WriteWord(
3045 headerBigBlock,
3046 OFFSET_BIGBLOCKSIZEBITS,
3047 This->bigBlockSizeBits);
3049 StorageUtl_WriteWord(
3050 headerBigBlock,
3051 OFFSET_SMALLBLOCKSIZEBITS,
3052 This->smallBlockSizeBits);
3054 if (major_version >= 4)
3056 if (This->rootBlockChain)
3057 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3058 else
3059 /* This file is being created, and it will start out with one block. */
3060 dirsectorcount = 1;
3062 else
3063 /* This field must be 0 in versions older than 4 */
3064 dirsectorcount = 0;
3066 StorageUtl_WriteDWord(
3067 headerBigBlock,
3068 OFFSET_DIRSECTORCOUNT,
3069 dirsectorcount);
3071 StorageUtl_WriteDWord(
3072 headerBigBlock,
3073 OFFSET_BBDEPOTCOUNT,
3074 This->bigBlockDepotCount);
3076 StorageUtl_WriteDWord(
3077 headerBigBlock,
3078 OFFSET_ROOTSTARTBLOCK,
3079 This->rootStartBlock);
3081 StorageUtl_WriteDWord(
3082 headerBigBlock,
3083 OFFSET_TRANSACTIONSIG,
3084 This->transactionSig);
3086 StorageUtl_WriteDWord(
3087 headerBigBlock,
3088 OFFSET_SMALLBLOCKLIMIT,
3089 This->smallBlockLimit);
3091 StorageUtl_WriteDWord(
3092 headerBigBlock,
3093 OFFSET_SBDEPOTSTART,
3094 This->smallBlockDepotStart);
3096 StorageUtl_WriteDWord(
3097 headerBigBlock,
3098 OFFSET_SBDEPOTCOUNT,
3099 This->smallBlockDepotChain ?
3100 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3102 StorageUtl_WriteDWord(
3103 headerBigBlock,
3104 OFFSET_EXTBBDEPOTSTART,
3105 This->extBigBlockDepotStart);
3107 StorageUtl_WriteDWord(
3108 headerBigBlock,
3109 OFFSET_EXTBBDEPOTCOUNT,
3110 This->extBigBlockDepotCount);
3112 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3114 StorageUtl_WriteDWord(
3115 headerBigBlock,
3116 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3117 (This->bigBlockDepotStart[index]));
3120 offset.QuadPart = 0;
3121 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3125 /************************************************************************
3126 * StorageImpl implementation : DirEntry methods
3127 ***********************************************************************/
3129 /******************************************************************************
3130 * StorageImpl_ReadRawDirEntry
3132 * This method will read the raw data from a directory entry in the file.
3134 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3136 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3138 ULARGE_INTEGER offset;
3139 HRESULT hr;
3140 ULONG bytesRead;
3142 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3144 hr = BlockChainStream_ReadAt(
3145 This->rootBlockChain,
3146 offset,
3147 RAW_DIRENTRY_SIZE,
3148 buffer,
3149 &bytesRead);
3151 if (bytesRead != RAW_DIRENTRY_SIZE)
3152 return STG_E_READFAULT;
3154 return hr;
3157 /******************************************************************************
3158 * StorageImpl_WriteRawDirEntry
3160 * This method will write the raw data from a directory entry in the file.
3162 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3164 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3166 ULARGE_INTEGER offset;
3167 ULONG bytesRead;
3169 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3171 return BlockChainStream_WriteAt(
3172 This->rootBlockChain,
3173 offset,
3174 RAW_DIRENTRY_SIZE,
3175 buffer,
3176 &bytesRead);
3179 /***************************************************************************
3181 * Internal Method
3183 * Mark a directory entry in the file as free.
3185 static HRESULT StorageImpl_DestroyDirEntry(
3186 StorageBaseImpl *base,
3187 DirRef index)
3189 BYTE emptyData[RAW_DIRENTRY_SIZE];
3190 StorageImpl *storage = (StorageImpl*)base;
3192 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3194 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3197 /******************************************************************************
3198 * UpdateRawDirEntry
3200 * Update raw directory entry data from the fields in newData.
3202 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3204 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3206 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3208 memcpy(
3209 buffer + OFFSET_PS_NAME,
3210 newData->name,
3211 DIRENTRY_NAME_BUFFER_LEN );
3213 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3215 StorageUtl_WriteWord(
3216 buffer,
3217 OFFSET_PS_NAMELENGTH,
3218 newData->sizeOfNameString);
3220 StorageUtl_WriteDWord(
3221 buffer,
3222 OFFSET_PS_LEFTCHILD,
3223 newData->leftChild);
3225 StorageUtl_WriteDWord(
3226 buffer,
3227 OFFSET_PS_RIGHTCHILD,
3228 newData->rightChild);
3230 StorageUtl_WriteDWord(
3231 buffer,
3232 OFFSET_PS_DIRROOT,
3233 newData->dirRootEntry);
3235 StorageUtl_WriteGUID(
3236 buffer,
3237 OFFSET_PS_GUID,
3238 &newData->clsid);
3240 StorageUtl_WriteDWord(
3241 buffer,
3242 OFFSET_PS_CTIMELOW,
3243 newData->ctime.dwLowDateTime);
3245 StorageUtl_WriteDWord(
3246 buffer,
3247 OFFSET_PS_CTIMEHIGH,
3248 newData->ctime.dwHighDateTime);
3250 StorageUtl_WriteDWord(
3251 buffer,
3252 OFFSET_PS_MTIMELOW,
3253 newData->mtime.dwLowDateTime);
3255 StorageUtl_WriteDWord(
3256 buffer,
3257 OFFSET_PS_MTIMEHIGH,
3258 newData->ctime.dwHighDateTime);
3260 StorageUtl_WriteDWord(
3261 buffer,
3262 OFFSET_PS_STARTBLOCK,
3263 newData->startingBlock);
3265 StorageUtl_WriteDWord(
3266 buffer,
3267 OFFSET_PS_SIZE,
3268 newData->size.u.LowPart);
3270 StorageUtl_WriteDWord(
3271 buffer,
3272 OFFSET_PS_SIZE_HIGH,
3273 newData->size.u.HighPart);
3276 /***************************************************************************
3278 * Internal Method
3280 * Reserve a directory entry in the file and initialize it.
3282 static HRESULT StorageImpl_CreateDirEntry(
3283 StorageBaseImpl *base,
3284 const DirEntry *newData,
3285 DirRef *index)
3287 StorageImpl *storage = (StorageImpl*)base;
3288 ULONG currentEntryIndex = 0;
3289 ULONG newEntryIndex = DIRENTRY_NULL;
3290 HRESULT hr = S_OK;
3291 BYTE currentData[RAW_DIRENTRY_SIZE];
3292 WORD sizeOfNameString;
3296 hr = StorageImpl_ReadRawDirEntry(storage,
3297 currentEntryIndex,
3298 currentData);
3300 if (SUCCEEDED(hr))
3302 StorageUtl_ReadWord(
3303 currentData,
3304 OFFSET_PS_NAMELENGTH,
3305 &sizeOfNameString);
3307 if (sizeOfNameString == 0)
3310 * The entry exists and is available, we found it.
3312 newEntryIndex = currentEntryIndex;
3315 else
3318 * We exhausted the directory entries, we will create more space below
3320 newEntryIndex = currentEntryIndex;
3322 currentEntryIndex++;
3324 } while (newEntryIndex == DIRENTRY_NULL);
3327 * grow the directory stream
3329 if (FAILED(hr))
3331 BYTE emptyData[RAW_DIRENTRY_SIZE];
3332 ULARGE_INTEGER newSize;
3333 ULONG entryIndex;
3334 ULONG lastEntry = 0;
3335 ULONG blockCount = 0;
3338 * obtain the new count of blocks in the directory stream
3340 blockCount = BlockChainStream_GetCount(
3341 storage->rootBlockChain)+1;
3344 * initialize the size used by the directory stream
3346 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3349 * add a block to the directory stream
3351 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3354 * memset the empty entry in order to initialize the unused newly
3355 * created entries
3357 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3360 * initialize them
3362 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3364 for(
3365 entryIndex = newEntryIndex + 1;
3366 entryIndex < lastEntry;
3367 entryIndex++)
3369 StorageImpl_WriteRawDirEntry(
3370 storage,
3371 entryIndex,
3372 emptyData);
3375 StorageImpl_SaveFileHeader(storage);
3378 UpdateRawDirEntry(currentData, newData);
3380 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3382 if (SUCCEEDED(hr))
3383 *index = newEntryIndex;
3385 return hr;
3388 /******************************************************************************
3389 * StorageImpl_ReadDirEntry
3391 * This method will read the specified directory entry.
3393 static HRESULT StorageImpl_ReadDirEntry(
3394 StorageImpl* This,
3395 DirRef index,
3396 DirEntry* buffer)
3398 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3399 HRESULT readRes;
3401 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3403 if (SUCCEEDED(readRes))
3405 memset(buffer->name, 0, sizeof(buffer->name));
3406 memcpy(
3407 buffer->name,
3408 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3409 DIRENTRY_NAME_BUFFER_LEN );
3410 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3412 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3414 StorageUtl_ReadWord(
3415 currentEntry,
3416 OFFSET_PS_NAMELENGTH,
3417 &buffer->sizeOfNameString);
3419 StorageUtl_ReadDWord(
3420 currentEntry,
3421 OFFSET_PS_LEFTCHILD,
3422 &buffer->leftChild);
3424 StorageUtl_ReadDWord(
3425 currentEntry,
3426 OFFSET_PS_RIGHTCHILD,
3427 &buffer->rightChild);
3429 StorageUtl_ReadDWord(
3430 currentEntry,
3431 OFFSET_PS_DIRROOT,
3432 &buffer->dirRootEntry);
3434 StorageUtl_ReadGUID(
3435 currentEntry,
3436 OFFSET_PS_GUID,
3437 &buffer->clsid);
3439 StorageUtl_ReadDWord(
3440 currentEntry,
3441 OFFSET_PS_CTIMELOW,
3442 &buffer->ctime.dwLowDateTime);
3444 StorageUtl_ReadDWord(
3445 currentEntry,
3446 OFFSET_PS_CTIMEHIGH,
3447 &buffer->ctime.dwHighDateTime);
3449 StorageUtl_ReadDWord(
3450 currentEntry,
3451 OFFSET_PS_MTIMELOW,
3452 &buffer->mtime.dwLowDateTime);
3454 StorageUtl_ReadDWord(
3455 currentEntry,
3456 OFFSET_PS_MTIMEHIGH,
3457 &buffer->mtime.dwHighDateTime);
3459 StorageUtl_ReadDWord(
3460 currentEntry,
3461 OFFSET_PS_STARTBLOCK,
3462 &buffer->startingBlock);
3464 StorageUtl_ReadDWord(
3465 currentEntry,
3466 OFFSET_PS_SIZE,
3467 &buffer->size.u.LowPart);
3469 if (This->bigBlockSize < 4096)
3471 /* Version 3 files may have junk in the high part of size. */
3472 buffer->size.u.HighPart = 0;
3474 else
3476 StorageUtl_ReadDWord(
3477 currentEntry,
3478 OFFSET_PS_SIZE_HIGH,
3479 &buffer->size.u.HighPart);
3483 return readRes;
3486 /*********************************************************************
3487 * Write the specified directory entry to the file
3489 static HRESULT StorageImpl_WriteDirEntry(
3490 StorageImpl* This,
3491 DirRef index,
3492 const DirEntry* buffer)
3494 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3496 UpdateRawDirEntry(currentEntry, buffer);
3498 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3502 /************************************************************************
3503 * StorageImpl implementation : Block methods
3504 ***********************************************************************/
3506 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3508 return (ULONGLONG)(index+1) * This->bigBlockSize;
3511 static HRESULT StorageImpl_ReadBigBlock(
3512 StorageImpl* This,
3513 ULONG blockIndex,
3514 void* buffer,
3515 ULONG* out_read)
3517 ULARGE_INTEGER ulOffset;
3518 DWORD read=0;
3519 HRESULT hr;
3521 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3523 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3525 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3527 /* File ends during this block; fill the rest with 0's. */
3528 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3531 if (out_read) *out_read = read;
3533 return hr;
3536 static BOOL StorageImpl_ReadDWordFromBigBlock(
3537 StorageImpl* This,
3538 ULONG blockIndex,
3539 ULONG offset,
3540 DWORD* value)
3542 ULARGE_INTEGER ulOffset;
3543 DWORD read;
3544 DWORD tmp;
3546 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3547 ulOffset.QuadPart += offset;
3549 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3550 *value = lendian32toh(tmp);
3551 return (read == sizeof(DWORD));
3554 static BOOL StorageImpl_WriteBigBlock(
3555 StorageImpl* This,
3556 ULONG blockIndex,
3557 const void* buffer)
3559 ULARGE_INTEGER ulOffset;
3560 DWORD wrote;
3562 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3564 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3565 return (wrote == This->bigBlockSize);
3568 static BOOL StorageImpl_WriteDWordToBigBlock(
3569 StorageImpl* This,
3570 ULONG blockIndex,
3571 ULONG offset,
3572 DWORD value)
3574 ULARGE_INTEGER ulOffset;
3575 DWORD wrote;
3577 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3578 ulOffset.QuadPart += offset;
3580 value = htole32(value);
3581 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3582 return (wrote == sizeof(DWORD));
3585 /******************************************************************************
3586 * Storage32Impl_SmallBlocksToBigBlocks
3588 * This method will convert a small block chain to a big block chain.
3589 * The small block chain will be destroyed.
3591 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3592 StorageImpl* This,
3593 SmallBlockChainStream** ppsbChain)
3595 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3596 ULARGE_INTEGER size, offset;
3597 ULONG cbRead, cbWritten;
3598 ULARGE_INTEGER cbTotalRead;
3599 DirRef streamEntryRef;
3600 HRESULT resWrite = S_OK;
3601 HRESULT resRead;
3602 DirEntry streamEntry;
3603 BYTE *buffer;
3604 BlockChainStream *bbTempChain = NULL;
3605 BlockChainStream *bigBlockChain = NULL;
3608 * Create a temporary big block chain that doesn't have
3609 * an associated directory entry. This temporary chain will be
3610 * used to copy data from small blocks to big blocks.
3612 bbTempChain = BlockChainStream_Construct(This,
3613 &bbHeadOfChain,
3614 DIRENTRY_NULL);
3615 if(!bbTempChain) return NULL;
3617 * Grow the big block chain.
3619 size = SmallBlockChainStream_GetSize(*ppsbChain);
3620 BlockChainStream_SetSize(bbTempChain, size);
3623 * Copy the contents of the small block chain to the big block chain
3624 * by small block size increments.
3626 offset.u.LowPart = 0;
3627 offset.u.HighPart = 0;
3628 cbTotalRead.QuadPart = 0;
3630 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3633 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3634 offset,
3635 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3636 buffer,
3637 &cbRead);
3638 if (FAILED(resRead))
3639 break;
3641 if (cbRead > 0)
3643 cbTotalRead.QuadPart += cbRead;
3645 resWrite = BlockChainStream_WriteAt(bbTempChain,
3646 offset,
3647 cbRead,
3648 buffer,
3649 &cbWritten);
3651 if (FAILED(resWrite))
3652 break;
3654 offset.u.LowPart += cbRead;
3656 else
3658 resRead = STG_E_READFAULT;
3659 break;
3661 } while (cbTotalRead.QuadPart < size.QuadPart);
3662 HeapFree(GetProcessHeap(),0,buffer);
3664 size.u.HighPart = 0;
3665 size.u.LowPart = 0;
3667 if (FAILED(resRead) || FAILED(resWrite))
3669 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite);
3670 BlockChainStream_SetSize(bbTempChain, size);
3671 BlockChainStream_Destroy(bbTempChain);
3672 return NULL;
3676 * Destroy the small block chain.
3678 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3679 SmallBlockChainStream_SetSize(*ppsbChain, size);
3680 SmallBlockChainStream_Destroy(*ppsbChain);
3681 *ppsbChain = 0;
3684 * Change the directory entry. This chain is now a big block chain
3685 * and it doesn't reside in the small blocks chain anymore.
3687 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3689 streamEntry.startingBlock = bbHeadOfChain;
3691 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3694 * Destroy the temporary entryless big block chain.
3695 * Create a new big block chain associated with this entry.
3697 BlockChainStream_Destroy(bbTempChain);
3698 bigBlockChain = BlockChainStream_Construct(This,
3699 NULL,
3700 streamEntryRef);
3702 return bigBlockChain;
3705 /******************************************************************************
3706 * Storage32Impl_BigBlocksToSmallBlocks
3708 * This method will convert a big block chain to a small block chain.
3709 * The big block chain will be destroyed on success.
3711 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3712 StorageImpl* This,
3713 BlockChainStream** ppbbChain,
3714 ULARGE_INTEGER newSize)
3716 ULARGE_INTEGER size, offset, cbTotalRead;
3717 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3718 DirRef streamEntryRef;
3719 HRESULT resWrite = S_OK, resRead = S_OK;
3720 DirEntry streamEntry;
3721 BYTE* buffer;
3722 SmallBlockChainStream* sbTempChain;
3724 TRACE("%p %p\n", This, ppbbChain);
3726 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3727 DIRENTRY_NULL);
3729 if(!sbTempChain)
3730 return NULL;
3732 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3733 size = BlockChainStream_GetSize(*ppbbChain);
3734 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3736 offset.u.HighPart = 0;
3737 offset.u.LowPart = 0;
3738 cbTotalRead.QuadPart = 0;
3739 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3740 while(cbTotalRead.QuadPart < size.QuadPart)
3742 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3743 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3744 buffer, &cbRead);
3746 if(FAILED(resRead))
3747 break;
3749 if(cbRead > 0)
3751 cbTotalRead.QuadPart += cbRead;
3753 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3754 cbRead, buffer, &cbWritten);
3756 if(FAILED(resWrite))
3757 break;
3759 offset.u.LowPart += cbRead;
3761 else
3763 resRead = STG_E_READFAULT;
3764 break;
3767 HeapFree(GetProcessHeap(), 0, buffer);
3769 size.u.HighPart = 0;
3770 size.u.LowPart = 0;
3772 if(FAILED(resRead) || FAILED(resWrite))
3774 ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite);
3775 SmallBlockChainStream_SetSize(sbTempChain, size);
3776 SmallBlockChainStream_Destroy(sbTempChain);
3777 return NULL;
3780 /* destroy the original big block chain */
3781 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3782 BlockChainStream_SetSize(*ppbbChain, size);
3783 BlockChainStream_Destroy(*ppbbChain);
3784 *ppbbChain = NULL;
3786 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3787 streamEntry.startingBlock = sbHeadOfChain;
3788 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3790 SmallBlockChainStream_Destroy(sbTempChain);
3791 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3794 /******************************************************************************
3795 * Storage32Impl_AddBlockDepot
3797 * This will create a depot block, essentially it is a block initialized
3798 * to BLOCK_UNUSEDs.
3800 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3802 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3803 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3804 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3805 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3808 * Initialize blocks as free
3810 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3812 /* Reserve the range lock sector */
3813 if (depotIndex == rangeLockDepot)
3815 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3818 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3821 /******************************************************************************
3822 * Storage32Impl_GetExtDepotBlock
3824 * Returns the index of the block that corresponds to the specified depot
3825 * index. This method is only for depot indexes equal or greater than
3826 * COUNT_BBDEPOTINHEADER.
3828 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3830 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3831 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3832 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3833 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3834 ULONG blockIndex = BLOCK_UNUSED;
3835 ULONG extBlockIndex;
3836 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3837 int index, num_blocks;
3839 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3841 if (extBlockCount >= This->extBigBlockDepotCount)
3842 return BLOCK_UNUSED;
3844 if (This->indexExtBlockDepotCached != extBlockCount)
3846 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3848 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3850 num_blocks = This->bigBlockSize / 4;
3852 for (index = 0; index < num_blocks; index++)
3854 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3855 This->extBlockDepotCached[index] = blockIndex;
3858 This->indexExtBlockDepotCached = extBlockCount;
3861 blockIndex = This->extBlockDepotCached[extBlockOffset];
3863 return blockIndex;
3866 /******************************************************************************
3867 * Storage32Impl_SetExtDepotBlock
3869 * Associates the specified block index to the specified depot index.
3870 * This method is only for depot indexes equal or greater than
3871 * COUNT_BBDEPOTINHEADER.
3873 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3875 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3876 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3877 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3878 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3879 ULONG extBlockIndex;
3881 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3883 assert(extBlockCount < This->extBigBlockDepotCount);
3885 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3887 if (extBlockIndex != BLOCK_UNUSED)
3889 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3890 extBlockOffset * sizeof(ULONG),
3891 blockIndex);
3894 if (This->indexExtBlockDepotCached == extBlockCount)
3896 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3900 /******************************************************************************
3901 * Storage32Impl_AddExtBlockDepot
3903 * Creates an extended depot block.
3905 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3907 ULONG numExtBlocks = This->extBigBlockDepotCount;
3908 ULONG nextExtBlock = This->extBigBlockDepotStart;
3909 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3910 ULONG index = BLOCK_UNUSED;
3911 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3912 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3913 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3915 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3916 blocksPerDepotBlock;
3918 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3921 * The first extended block.
3923 This->extBigBlockDepotStart = index;
3925 else
3928 * Find the last existing extended block.
3930 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3933 * Add the new extended block to the chain.
3935 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3936 index);
3940 * Initialize this block.
3942 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3943 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3945 /* Add the block to our cache. */
3946 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3948 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3949 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3951 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3952 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3954 This->extBigBlockDepotLocations = new_cache;
3955 This->extBigBlockDepotLocationsSize = new_cache_size;
3957 This->extBigBlockDepotLocations[numExtBlocks] = index;
3959 return index;
3962 /************************************************************************
3963 * StorageImpl_GetNextBlockInChain
3965 * This method will retrieve the block index of the next big block in
3966 * in the chain.
3968 * Params: This - Pointer to the Storage object.
3969 * blockIndex - Index of the block to retrieve the chain
3970 * for.
3971 * nextBlockIndex - receives the return value.
3973 * Returns: This method returns the index of the next block in the chain.
3974 * It will return the constants:
3975 * BLOCK_SPECIAL - If the block given was not part of a
3976 * chain.
3977 * BLOCK_END_OF_CHAIN - If the block given was the last in
3978 * a chain.
3979 * BLOCK_UNUSED - If the block given was not past of a chain
3980 * and is available.
3981 * BLOCK_EXTBBDEPOT - This block is part of the extended
3982 * big block depot.
3984 * See Windows documentation for more details on IStorage methods.
3986 static HRESULT StorageImpl_GetNextBlockInChain(
3987 StorageImpl* This,
3988 ULONG blockIndex,
3989 ULONG* nextBlockIndex)
3991 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3992 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3993 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3994 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3995 ULONG read;
3996 ULONG depotBlockIndexPos;
3997 int index, num_blocks;
3999 *nextBlockIndex = BLOCK_SPECIAL;
4001 if(depotBlockCount >= This->bigBlockDepotCount)
4003 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount, This->bigBlockDepotCount);
4004 return STG_E_READFAULT;
4008 * Cache the currently accessed depot block.
4010 if (depotBlockCount != This->indexBlockDepotCached)
4012 This->indexBlockDepotCached = depotBlockCount;
4014 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4016 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4018 else
4021 * We have to look in the extended depot.
4023 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4026 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4028 if (!read)
4029 return STG_E_READFAULT;
4031 num_blocks = This->bigBlockSize / 4;
4033 for (index = 0; index < num_blocks; index++)
4035 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4036 This->blockDepotCached[index] = *nextBlockIndex;
4040 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4042 return S_OK;
4045 /******************************************************************************
4046 * Storage32Impl_GetNextExtendedBlock
4048 * Given an extended block this method will return the next extended block.
4050 * NOTES:
4051 * The last ULONG of an extended block is the block index of the next
4052 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4053 * depot.
4055 * Return values:
4056 * - The index of the next extended block
4057 * - BLOCK_UNUSED: there is no next extended block.
4058 * - Any other return values denotes failure.
4060 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4062 ULONG nextBlockIndex = BLOCK_SPECIAL;
4063 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4065 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4066 &nextBlockIndex);
4068 return nextBlockIndex;
4071 /******************************************************************************
4072 * StorageImpl_SetNextBlockInChain
4074 * This method will write the index of the specified block's next block
4075 * in the big block depot.
4077 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4078 * do the following
4080 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4081 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4082 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4085 static void StorageImpl_SetNextBlockInChain(
4086 StorageImpl* This,
4087 ULONG blockIndex,
4088 ULONG nextBlock)
4090 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4091 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4092 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4093 ULONG depotBlockIndexPos;
4095 assert(depotBlockCount < This->bigBlockDepotCount);
4096 assert(blockIndex != nextBlock);
4098 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4099 /* This should never happen (storage file format spec forbids it), but
4100 * older versions of Wine may have generated broken files. We don't want to
4101 * assert and potentially lose data, but we do want to know if this ever
4102 * happens in a newly-created file. */
4103 ERR("Using range lock page\n");
4105 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4107 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4109 else
4112 * We have to look in the extended depot.
4114 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4117 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4118 nextBlock);
4120 * Update the cached block depot, if necessary.
4122 if (depotBlockCount == This->indexBlockDepotCached)
4124 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4128 /******************************************************************************
4129 * StorageImpl_GetNextFreeBigBlock
4131 * Returns the index of the next free big block.
4132 * If the big block depot is filled, this method will enlarge it.
4135 static ULONG StorageImpl_GetNextFreeBigBlock(
4136 StorageImpl* This, ULONG neededAddNumBlocks)
4138 ULONG depotBlockIndexPos;
4139 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4140 ULONG depotBlockOffset;
4141 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4142 ULONG nextBlockIndex = BLOCK_SPECIAL;
4143 int depotIndex = 0;
4144 ULONG freeBlock = BLOCK_UNUSED;
4145 ULONG read;
4146 ULARGE_INTEGER neededSize;
4147 STATSTG statstg;
4149 depotIndex = This->prevFreeBlock / blocksPerDepot;
4150 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4153 * Scan the entire big block depot until we find a block marked free
4155 while (nextBlockIndex != BLOCK_UNUSED)
4157 if (depotIndex < COUNT_BBDEPOTINHEADER)
4159 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4162 * Grow the primary depot.
4164 if (depotBlockIndexPos == BLOCK_UNUSED)
4166 depotBlockIndexPos = depotIndex*blocksPerDepot;
4169 * Add a block depot.
4171 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4172 This->bigBlockDepotCount++;
4173 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4176 * Flag it as a block depot.
4178 StorageImpl_SetNextBlockInChain(This,
4179 depotBlockIndexPos,
4180 BLOCK_SPECIAL);
4182 /* Save new header information.
4184 StorageImpl_SaveFileHeader(This);
4187 else
4189 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4191 if (depotBlockIndexPos == BLOCK_UNUSED)
4194 * Grow the extended depot.
4196 ULONG extIndex = BLOCK_UNUSED;
4197 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4198 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4200 if (extBlockOffset == 0)
4202 /* We need an extended block.
4204 extIndex = Storage32Impl_AddExtBlockDepot(This);
4205 This->extBigBlockDepotCount++;
4206 depotBlockIndexPos = extIndex + 1;
4208 else
4209 depotBlockIndexPos = depotIndex * blocksPerDepot;
4212 * Add a block depot and mark it in the extended block.
4214 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4215 This->bigBlockDepotCount++;
4216 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4218 /* Flag the block depot.
4220 StorageImpl_SetNextBlockInChain(This,
4221 depotBlockIndexPos,
4222 BLOCK_SPECIAL);
4224 /* If necessary, flag the extended depot block.
4226 if (extIndex != BLOCK_UNUSED)
4227 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4229 /* Save header information.
4231 StorageImpl_SaveFileHeader(This);
4235 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4237 if (read)
4239 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4240 ( nextBlockIndex != BLOCK_UNUSED))
4242 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4244 if (nextBlockIndex == BLOCK_UNUSED)
4246 freeBlock = (depotIndex * blocksPerDepot) +
4247 (depotBlockOffset/sizeof(ULONG));
4250 depotBlockOffset += sizeof(ULONG);
4254 depotIndex++;
4255 depotBlockOffset = 0;
4259 * make sure that the block physically exists before using it
4261 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize * neededAddNumBlocks;
4263 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4265 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4266 ILockBytes_SetSize(This->lockBytes, neededSize);
4268 This->prevFreeBlock = freeBlock;
4270 return freeBlock;
4273 /******************************************************************************
4274 * StorageImpl_FreeBigBlock
4276 * This method will flag the specified block as free in the big block depot.
4278 static void StorageImpl_FreeBigBlock(
4279 StorageImpl* This,
4280 ULONG blockIndex)
4282 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4284 if (blockIndex < This->prevFreeBlock)
4285 This->prevFreeBlock = blockIndex;
4289 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4290 DirRef index, const DirEntry *data)
4292 StorageImpl *This = (StorageImpl*)base;
4293 return StorageImpl_WriteDirEntry(This, index, data);
4296 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4297 DirRef index, DirEntry *data)
4299 StorageImpl *This = (StorageImpl*)base;
4300 return StorageImpl_ReadDirEntry(This, index, data);
4303 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4305 int i;
4307 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4309 if (!This->blockChainCache[i])
4311 return &This->blockChainCache[i];
4315 i = This->blockChainToEvict;
4317 BlockChainStream_Destroy(This->blockChainCache[i]);
4318 This->blockChainCache[i] = NULL;
4320 This->blockChainToEvict++;
4321 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4322 This->blockChainToEvict = 0;
4324 return &This->blockChainCache[i];
4327 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4328 DirRef index)
4330 int i, free_index=-1;
4332 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4334 if (!This->blockChainCache[i])
4336 if (free_index == -1) free_index = i;
4338 else if (This->blockChainCache[i]->ownerDirEntry == index)
4340 return &This->blockChainCache[i];
4344 if (free_index == -1)
4346 free_index = This->blockChainToEvict;
4348 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4349 This->blockChainCache[free_index] = NULL;
4351 This->blockChainToEvict++;
4352 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4353 This->blockChainToEvict = 0;
4356 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4357 return &This->blockChainCache[free_index];
4360 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4362 int i;
4364 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4366 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4368 BlockChainStream_Destroy(This->blockChainCache[i]);
4369 This->blockChainCache[i] = NULL;
4370 return;
4375 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4376 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4378 StorageImpl *This = (StorageImpl*)base;
4379 DirEntry data;
4380 HRESULT hr;
4381 ULONG bytesToRead;
4383 hr = StorageImpl_ReadDirEntry(This, index, &data);
4384 if (FAILED(hr)) return hr;
4386 if (data.size.QuadPart == 0)
4388 *bytesRead = 0;
4389 return S_OK;
4392 if (offset.QuadPart + size > data.size.QuadPart)
4394 bytesToRead = data.size.QuadPart - offset.QuadPart;
4396 else
4398 bytesToRead = size;
4401 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4403 SmallBlockChainStream *stream;
4405 stream = SmallBlockChainStream_Construct(This, NULL, index);
4406 if (!stream) return E_OUTOFMEMORY;
4408 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4410 SmallBlockChainStream_Destroy(stream);
4412 return hr;
4414 else
4416 BlockChainStream *stream = NULL;
4418 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4419 if (!stream) return E_OUTOFMEMORY;
4421 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4423 return hr;
4427 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4428 ULARGE_INTEGER newsize)
4430 StorageImpl *This = (StorageImpl*)base;
4431 DirEntry data;
4432 HRESULT hr;
4433 SmallBlockChainStream *smallblock=NULL;
4434 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4436 hr = StorageImpl_ReadDirEntry(This, index, &data);
4437 if (FAILED(hr)) return hr;
4439 /* In simple mode keep the stream size above the small block limit */
4440 if (This->base.openFlags & STGM_SIMPLE)
4441 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4443 if (data.size.QuadPart == newsize.QuadPart)
4444 return S_OK;
4446 /* Create a block chain object of the appropriate type */
4447 if (data.size.QuadPart == 0)
4449 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4451 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4452 if (!smallblock) return E_OUTOFMEMORY;
4454 else
4456 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4457 bigblock = *pbigblock;
4458 if (!bigblock) return E_OUTOFMEMORY;
4461 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4463 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4464 if (!smallblock) return E_OUTOFMEMORY;
4466 else
4468 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4469 bigblock = *pbigblock;
4470 if (!bigblock) return E_OUTOFMEMORY;
4473 /* Change the block chain type if necessary. */
4474 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4476 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4477 if (!bigblock)
4479 SmallBlockChainStream_Destroy(smallblock);
4480 return E_FAIL;
4483 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4484 *pbigblock = bigblock;
4486 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4488 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4489 if (!smallblock)
4490 return E_FAIL;
4493 /* Set the size of the block chain. */
4494 if (smallblock)
4496 SmallBlockChainStream_SetSize(smallblock, newsize);
4497 SmallBlockChainStream_Destroy(smallblock);
4499 else
4501 BlockChainStream_SetSize(bigblock, newsize);
4504 /* Set the size in the directory entry. */
4505 hr = StorageImpl_ReadDirEntry(This, index, &data);
4506 if (SUCCEEDED(hr))
4508 data.size = newsize;
4510 hr = StorageImpl_WriteDirEntry(This, index, &data);
4512 return hr;
4515 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4516 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4518 StorageImpl *This = (StorageImpl*)base;
4519 DirEntry data;
4520 HRESULT hr;
4521 ULARGE_INTEGER newSize;
4523 hr = StorageImpl_ReadDirEntry(This, index, &data);
4524 if (FAILED(hr)) return hr;
4526 /* Grow the stream if necessary */
4527 newSize.QuadPart = offset.QuadPart + size;
4529 if (newSize.QuadPart > data.size.QuadPart)
4531 hr = StorageImpl_StreamSetSize(base, index, newSize);
4532 if (FAILED(hr))
4533 return hr;
4535 hr = StorageImpl_ReadDirEntry(This, index, &data);
4536 if (FAILED(hr)) return hr;
4539 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4541 SmallBlockChainStream *stream;
4543 stream = SmallBlockChainStream_Construct(This, NULL, index);
4544 if (!stream) return E_OUTOFMEMORY;
4546 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4548 SmallBlockChainStream_Destroy(stream);
4550 return hr;
4552 else
4554 BlockChainStream *stream;
4556 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4557 if (!stream) return E_OUTOFMEMORY;
4559 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4563 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4564 DirRef src)
4566 StorageImpl *This = (StorageImpl*)base;
4567 DirEntry dst_data, src_data;
4568 HRESULT hr;
4570 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4572 if (SUCCEEDED(hr))
4573 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4575 if (SUCCEEDED(hr))
4577 StorageImpl_DeleteCachedBlockChainStream(This, src);
4578 dst_data.startingBlock = src_data.startingBlock;
4579 dst_data.size = src_data.size;
4581 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4584 return hr;
4587 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4589 HRESULT hr=S_OK;
4590 DirEntry currentEntry;
4591 DirRef currentEntryRef;
4592 BlockChainStream *blockChainStream;
4594 if (create)
4596 ULARGE_INTEGER size;
4597 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4599 /* Discard any existing data. */
4600 size.QuadPart = 0;
4601 ILockBytes_SetSize(This->lockBytes, size);
4604 * Initialize all header variables:
4605 * - The big block depot consists of one block and it is at block 0
4606 * - The directory table starts at block 1
4607 * - There is no small block depot
4609 memset( This->bigBlockDepotStart,
4610 BLOCK_UNUSED,
4611 sizeof(This->bigBlockDepotStart));
4613 This->bigBlockDepotCount = 1;
4614 This->bigBlockDepotStart[0] = 0;
4615 This->rootStartBlock = 1;
4616 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4617 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4618 if (This->bigBlockSize == 4096)
4619 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4620 else
4621 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4622 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4623 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4624 This->extBigBlockDepotCount = 0;
4626 StorageImpl_SaveFileHeader(This);
4629 * Add one block for the big block depot and one block for the directory table
4631 size.u.HighPart = 0;
4632 size.u.LowPart = This->bigBlockSize * 3;
4633 ILockBytes_SetSize(This->lockBytes, size);
4636 * Initialize the big block depot
4638 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4639 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4640 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4641 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4643 else
4646 * Load the header for the file.
4648 hr = StorageImpl_LoadFileHeader(This);
4650 if (FAILED(hr))
4652 return hr;
4657 * There is no block depot cached yet.
4659 This->indexBlockDepotCached = 0xFFFFFFFF;
4660 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4663 * Start searching for free blocks with block 0.
4665 This->prevFreeBlock = 0;
4667 This->firstFreeSmallBlock = 0;
4669 /* Read the extended big block depot locations. */
4670 if (This->extBigBlockDepotCount != 0)
4672 ULONG current_block = This->extBigBlockDepotStart;
4673 ULONG cache_size = This->extBigBlockDepotCount * 2;
4674 ULONG i;
4676 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4677 if (!This->extBigBlockDepotLocations)
4679 return E_OUTOFMEMORY;
4682 This->extBigBlockDepotLocationsSize = cache_size;
4684 for (i=0; i<This->extBigBlockDepotCount; i++)
4686 if (current_block == BLOCK_END_OF_CHAIN)
4688 WARN("File has too few extended big block depot blocks.\n");
4689 return STG_E_DOCFILECORRUPT;
4691 This->extBigBlockDepotLocations[i] = current_block;
4692 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4695 else
4697 This->extBigBlockDepotLocations = NULL;
4698 This->extBigBlockDepotLocationsSize = 0;
4702 * Create the block chain abstractions.
4704 if(!(blockChainStream =
4705 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4707 return STG_E_READFAULT;
4709 if (!new_object)
4710 BlockChainStream_Destroy(This->rootBlockChain);
4711 This->rootBlockChain = blockChainStream;
4713 if(!(blockChainStream =
4714 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4715 DIRENTRY_NULL)))
4717 return STG_E_READFAULT;
4719 if (!new_object)
4720 BlockChainStream_Destroy(This->smallBlockDepotChain);
4721 This->smallBlockDepotChain = blockChainStream;
4724 * Write the root storage entry (memory only)
4726 if (create)
4728 DirEntry rootEntry;
4730 * Initialize the directory table
4732 memset(&rootEntry, 0, sizeof(rootEntry));
4733 lstrcpyW(rootEntry.name, L"Root Entry");
4734 rootEntry.sizeOfNameString = sizeof(L"Root Entry");
4735 rootEntry.stgType = STGTY_ROOT;
4736 rootEntry.leftChild = DIRENTRY_NULL;
4737 rootEntry.rightChild = DIRENTRY_NULL;
4738 rootEntry.dirRootEntry = DIRENTRY_NULL;
4739 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4740 rootEntry.size.u.HighPart = 0;
4741 rootEntry.size.u.LowPart = 0;
4743 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4747 * Find the ID of the root storage.
4749 currentEntryRef = 0;
4753 hr = StorageImpl_ReadDirEntry(
4754 This,
4755 currentEntryRef,
4756 &currentEntry);
4758 if (SUCCEEDED(hr))
4760 if ( (currentEntry.sizeOfNameString != 0 ) &&
4761 (currentEntry.stgType == STGTY_ROOT) )
4763 This->base.storageDirEntry = currentEntryRef;
4767 currentEntryRef++;
4769 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4771 if (FAILED(hr))
4773 return STG_E_READFAULT;
4777 * Create the block chain abstraction for the small block root chain.
4779 if(!(blockChainStream =
4780 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4782 return STG_E_READFAULT;
4784 if (!new_object)
4785 BlockChainStream_Destroy(This->smallBlockRootChain);
4786 This->smallBlockRootChain = blockChainStream;
4788 if (!new_object)
4790 int i;
4791 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4793 BlockChainStream_Destroy(This->blockChainCache[i]);
4794 This->blockChainCache[i] = NULL;
4798 return hr;
4801 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4802 ULONG* result, BOOL refresh)
4804 StorageImpl *This = (StorageImpl*)base;
4805 HRESULT hr=S_OK;
4806 DWORD oldTransactionSig = This->transactionSig;
4808 if (refresh)
4810 ULARGE_INTEGER offset;
4811 ULONG bytes_read;
4812 BYTE data[4];
4814 offset.u.HighPart = 0;
4815 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4816 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4818 if (SUCCEEDED(hr))
4820 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4822 if (oldTransactionSig != This->transactionSig)
4824 /* Someone else wrote to this, so toss all cached information. */
4825 TRACE("signature changed\n");
4827 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4830 if (FAILED(hr))
4831 This->transactionSig = oldTransactionSig;
4835 *result = This->transactionSig;
4837 return hr;
4840 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4841 ULONG value)
4843 StorageImpl *This = (StorageImpl*)base;
4845 This->transactionSig = value;
4846 StorageImpl_SaveFileHeader(This);
4848 return S_OK;
4851 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4852 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4854 if ((dwLockType & This->locks_supported) == 0)
4856 if (supported) *supported = FALSE;
4857 return S_OK;
4860 if (supported) *supported = TRUE;
4861 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4864 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4865 ULARGE_INTEGER cb, DWORD dwLockType)
4867 if ((dwLockType & This->locks_supported) == 0)
4868 return S_OK;
4870 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4873 /* Internal function */
4874 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4875 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4877 HRESULT hr;
4878 int delay = 0;
4879 DWORD start_time = GetTickCount();
4880 DWORD last_sanity_check = start_time;
4881 ULARGE_INTEGER sanity_offset, sanity_cb;
4883 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4884 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4888 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4890 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4892 DWORD current_time = GetTickCount();
4893 if (current_time - start_time >= 20000)
4895 /* timeout */
4896 break;
4898 if (current_time - last_sanity_check >= 500)
4900 /* Any storage implementation with the file open in a
4901 * shared mode should not lock these bytes for writing. However,
4902 * some programs (LibreOffice Writer) will keep ALL bytes locked
4903 * when opening in exclusive mode. We can use a read lock to
4904 * detect this case early, and not hang a full 20 seconds.
4906 * This can collide with another attempt to open the file in
4907 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4908 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4909 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4910 break;
4911 if (SUCCEEDED(hr))
4913 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4914 hr = STG_E_ACCESSDENIED;
4917 last_sanity_check = current_time;
4919 Sleep(delay);
4920 if (delay < 150) delay++;
4922 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4924 return hr;
4927 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4929 StorageImpl *This = (StorageImpl*)base;
4930 HRESULT hr;
4931 ULARGE_INTEGER offset, cb;
4933 if (write)
4935 /* Synchronous grab of second priority range, the commit lock, and the
4936 * lock-checking lock. */
4937 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4938 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4940 else
4942 offset.QuadPart = RANGELOCK_COMMIT;
4943 cb.QuadPart = 1;
4946 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4948 return hr;
4951 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4953 StorageImpl *This = (StorageImpl*)base;
4954 HRESULT hr;
4955 ULARGE_INTEGER offset, cb;
4957 if (write)
4959 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4960 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4962 else
4964 offset.QuadPart = RANGELOCK_COMMIT;
4965 cb.QuadPart = 1;
4968 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4970 return hr;
4973 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4975 StorageImpl *This = (StorageImpl*) iface;
4976 STATSTG statstg;
4977 HRESULT hr;
4979 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4981 *result = statstg.pwcsName;
4983 return hr;
4986 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
4987 ULONG end, HRESULT fail_hr)
4989 HRESULT hr;
4990 ULARGE_INTEGER offset, cb;
4992 offset.QuadPart = start;
4993 cb.QuadPart = 1 + end - start;
4995 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4996 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4998 if (FAILED(hr))
4999 return fail_hr;
5000 else
5001 return S_OK;
5004 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
5006 HRESULT hr=S_OK;
5007 int i, j;
5008 ULARGE_INTEGER offset, cb;
5010 cb.QuadPart = 1;
5012 for (i=start; i<=end; i++)
5014 offset.QuadPart = i;
5015 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5016 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
5017 break;
5020 if (SUCCEEDED(hr))
5022 for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++)
5024 if (This->locked_bytes[j] == 0)
5026 This->locked_bytes[j] = i;
5027 break;
5032 return hr;
5035 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5037 HRESULT hr;
5038 ULARGE_INTEGER offset;
5039 ULARGE_INTEGER cb;
5040 DWORD share_mode = STGM_SHARE_MODE(openFlags);
5041 BOOL supported;
5043 if (openFlags & STGM_NOSNAPSHOT)
5045 /* STGM_NOSNAPSHOT implies deny write */
5046 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5047 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5050 /* Wrap all other locking inside a single lock so we can check ranges safely */
5051 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5052 cb.QuadPart = 1;
5053 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5055 /* If the ILockBytes doesn't support locking that's ok. */
5056 if (!supported) return S_OK;
5057 else if (FAILED(hr)) return hr;
5059 hr = S_OK;
5061 /* First check for any conflicting locks. */
5062 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5063 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5065 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5066 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5068 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5069 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5071 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5072 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5074 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5075 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5077 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5079 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5081 if (SUCCEEDED(hr))
5082 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5085 /* Then grab our locks. */
5086 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5088 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5089 if (SUCCEEDED(hr))
5090 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5093 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5094 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5096 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5097 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5099 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5100 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5102 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5103 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5105 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5106 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5108 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5109 cb.QuadPart = 1;
5110 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5112 return hr;
5115 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5117 StorageImpl *This = (StorageImpl*)storage;
5118 int i;
5119 HRESULT hr;
5120 TRACE("(%p)\n", This);
5122 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5124 if (SUCCEEDED(hr))
5125 hr = BlockChainStream_Flush(This->rootBlockChain);
5127 if (SUCCEEDED(hr))
5128 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5130 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5131 if (This->blockChainCache[i])
5132 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5134 if (SUCCEEDED(hr))
5135 hr = ILockBytes_Flush(This->lockBytes);
5137 return hr;
5140 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5142 StorageImpl *This = (StorageImpl*) iface;
5144 StorageBaseImpl_DeleteAll(&This->base);
5146 This->base.reverted = TRUE;
5149 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5151 StorageImpl *This = (StorageImpl*) iface;
5152 int i;
5153 TRACE("(%p)\n", This);
5155 StorageImpl_Flush(iface);
5157 StorageImpl_Invalidate(iface);
5159 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5161 BlockChainStream_Destroy(This->smallBlockRootChain);
5162 BlockChainStream_Destroy(This->rootBlockChain);
5163 BlockChainStream_Destroy(This->smallBlockDepotChain);
5165 for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++)
5166 BlockChainStream_Destroy(This->blockChainCache[i]);
5168 for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++)
5170 ULARGE_INTEGER offset, cb;
5171 cb.QuadPart = 1;
5172 if (This->locked_bytes[i] != 0)
5174 offset.QuadPart = This->locked_bytes[i];
5175 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5179 if (This->lockBytes)
5180 ILockBytes_Release(This->lockBytes);
5181 HeapFree(GetProcessHeap(), 0, This);
5185 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5187 StorageImpl_Destroy,
5188 StorageImpl_Invalidate,
5189 StorageImpl_Flush,
5190 StorageImpl_GetFilename,
5191 StorageImpl_CreateDirEntry,
5192 StorageImpl_BaseWriteDirEntry,
5193 StorageImpl_BaseReadDirEntry,
5194 StorageImpl_DestroyDirEntry,
5195 StorageImpl_StreamReadAt,
5196 StorageImpl_StreamWriteAt,
5197 StorageImpl_StreamSetSize,
5198 StorageImpl_StreamLink,
5199 StorageImpl_GetTransactionSig,
5200 StorageImpl_SetTransactionSig,
5201 StorageImpl_LockTransaction,
5202 StorageImpl_UnlockTransaction
5207 * Virtual function table for the IStorageBaseImpl class.
5209 static const IStorageVtbl StorageImpl_Vtbl =
5211 StorageBaseImpl_QueryInterface,
5212 StorageBaseImpl_AddRef,
5213 StorageBaseImpl_Release,
5214 StorageBaseImpl_CreateStream,
5215 StorageBaseImpl_OpenStream,
5216 StorageBaseImpl_CreateStorage,
5217 StorageBaseImpl_OpenStorage,
5218 StorageBaseImpl_CopyTo,
5219 StorageBaseImpl_MoveElementTo,
5220 StorageBaseImpl_Commit,
5221 StorageBaseImpl_Revert,
5222 StorageBaseImpl_EnumElements,
5223 StorageBaseImpl_DestroyElement,
5224 StorageBaseImpl_RenameElement,
5225 StorageBaseImpl_SetElementTimes,
5226 StorageBaseImpl_SetClass,
5227 StorageBaseImpl_SetStateBits,
5228 StorageBaseImpl_Stat
5231 static HRESULT StorageImpl_Construct(
5232 HANDLE hFile,
5233 LPCOLESTR pwcsName,
5234 ILockBytes* pLkbyt,
5235 DWORD openFlags,
5236 BOOL fileBased,
5237 BOOL create,
5238 ULONG sector_size,
5239 StorageImpl** result)
5241 StorageImpl* This;
5242 HRESULT hr = S_OK;
5243 STATSTG stat;
5245 if ( FAILED( validateSTGM(openFlags) ))
5246 return STG_E_INVALIDFLAG;
5248 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5249 if (!This)
5250 return E_OUTOFMEMORY;
5252 memset(This, 0, sizeof(StorageImpl));
5254 list_init(&This->base.strmHead);
5256 list_init(&This->base.storageHead);
5258 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5259 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5260 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5261 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5262 This->base.openFlags = (openFlags & ~STGM_CREATE);
5263 This->base.ref = 1;
5264 This->base.create = create;
5266 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5267 This->base.lockingrole = SWMR_Writer;
5268 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5269 This->base.lockingrole = SWMR_Reader;
5270 else
5271 This->base.lockingrole = SWMR_None;
5273 This->base.reverted = FALSE;
5276 * Initialize the big block cache.
5278 This->bigBlockSize = sector_size;
5279 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5280 if (hFile)
5281 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5282 else
5284 This->lockBytes = pLkbyt;
5285 ILockBytes_AddRef(pLkbyt);
5288 if (SUCCEEDED(hr))
5289 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5291 if (SUCCEEDED(hr))
5293 This->locks_supported = stat.grfLocksSupported;
5294 if (!hFile)
5295 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5296 This->locks_supported &= ~WINE_LOCK_READ;
5298 hr = StorageImpl_GrabLocks(This, openFlags);
5301 if (SUCCEEDED(hr))
5302 hr = StorageImpl_Refresh(This, TRUE, create);
5304 if (FAILED(hr))
5306 IStorage_Release(&This->base.IStorage_iface);
5307 *result = NULL;
5309 else
5311 StorageImpl_Flush(&This->base);
5312 *result = This;
5315 return hr;
5319 /************************************************************************
5320 * StorageInternalImpl implementation
5321 ***********************************************************************/
5323 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5325 StorageInternalImpl* This = (StorageInternalImpl*) base;
5327 if (!This->base.reverted)
5329 TRACE("Storage invalidated (stg=%p)\n", This);
5331 This->base.reverted = TRUE;
5333 This->parentStorage = NULL;
5335 StorageBaseImpl_DeleteAll(&This->base);
5337 list_remove(&This->ParentListEntry);
5341 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5343 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5345 StorageInternalImpl_Invalidate(&This->base);
5347 HeapFree(GetProcessHeap(), 0, This);
5350 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5352 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5354 return StorageBaseImpl_Flush(This->parentStorage);
5357 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5359 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5361 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5364 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5365 const DirEntry *newData, DirRef *index)
5367 StorageInternalImpl* This = (StorageInternalImpl*) base;
5369 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5370 newData, index);
5373 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5374 DirRef index, const DirEntry *data)
5376 StorageInternalImpl* This = (StorageInternalImpl*) base;
5378 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5379 index, data);
5382 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5383 DirRef index, DirEntry *data)
5385 StorageInternalImpl* This = (StorageInternalImpl*) base;
5387 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5388 index, data);
5391 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5392 DirRef index)
5394 StorageInternalImpl* This = (StorageInternalImpl*) base;
5396 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5397 index);
5400 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5401 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5403 StorageInternalImpl* This = (StorageInternalImpl*) base;
5405 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5406 index, offset, size, buffer, bytesRead);
5409 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5410 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5412 StorageInternalImpl* This = (StorageInternalImpl*) base;
5414 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5415 index, offset, size, buffer, bytesWritten);
5418 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5419 DirRef index, ULARGE_INTEGER newsize)
5421 StorageInternalImpl* This = (StorageInternalImpl*) base;
5423 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5424 index, newsize);
5427 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5428 DirRef dst, DirRef src)
5430 StorageInternalImpl* This = (StorageInternalImpl*) base;
5432 return StorageBaseImpl_StreamLink(This->parentStorage,
5433 dst, src);
5436 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5437 ULONG* result, BOOL refresh)
5439 return E_NOTIMPL;
5442 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5443 ULONG value)
5445 return E_NOTIMPL;
5448 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5450 return E_NOTIMPL;
5453 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5455 return E_NOTIMPL;
5458 /******************************************************************************
5460 ** StorageInternalImpl_Commit
5463 static HRESULT WINAPI StorageInternalImpl_Commit(
5464 IStorage* iface,
5465 DWORD grfCommitFlags) /* [in] */
5467 StorageBaseImpl* This = impl_from_IStorage(iface);
5468 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
5469 return StorageBaseImpl_Flush(This);
5472 /******************************************************************************
5474 ** StorageInternalImpl_Revert
5477 static HRESULT WINAPI StorageInternalImpl_Revert(
5478 IStorage* iface)
5480 FIXME("(%p): stub\n", iface);
5481 return S_OK;
5485 * Virtual function table for the StorageInternalImpl class.
5487 static const IStorageVtbl StorageInternalImpl_Vtbl =
5489 StorageBaseImpl_QueryInterface,
5490 StorageBaseImpl_AddRef,
5491 StorageBaseImpl_Release,
5492 StorageBaseImpl_CreateStream,
5493 StorageBaseImpl_OpenStream,
5494 StorageBaseImpl_CreateStorage,
5495 StorageBaseImpl_OpenStorage,
5496 StorageBaseImpl_CopyTo,
5497 StorageBaseImpl_MoveElementTo,
5498 StorageInternalImpl_Commit,
5499 StorageInternalImpl_Revert,
5500 StorageBaseImpl_EnumElements,
5501 StorageBaseImpl_DestroyElement,
5502 StorageBaseImpl_RenameElement,
5503 StorageBaseImpl_SetElementTimes,
5504 StorageBaseImpl_SetClass,
5505 StorageBaseImpl_SetStateBits,
5506 StorageBaseImpl_Stat
5509 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5511 StorageInternalImpl_Destroy,
5512 StorageInternalImpl_Invalidate,
5513 StorageInternalImpl_Flush,
5514 StorageInternalImpl_GetFilename,
5515 StorageInternalImpl_CreateDirEntry,
5516 StorageInternalImpl_WriteDirEntry,
5517 StorageInternalImpl_ReadDirEntry,
5518 StorageInternalImpl_DestroyDirEntry,
5519 StorageInternalImpl_StreamReadAt,
5520 StorageInternalImpl_StreamWriteAt,
5521 StorageInternalImpl_StreamSetSize,
5522 StorageInternalImpl_StreamLink,
5523 StorageInternalImpl_GetTransactionSig,
5524 StorageInternalImpl_SetTransactionSig,
5525 StorageInternalImpl_LockTransaction,
5526 StorageInternalImpl_UnlockTransaction
5529 static StorageInternalImpl* StorageInternalImpl_Construct(
5530 StorageBaseImpl* parentStorage,
5531 DWORD openFlags,
5532 DirRef storageDirEntry)
5534 StorageInternalImpl* newStorage;
5536 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5538 if (newStorage!=0)
5540 list_init(&newStorage->base.strmHead);
5542 list_init(&newStorage->base.storageHead);
5545 * Initialize the virtual function table.
5547 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5548 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5549 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5550 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5552 newStorage->base.reverted = FALSE;
5554 newStorage->base.ref = 1;
5556 newStorage->parentStorage = parentStorage;
5559 * Keep a reference to the directory entry of this storage
5561 newStorage->base.storageDirEntry = storageDirEntry;
5563 newStorage->base.create = FALSE;
5565 return newStorage;
5568 return 0;
5572 /************************************************************************
5573 * TransactedSnapshotImpl implementation
5574 ***********************************************************************/
5576 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5578 DirRef result=This->firstFreeEntry;
5580 while (result < This->entries_size && This->entries[result].inuse)
5581 result++;
5583 if (result == This->entries_size)
5585 ULONG new_size = This->entries_size * 2;
5586 TransactedDirEntry *new_entries;
5588 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5589 if (!new_entries) return DIRENTRY_NULL;
5591 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5592 HeapFree(GetProcessHeap(), 0, This->entries);
5594 This->entries = new_entries;
5595 This->entries_size = new_size;
5598 This->entries[result].inuse = TRUE;
5600 This->firstFreeEntry = result+1;
5602 return result;
5605 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5606 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5608 DirRef stubEntryRef;
5609 TransactedDirEntry *entry;
5611 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5613 if (stubEntryRef != DIRENTRY_NULL)
5615 entry = &This->entries[stubEntryRef];
5617 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5619 entry->read = FALSE;
5622 return stubEntryRef;
5625 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5626 TransactedSnapshotImpl *This, DirRef entry)
5628 HRESULT hr=S_OK;
5629 DirEntry data;
5631 if (!This->entries[entry].read)
5633 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5634 This->entries[entry].transactedParentEntry,
5635 &data);
5637 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5639 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5641 if (data.leftChild == DIRENTRY_NULL)
5642 hr = E_OUTOFMEMORY;
5645 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5647 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5649 if (data.rightChild == DIRENTRY_NULL)
5650 hr = E_OUTOFMEMORY;
5653 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5655 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5657 if (data.dirRootEntry == DIRENTRY_NULL)
5658 hr = E_OUTOFMEMORY;
5661 if (SUCCEEDED(hr))
5663 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5664 This->entries[entry].read = TRUE;
5668 return hr;
5671 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5672 TransactedSnapshotImpl *This, DirRef entry)
5674 HRESULT hr = S_OK;
5676 if (!This->entries[entry].stream_dirty)
5678 DirEntry new_entrydata;
5680 memset(&new_entrydata, 0, sizeof(DirEntry));
5681 new_entrydata.name[0] = 'S';
5682 new_entrydata.sizeOfNameString = 1;
5683 new_entrydata.stgType = STGTY_STREAM;
5684 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5685 new_entrydata.leftChild = DIRENTRY_NULL;
5686 new_entrydata.rightChild = DIRENTRY_NULL;
5687 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5689 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5690 &This->entries[entry].stream_entry);
5692 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5694 hr = StorageBaseImpl_CopyStream(
5695 This->scratch, This->entries[entry].stream_entry,
5696 This->transactedParent, This->entries[entry].transactedParentEntry);
5698 if (FAILED(hr))
5699 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5702 if (SUCCEEDED(hr))
5703 This->entries[entry].stream_dirty = TRUE;
5705 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5707 /* Since this entry is modified, and we aren't using its stream data, we
5708 * no longer care about the original entry. */
5709 DirRef delete_ref;
5710 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5712 if (delete_ref != DIRENTRY_NULL)
5713 This->entries[delete_ref].deleted = TRUE;
5715 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5719 return hr;
5722 /* Find the first entry in a depth-first traversal. */
5723 static DirRef TransactedSnapshotImpl_FindFirstChild(
5724 TransactedSnapshotImpl* This, DirRef parent)
5726 DirRef cursor, prev;
5727 TransactedDirEntry *entry;
5729 cursor = parent;
5730 entry = &This->entries[cursor];
5731 while (entry->read)
5733 if (entry->data.leftChild != DIRENTRY_NULL)
5735 prev = cursor;
5736 cursor = entry->data.leftChild;
5737 entry = &This->entries[cursor];
5738 entry->parent = prev;
5740 else if (entry->data.rightChild != DIRENTRY_NULL)
5742 prev = cursor;
5743 cursor = entry->data.rightChild;
5744 entry = &This->entries[cursor];
5745 entry->parent = prev;
5747 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5749 prev = cursor;
5750 cursor = entry->data.dirRootEntry;
5751 entry = &This->entries[cursor];
5752 entry->parent = prev;
5754 else
5755 break;
5758 return cursor;
5761 /* Find the next entry in a depth-first traversal. */
5762 static DirRef TransactedSnapshotImpl_FindNextChild(
5763 TransactedSnapshotImpl* This, DirRef current)
5765 DirRef parent;
5766 TransactedDirEntry *parent_entry;
5768 parent = This->entries[current].parent;
5769 parent_entry = &This->entries[parent];
5771 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5773 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5775 This->entries[parent_entry->data.rightChild].parent = parent;
5776 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5779 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5781 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5782 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5786 return parent;
5789 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5790 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5791 TransactedSnapshotImpl* This, DirRef entry)
5793 return entry != DIRENTRY_NULL &&
5794 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5797 /* Destroy the entries created by CopyTree. */
5798 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5799 TransactedSnapshotImpl* This, DirRef stop)
5801 DirRef cursor;
5802 TransactedDirEntry *entry;
5803 ULARGE_INTEGER zero;
5805 zero.QuadPart = 0;
5807 if (!This->entries[This->base.storageDirEntry].read)
5808 return;
5810 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5812 if (cursor == DIRENTRY_NULL)
5813 return;
5815 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5817 while (cursor != DIRENTRY_NULL && cursor != stop)
5819 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5821 entry = &This->entries[cursor];
5823 if (entry->stream_dirty)
5824 StorageBaseImpl_StreamSetSize(This->transactedParent,
5825 entry->newTransactedParentEntry, zero);
5827 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5828 entry->newTransactedParentEntry);
5830 entry->newTransactedParentEntry = entry->transactedParentEntry;
5833 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5837 /* Make a copy of our edited tree that we can use in the parent. */
5838 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5840 DirRef cursor;
5841 TransactedDirEntry *entry;
5842 HRESULT hr = S_OK;
5844 cursor = This->base.storageDirEntry;
5845 entry = &This->entries[cursor];
5846 entry->parent = DIRENTRY_NULL;
5847 entry->newTransactedParentEntry = entry->transactedParentEntry;
5849 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5850 return S_OK;
5852 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5854 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5855 entry = &This->entries[cursor];
5857 while (cursor != DIRENTRY_NULL)
5859 /* Make a copy of this entry in the transacted parent. */
5860 if (!entry->read ||
5861 (!entry->dirty && !entry->stream_dirty &&
5862 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5863 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5864 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5865 entry->newTransactedParentEntry = entry->transactedParentEntry;
5866 else
5868 DirEntry newData;
5870 memcpy(&newData, &entry->data, sizeof(DirEntry));
5872 newData.size.QuadPart = 0;
5873 newData.startingBlock = BLOCK_END_OF_CHAIN;
5875 if (newData.leftChild != DIRENTRY_NULL)
5876 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5878 if (newData.rightChild != DIRENTRY_NULL)
5879 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5881 if (newData.dirRootEntry != DIRENTRY_NULL)
5882 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5884 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5885 &entry->newTransactedParentEntry);
5886 if (FAILED(hr))
5888 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5889 return hr;
5892 if (entry->stream_dirty)
5894 hr = StorageBaseImpl_CopyStream(
5895 This->transactedParent, entry->newTransactedParentEntry,
5896 This->scratch, entry->stream_entry);
5898 else if (entry->data.size.QuadPart)
5900 hr = StorageBaseImpl_StreamLink(
5901 This->transactedParent, entry->newTransactedParentEntry,
5902 entry->transactedParentEntry);
5905 if (FAILED(hr))
5907 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5908 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5909 return hr;
5913 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5914 entry = &This->entries[cursor];
5917 return hr;
5920 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5921 IStorage* iface,
5922 DWORD grfCommitFlags) /* [in] */
5924 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5925 TransactedDirEntry *root_entry;
5926 DirRef i, dir_root_ref;
5927 DirEntry data;
5928 ULARGE_INTEGER zero;
5929 HRESULT hr;
5930 ULONG transactionSig;
5932 zero.QuadPart = 0;
5934 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
5936 /* Cannot commit a read-only transacted storage */
5937 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5938 return STG_E_ACCESSDENIED;
5940 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5941 if (hr == E_NOTIMPL) hr = S_OK;
5942 if (SUCCEEDED(hr))
5944 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5945 if (SUCCEEDED(hr))
5947 if (transactionSig != This->lastTransactionSig)
5949 ERR("file was externally modified\n");
5950 hr = STG_E_NOTCURRENT;
5953 if (SUCCEEDED(hr))
5955 This->lastTransactionSig = transactionSig+1;
5956 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5959 else if (hr == E_NOTIMPL)
5960 hr = S_OK;
5962 if (FAILED(hr)) goto end;
5964 /* To prevent data loss, we create the new structure in the file before we
5965 * delete the old one, so that in case of errors the old data is intact. We
5966 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5967 * needed in the rare situation where we have just enough free disk space to
5968 * overwrite the existing data. */
5970 root_entry = &This->entries[This->base.storageDirEntry];
5972 if (!root_entry->read)
5973 goto end;
5975 hr = TransactedSnapshotImpl_CopyTree(This);
5976 if (FAILED(hr)) goto end;
5978 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5979 dir_root_ref = DIRENTRY_NULL;
5980 else
5981 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5983 hr = StorageBaseImpl_Flush(This->transactedParent);
5985 /* Update the storage to use the new data in one step. */
5986 if (SUCCEEDED(hr))
5987 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5988 root_entry->transactedParentEntry, &data);
5990 if (SUCCEEDED(hr))
5992 data.dirRootEntry = dir_root_ref;
5993 data.clsid = root_entry->data.clsid;
5994 data.ctime = root_entry->data.ctime;
5995 data.mtime = root_entry->data.mtime;
5997 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5998 root_entry->transactedParentEntry, &data);
6001 /* Try to flush after updating the root storage, but if the flush fails, keep
6002 * going, on the theory that it'll either succeed later or the subsequent
6003 * writes will fail. */
6004 StorageBaseImpl_Flush(This->transactedParent);
6006 if (SUCCEEDED(hr))
6008 /* Destroy the old now-orphaned data. */
6009 for (i=0; i<This->entries_size; i++)
6011 TransactedDirEntry *entry = &This->entries[i];
6012 if (entry->inuse)
6014 if (entry->deleted)
6016 StorageBaseImpl_StreamSetSize(This->transactedParent,
6017 entry->transactedParentEntry, zero);
6018 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6019 entry->transactedParentEntry);
6020 memset(entry, 0, sizeof(TransactedDirEntry));
6021 This->firstFreeEntry = min(i, This->firstFreeEntry);
6023 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6025 if (entry->transactedParentEntry != DIRENTRY_NULL)
6026 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6027 entry->transactedParentEntry);
6028 if (entry->stream_dirty)
6030 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6031 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6032 entry->stream_dirty = FALSE;
6034 entry->dirty = FALSE;
6035 entry->transactedParentEntry = entry->newTransactedParentEntry;
6040 else
6042 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6045 if (SUCCEEDED(hr))
6046 hr = StorageBaseImpl_Flush(This->transactedParent);
6047 end:
6048 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6051 TRACE("<-- %#lx\n", hr);
6052 return hr;
6055 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6056 IStorage* iface)
6058 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6059 ULARGE_INTEGER zero;
6060 ULONG i;
6062 TRACE("(%p)\n", iface);
6064 /* Destroy the open objects. */
6065 StorageBaseImpl_DeleteAll(&This->base);
6067 /* Clear out the scratch file. */
6068 zero.QuadPart = 0;
6069 for (i=0; i<This->entries_size; i++)
6071 if (This->entries[i].stream_dirty)
6073 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6074 zero);
6076 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6080 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6082 This->firstFreeEntry = 0;
6083 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6085 return S_OK;
6088 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6090 if (!This->reverted)
6092 TRACE("Storage invalidated (stg=%p)\n", This);
6094 This->reverted = TRUE;
6096 StorageBaseImpl_DeleteAll(This);
6100 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6102 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6104 IStorage_Revert(&This->base.IStorage_iface);
6105 IStorage_Release(&This->transactedParent->IStorage_iface);
6106 IStorage_Release(&This->scratch->IStorage_iface);
6107 HeapFree(GetProcessHeap(), 0, This->entries);
6108 HeapFree(GetProcessHeap(), 0, This);
6111 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6113 /* We only need to flush when committing. */
6114 return S_OK;
6117 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6119 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6121 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6124 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6125 const DirEntry *newData, DirRef *index)
6127 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6128 DirRef new_ref;
6129 TransactedDirEntry *new_entry;
6131 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6132 if (new_ref == DIRENTRY_NULL)
6133 return E_OUTOFMEMORY;
6135 new_entry = &This->entries[new_ref];
6137 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6138 new_entry->read = TRUE;
6139 new_entry->dirty = TRUE;
6140 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6142 *index = new_ref;
6144 TRACE("%s l=%lx r=%lx d=%lx <-- %lx\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6146 return S_OK;
6149 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6150 DirRef index, const DirEntry *data)
6152 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6153 HRESULT hr;
6155 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6157 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6158 if (FAILED(hr))
6160 TRACE("<-- %#lx\n", hr);
6161 return hr;
6164 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6166 if (index != This->base.storageDirEntry)
6168 This->entries[index].dirty = TRUE;
6170 if (data->size.QuadPart == 0 &&
6171 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6173 /* Since this entry is modified, and we aren't using its stream data, we
6174 * no longer care about the original entry. */
6175 DirRef delete_ref;
6176 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6178 if (delete_ref != DIRENTRY_NULL)
6179 This->entries[delete_ref].deleted = TRUE;
6181 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6184 TRACE("<-- S_OK\n");
6185 return S_OK;
6188 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6189 DirRef index, DirEntry *data)
6191 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6192 HRESULT hr;
6194 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6195 if (FAILED(hr))
6197 TRACE("<-- %#lx\n", hr);
6198 return hr;
6201 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6203 TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6205 return S_OK;
6208 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6209 DirRef index)
6211 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6213 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6214 This->entries[index].data.size.QuadPart != 0)
6216 /* If we deleted this entry while it has stream data. We must have left the
6217 * data because some other entry is using it, and we need to leave the
6218 * original entry alone. */
6219 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6220 This->firstFreeEntry = min(index, This->firstFreeEntry);
6222 else
6224 This->entries[index].deleted = TRUE;
6227 return S_OK;
6230 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6231 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6233 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6235 if (This->entries[index].stream_dirty)
6237 return StorageBaseImpl_StreamReadAt(This->scratch,
6238 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6240 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6242 /* This stream doesn't live in the parent, and we haven't allocated storage
6243 * for it yet */
6244 *bytesRead = 0;
6245 return S_OK;
6247 else
6249 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6250 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6254 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6255 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6257 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6258 HRESULT hr;
6260 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6261 if (FAILED(hr))
6263 TRACE("<-- %#lx\n", hr);
6264 return hr;
6267 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6268 if (FAILED(hr))
6270 TRACE("<-- %#lx\n", hr);
6271 return hr;
6274 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6275 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6277 if (SUCCEEDED(hr) && size != 0)
6278 This->entries[index].data.size.QuadPart = max(
6279 This->entries[index].data.size.QuadPart,
6280 offset.QuadPart + size);
6282 TRACE("<-- %#lx\n", hr);
6283 return hr;
6286 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6287 DirRef index, ULARGE_INTEGER newsize)
6289 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6290 HRESULT hr;
6292 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6293 if (FAILED(hr))
6295 TRACE("<-- %#lx\n", hr);
6296 return hr;
6299 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6300 return S_OK;
6302 if (newsize.QuadPart == 0)
6304 /* Destroy any parent references or entries in the scratch file. */
6305 if (This->entries[index].stream_dirty)
6307 ULARGE_INTEGER zero;
6308 zero.QuadPart = 0;
6309 StorageBaseImpl_StreamSetSize(This->scratch,
6310 This->entries[index].stream_entry, zero);
6311 StorageBaseImpl_DestroyDirEntry(This->scratch,
6312 This->entries[index].stream_entry);
6313 This->entries[index].stream_dirty = FALSE;
6315 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6317 DirRef delete_ref;
6318 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6320 if (delete_ref != DIRENTRY_NULL)
6321 This->entries[delete_ref].deleted = TRUE;
6323 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6326 else
6328 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6329 if (FAILED(hr)) return hr;
6331 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6332 This->entries[index].stream_entry, newsize);
6335 if (SUCCEEDED(hr))
6336 This->entries[index].data.size = newsize;
6338 TRACE("<-- %#lx\n", hr);
6339 return hr;
6342 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6343 DirRef dst, DirRef src)
6345 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6346 HRESULT hr;
6347 TransactedDirEntry *dst_entry, *src_entry;
6349 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6350 if (FAILED(hr))
6352 TRACE("<-- %#lx\n", hr);
6353 return hr;
6356 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6357 if (FAILED(hr))
6359 TRACE("<-- %#lx\n", hr);
6360 return hr;
6363 dst_entry = &This->entries[dst];
6364 src_entry = &This->entries[src];
6366 dst_entry->stream_dirty = src_entry->stream_dirty;
6367 dst_entry->stream_entry = src_entry->stream_entry;
6368 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6369 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6370 dst_entry->data.size = src_entry->data.size;
6372 return S_OK;
6375 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6376 ULONG* result, BOOL refresh)
6378 return E_NOTIMPL;
6381 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6382 ULONG value)
6384 return E_NOTIMPL;
6387 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6389 return E_NOTIMPL;
6392 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6394 return E_NOTIMPL;
6397 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6399 StorageBaseImpl_QueryInterface,
6400 StorageBaseImpl_AddRef,
6401 StorageBaseImpl_Release,
6402 StorageBaseImpl_CreateStream,
6403 StorageBaseImpl_OpenStream,
6404 StorageBaseImpl_CreateStorage,
6405 StorageBaseImpl_OpenStorage,
6406 StorageBaseImpl_CopyTo,
6407 StorageBaseImpl_MoveElementTo,
6408 TransactedSnapshotImpl_Commit,
6409 TransactedSnapshotImpl_Revert,
6410 StorageBaseImpl_EnumElements,
6411 StorageBaseImpl_DestroyElement,
6412 StorageBaseImpl_RenameElement,
6413 StorageBaseImpl_SetElementTimes,
6414 StorageBaseImpl_SetClass,
6415 StorageBaseImpl_SetStateBits,
6416 StorageBaseImpl_Stat
6419 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6421 TransactedSnapshotImpl_Destroy,
6422 TransactedSnapshotImpl_Invalidate,
6423 TransactedSnapshotImpl_Flush,
6424 TransactedSnapshotImpl_GetFilename,
6425 TransactedSnapshotImpl_CreateDirEntry,
6426 TransactedSnapshotImpl_WriteDirEntry,
6427 TransactedSnapshotImpl_ReadDirEntry,
6428 TransactedSnapshotImpl_DestroyDirEntry,
6429 TransactedSnapshotImpl_StreamReadAt,
6430 TransactedSnapshotImpl_StreamWriteAt,
6431 TransactedSnapshotImpl_StreamSetSize,
6432 TransactedSnapshotImpl_StreamLink,
6433 TransactedSnapshotImpl_GetTransactionSig,
6434 TransactedSnapshotImpl_SetTransactionSig,
6435 TransactedSnapshotImpl_LockTransaction,
6436 TransactedSnapshotImpl_UnlockTransaction
6439 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6440 TransactedSnapshotImpl** result)
6442 HRESULT hr;
6444 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6445 if (*result)
6447 IStorage *scratch;
6449 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6451 /* This is OK because the property set storage functions use the IStorage functions. */
6452 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6453 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6455 list_init(&(*result)->base.strmHead);
6457 list_init(&(*result)->base.storageHead);
6459 (*result)->base.ref = 1;
6461 (*result)->base.openFlags = parentStorage->openFlags;
6463 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6464 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6466 /* Create a new temporary storage to act as the scratch file. */
6467 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6468 0, &scratch);
6469 (*result)->scratch = impl_from_IStorage(scratch);
6471 if (SUCCEEDED(hr))
6473 ULONG num_entries = 20;
6475 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6476 (*result)->entries_size = num_entries;
6477 (*result)->firstFreeEntry = 0;
6479 if ((*result)->entries)
6481 /* parentStorage already has 1 reference, which we take over here. */
6482 (*result)->transactedParent = parentStorage;
6484 parentStorage->transactedChild = &(*result)->base;
6486 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6488 else
6490 IStorage_Release(scratch);
6492 hr = E_OUTOFMEMORY;
6496 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6498 return hr;
6500 else
6501 return E_OUTOFMEMORY;
6505 /************************************************************************
6506 * TransactedSharedImpl implementation
6507 ***********************************************************************/
6509 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6511 if (!This->reverted)
6513 TRACE("Storage invalidated (stg=%p)\n", This);
6515 This->reverted = TRUE;
6517 StorageBaseImpl_DeleteAll(This);
6521 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6523 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6525 TransactedSharedImpl_Invalidate(&This->base);
6526 IStorage_Release(&This->transactedParent->IStorage_iface);
6527 IStorage_Release(&This->scratch->base.IStorage_iface);
6528 HeapFree(GetProcessHeap(), 0, This);
6531 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6533 /* We only need to flush when committing. */
6534 return S_OK;
6537 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6539 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6541 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6544 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6545 const DirEntry *newData, DirRef *index)
6547 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6549 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6550 newData, index);
6553 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6554 DirRef index, const DirEntry *data)
6556 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6558 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6559 index, data);
6562 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6563 DirRef index, DirEntry *data)
6565 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6567 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6568 index, data);
6571 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6572 DirRef index)
6574 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6576 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6577 index);
6580 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6581 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6583 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6585 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6586 index, offset, size, buffer, bytesRead);
6589 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6590 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6592 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6594 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6595 index, offset, size, buffer, bytesWritten);
6598 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6599 DirRef index, ULARGE_INTEGER newsize)
6601 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6603 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6604 index, newsize);
6607 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6608 DirRef dst, DirRef src)
6610 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6612 return StorageBaseImpl_StreamLink(&This->scratch->base,
6613 dst, src);
6616 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6617 ULONG* result, BOOL refresh)
6619 return E_NOTIMPL;
6622 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6623 ULONG value)
6625 return E_NOTIMPL;
6628 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6630 return E_NOTIMPL;
6633 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6635 return E_NOTIMPL;
6638 static HRESULT WINAPI TransactedSharedImpl_Commit(
6639 IStorage* iface,
6640 DWORD grfCommitFlags) /* [in] */
6642 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6643 DirRef new_storage_ref, prev_storage_ref;
6644 DirEntry src_data, dst_data;
6645 HRESULT hr;
6646 ULONG transactionSig;
6648 TRACE("%p, %#lx\n", iface, grfCommitFlags);
6650 /* Cannot commit a read-only transacted storage */
6651 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6652 return STG_E_ACCESSDENIED;
6654 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6655 if (hr == E_NOTIMPL) hr = S_OK;
6656 if (SUCCEEDED(hr))
6658 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6659 if (SUCCEEDED(hr))
6661 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6662 hr = STG_E_NOTCURRENT;
6664 if (SUCCEEDED(hr))
6665 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6667 else if (hr == E_NOTIMPL)
6668 hr = S_OK;
6670 if (SUCCEEDED(hr))
6671 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6673 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6674 if (SUCCEEDED(hr))
6675 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6677 if (SUCCEEDED(hr))
6678 hr = StorageBaseImpl_Flush(This->transactedParent);
6680 if (SUCCEEDED(hr))
6681 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6683 if (SUCCEEDED(hr))
6685 prev_storage_ref = dst_data.dirRootEntry;
6686 dst_data.dirRootEntry = new_storage_ref;
6687 dst_data.clsid = src_data.clsid;
6688 dst_data.ctime = src_data.ctime;
6689 dst_data.mtime = src_data.mtime;
6690 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6693 if (SUCCEEDED(hr))
6695 /* Try to flush after updating the root storage, but if the flush fails, keep
6696 * going, on the theory that it'll either succeed later or the subsequent
6697 * writes will fail. */
6698 StorageBaseImpl_Flush(This->transactedParent);
6700 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6703 if (SUCCEEDED(hr))
6704 hr = StorageBaseImpl_Flush(This->transactedParent);
6706 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6708 if (SUCCEEDED(hr))
6709 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6711 if (SUCCEEDED(hr))
6713 This->lastTransactionSig = transactionSig+1;
6716 TRACE("<-- %#lx\n", hr);
6717 return hr;
6720 static HRESULT WINAPI TransactedSharedImpl_Revert(
6721 IStorage* iface)
6723 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6725 TRACE("(%p)\n", iface);
6727 /* Destroy the open objects. */
6728 StorageBaseImpl_DeleteAll(&This->base);
6730 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6733 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6735 StorageBaseImpl_QueryInterface,
6736 StorageBaseImpl_AddRef,
6737 StorageBaseImpl_Release,
6738 StorageBaseImpl_CreateStream,
6739 StorageBaseImpl_OpenStream,
6740 StorageBaseImpl_CreateStorage,
6741 StorageBaseImpl_OpenStorage,
6742 StorageBaseImpl_CopyTo,
6743 StorageBaseImpl_MoveElementTo,
6744 TransactedSharedImpl_Commit,
6745 TransactedSharedImpl_Revert,
6746 StorageBaseImpl_EnumElements,
6747 StorageBaseImpl_DestroyElement,
6748 StorageBaseImpl_RenameElement,
6749 StorageBaseImpl_SetElementTimes,
6750 StorageBaseImpl_SetClass,
6751 StorageBaseImpl_SetStateBits,
6752 StorageBaseImpl_Stat
6755 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6757 TransactedSharedImpl_Destroy,
6758 TransactedSharedImpl_Invalidate,
6759 TransactedSharedImpl_Flush,
6760 TransactedSharedImpl_GetFilename,
6761 TransactedSharedImpl_CreateDirEntry,
6762 TransactedSharedImpl_WriteDirEntry,
6763 TransactedSharedImpl_ReadDirEntry,
6764 TransactedSharedImpl_DestroyDirEntry,
6765 TransactedSharedImpl_StreamReadAt,
6766 TransactedSharedImpl_StreamWriteAt,
6767 TransactedSharedImpl_StreamSetSize,
6768 TransactedSharedImpl_StreamLink,
6769 TransactedSharedImpl_GetTransactionSig,
6770 TransactedSharedImpl_SetTransactionSig,
6771 TransactedSharedImpl_LockTransaction,
6772 TransactedSharedImpl_UnlockTransaction
6775 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6776 TransactedSharedImpl** result)
6778 HRESULT hr;
6780 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6781 if (*result)
6783 IStorage *scratch;
6785 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6787 /* This is OK because the property set storage functions use the IStorage functions. */
6788 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6789 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6791 list_init(&(*result)->base.strmHead);
6793 list_init(&(*result)->base.storageHead);
6795 (*result)->base.ref = 1;
6797 (*result)->base.openFlags = parentStorage->openFlags;
6799 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6801 if (SUCCEEDED(hr))
6803 STGOPTIONS stgo;
6805 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6806 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6808 stgo.usVersion = 1;
6809 stgo.reserved = 0;
6810 stgo.ulSectorSize = 4096;
6811 stgo.pwcsTemplateFile = NULL;
6813 /* Create a new temporary storage to act as the scratch file. */
6814 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6815 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6816 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6818 if (SUCCEEDED(hr))
6820 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6821 parentStorage, parentStorage->storageDirEntry);
6823 if (SUCCEEDED(hr))
6825 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6827 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6828 (*result)->transactedParent = parentStorage;
6831 if (FAILED(hr))
6832 IStorage_Release(scratch);
6835 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6838 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6840 return hr;
6842 else
6843 return E_OUTOFMEMORY;
6846 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6847 BOOL toplevel, StorageBaseImpl** result)
6849 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6851 if (parentStorage->openFlags & fixme_flags)
6853 fixme_flags &= ~parentStorage->openFlags;
6854 FIXME("Unimplemented flags %lx\n", parentStorage->openFlags);
6857 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6858 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6859 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6861 /* Need to create a temp file for the snapshot */
6862 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6865 return TransactedSnapshotImpl_Construct(parentStorage,
6866 (TransactedSnapshotImpl**)result);
6869 static HRESULT Storage_Construct(
6870 HANDLE hFile,
6871 LPCOLESTR pwcsName,
6872 ILockBytes* pLkbyt,
6873 DWORD openFlags,
6874 BOOL fileBased,
6875 BOOL create,
6876 ULONG sector_size,
6877 StorageBaseImpl** result)
6879 StorageImpl *newStorage;
6880 StorageBaseImpl *newTransactedStorage;
6881 HRESULT hr;
6883 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6884 if (FAILED(hr)) goto end;
6886 if (openFlags & STGM_TRANSACTED)
6888 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6889 if (FAILED(hr))
6890 IStorage_Release(&newStorage->base.IStorage_iface);
6891 else
6892 *result = newTransactedStorage;
6894 else
6895 *result = &newStorage->base;
6897 end:
6898 return hr;
6902 /************************************************************************
6903 * StorageUtl helper functions
6904 ***********************************************************************/
6906 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6908 WORD tmp;
6910 memcpy(&tmp, buffer+offset, sizeof(WORD));
6911 *value = lendian16toh(tmp);
6914 void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value)
6916 value = htole16(value);
6917 memcpy((BYTE *)buffer + offset, &value, sizeof(WORD));
6920 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6922 DWORD tmp;
6924 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6925 *value = lendian32toh(tmp);
6928 void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value)
6930 value = htole32(value);
6931 memcpy((BYTE *)buffer + offset, &value, sizeof(DWORD));
6934 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6935 ULARGE_INTEGER* value)
6937 #ifdef WORDS_BIGENDIAN
6938 ULARGE_INTEGER tmp;
6940 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6941 value->u.LowPart = htole32(tmp.u.HighPart);
6942 value->u.HighPart = htole32(tmp.u.LowPart);
6943 #else
6944 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6945 #endif
6948 void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value)
6950 #ifdef WORDS_BIGENDIAN
6951 ULARGE_INTEGER tmp;
6953 tmp.u.LowPart = htole32(value->u.HighPart);
6954 tmp.u.HighPart = htole32(value->u.LowPart);
6955 memcpy((BYTE *)buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6956 #else
6957 memcpy((BYTE *)buffer + offset, value, sizeof(ULARGE_INTEGER));
6958 #endif
6961 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6963 StorageUtl_ReadDWord(buffer, offset, (DWORD *)&value->Data1);
6964 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6965 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6967 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6970 void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value)
6972 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6973 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6974 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6976 memcpy((BYTE *)buffer + offset + 8, value->Data4, sizeof(value->Data4));
6979 void StorageUtl_CopyDirEntryToSTATSTG(
6980 StorageBaseImpl* storage,
6981 STATSTG* destination,
6982 const DirEntry* source,
6983 int statFlags)
6986 * The copy of the string occurs only when the flag is not set
6988 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6990 /* Use the filename for the root storage. */
6991 destination->pwcsName = 0;
6992 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6994 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6995 (source->name[0] == 0) )
6997 destination->pwcsName = 0;
6999 else
7001 destination->pwcsName =
7002 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
7004 lstrcpyW(destination->pwcsName, source->name);
7007 switch (source->stgType)
7009 case STGTY_STORAGE:
7010 case STGTY_ROOT:
7011 destination->type = STGTY_STORAGE;
7012 break;
7013 case STGTY_STREAM:
7014 destination->type = STGTY_STREAM;
7015 break;
7016 default:
7017 destination->type = STGTY_STREAM;
7018 break;
7021 destination->cbSize = source->size;
7023 currentReturnStruct->mtime = {0}; TODO
7024 currentReturnStruct->ctime = {0};
7025 currentReturnStruct->atime = {0};
7027 destination->grfMode = 0;
7028 destination->grfLocksSupported = 0;
7029 destination->clsid = source->clsid;
7030 destination->grfStateBits = 0;
7031 destination->reserved = 0;
7035 /************************************************************************
7036 * BlockChainStream implementation
7037 ***********************************************************************/
7039 /******************************************************************************
7040 * BlockChainStream_GetHeadOfChain
7042 * Returns the head of this stream chain.
7043 * Some special chains don't have directory entries, their heads are kept in
7044 * This->headOfStreamPlaceHolder.
7047 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7049 DirEntry chainEntry;
7050 HRESULT hr;
7052 if (This->headOfStreamPlaceHolder != 0)
7053 return *(This->headOfStreamPlaceHolder);
7055 if (This->ownerDirEntry != DIRENTRY_NULL)
7057 hr = StorageImpl_ReadDirEntry(
7058 This->parentStorage,
7059 This->ownerDirEntry,
7060 &chainEntry);
7062 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7063 return chainEntry.startingBlock;
7066 return BLOCK_END_OF_CHAIN;
7069 /* Read and save the index of all blocks in this stream. */
7070 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7072 ULONG next_sector, next_offset;
7073 HRESULT hr;
7074 struct BlockChainRun *last_run;
7076 if (This->indexCacheLen == 0)
7078 last_run = NULL;
7079 next_offset = 0;
7080 next_sector = BlockChainStream_GetHeadOfChain(This);
7082 else
7084 last_run = &This->indexCache[This->indexCacheLen-1];
7085 next_offset = last_run->lastOffset+1;
7086 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7087 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7088 &next_sector);
7089 if (FAILED(hr)) return hr;
7092 while (next_sector != BLOCK_END_OF_CHAIN)
7094 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7096 /* Add the current block to the cache. */
7097 if (This->indexCacheSize == 0)
7099 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7100 if (!This->indexCache) return E_OUTOFMEMORY;
7101 This->indexCacheSize = 16;
7103 else if (This->indexCacheSize == This->indexCacheLen)
7105 struct BlockChainRun *new_cache;
7106 ULONG new_size;
7108 new_size = This->indexCacheSize * 2;
7109 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7110 if (!new_cache) return E_OUTOFMEMORY;
7111 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7113 HeapFree(GetProcessHeap(), 0, This->indexCache);
7114 This->indexCache = new_cache;
7115 This->indexCacheSize = new_size;
7118 This->indexCacheLen++;
7119 last_run = &This->indexCache[This->indexCacheLen-1];
7120 last_run->firstSector = next_sector;
7121 last_run->firstOffset = next_offset;
7124 last_run->lastOffset = next_offset;
7126 /* Find the next block. */
7127 next_offset++;
7128 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7129 if (FAILED(hr)) return hr;
7132 if (This->indexCacheLen)
7134 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7135 This->numBlocks = last_run->lastOffset+1;
7137 else
7139 This->tailIndex = BLOCK_END_OF_CHAIN;
7140 This->numBlocks = 0;
7143 return S_OK;
7146 /* Locate the nth block in this stream. */
7147 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7149 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7150 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7152 if (offset >= This->numBlocks)
7153 return BLOCK_END_OF_CHAIN;
7155 while (min_run < max_run)
7157 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7158 if (offset < This->indexCache[run_to_check].firstOffset)
7160 max_offset = This->indexCache[run_to_check].firstOffset-1;
7161 max_run = run_to_check-1;
7163 else if (offset > This->indexCache[run_to_check].lastOffset)
7165 min_offset = This->indexCache[run_to_check].lastOffset+1;
7166 min_run = run_to_check+1;
7168 else
7169 /* Block is in this run. */
7170 min_run = max_run = run_to_check;
7173 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7176 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7177 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7179 BlockChainBlock *result=NULL;
7180 int i;
7182 for (i=0; i<2; i++)
7183 if (This->cachedBlocks[i].index == index)
7185 *sector = This->cachedBlocks[i].sector;
7186 *block = &This->cachedBlocks[i];
7187 return S_OK;
7190 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7191 if (*sector == BLOCK_END_OF_CHAIN)
7192 return STG_E_DOCFILECORRUPT;
7194 if (create)
7196 if (This->cachedBlocks[0].index == 0xffffffff)
7197 result = &This->cachedBlocks[0];
7198 else if (This->cachedBlocks[1].index == 0xffffffff)
7199 result = &This->cachedBlocks[1];
7200 else
7202 result = &This->cachedBlocks[This->blockToEvict++];
7203 if (This->blockToEvict == 2)
7204 This->blockToEvict = 0;
7207 if (result->dirty)
7209 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7210 return STG_E_WRITEFAULT;
7211 result->dirty = FALSE;
7214 result->read = FALSE;
7215 result->index = index;
7216 result->sector = *sector;
7219 *block = result;
7220 return S_OK;
7223 BlockChainStream* BlockChainStream_Construct(
7224 StorageImpl* parentStorage,
7225 ULONG* headOfStreamPlaceHolder,
7226 DirRef dirEntry)
7228 BlockChainStream* newStream;
7230 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7231 if(!newStream)
7232 return NULL;
7234 newStream->parentStorage = parentStorage;
7235 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7236 newStream->ownerDirEntry = dirEntry;
7237 newStream->indexCache = NULL;
7238 newStream->indexCacheLen = 0;
7239 newStream->indexCacheSize = 0;
7240 newStream->cachedBlocks[0].index = 0xffffffff;
7241 newStream->cachedBlocks[0].dirty = FALSE;
7242 newStream->cachedBlocks[1].index = 0xffffffff;
7243 newStream->cachedBlocks[1].dirty = FALSE;
7244 newStream->blockToEvict = 0;
7246 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7248 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7249 HeapFree(GetProcessHeap(), 0, newStream);
7250 return NULL;
7253 return newStream;
7256 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7258 int i;
7259 if (!This) return S_OK;
7260 for (i=0; i<2; i++)
7262 if (This->cachedBlocks[i].dirty)
7264 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7265 This->cachedBlocks[i].dirty = FALSE;
7266 else
7267 return STG_E_WRITEFAULT;
7270 return S_OK;
7273 void BlockChainStream_Destroy(BlockChainStream* This)
7275 if (This)
7277 BlockChainStream_Flush(This);
7278 HeapFree(GetProcessHeap(), 0, This->indexCache);
7280 HeapFree(GetProcessHeap(), 0, This);
7283 /******************************************************************************
7284 * BlockChainStream_Shrink
7286 * Shrinks this chain in the big block depot.
7288 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7289 ULARGE_INTEGER newSize)
7291 ULONG blockIndex;
7292 ULONG numBlocks;
7293 int i;
7296 * Figure out how many blocks are needed to contain the new size
7298 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7300 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7301 numBlocks++;
7303 if (numBlocks)
7306 * Go to the new end of chain
7308 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7310 /* Mark the new end of chain */
7311 StorageImpl_SetNextBlockInChain(
7312 This->parentStorage,
7313 blockIndex,
7314 BLOCK_END_OF_CHAIN);
7316 This->tailIndex = blockIndex;
7318 else
7320 if (This->headOfStreamPlaceHolder != 0)
7322 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7324 else
7326 DirEntry chainEntry;
7327 assert(This->ownerDirEntry != DIRENTRY_NULL);
7329 StorageImpl_ReadDirEntry(
7330 This->parentStorage,
7331 This->ownerDirEntry,
7332 &chainEntry);
7334 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7336 StorageImpl_WriteDirEntry(
7337 This->parentStorage,
7338 This->ownerDirEntry,
7339 &chainEntry);
7342 This->tailIndex = BLOCK_END_OF_CHAIN;
7345 This->numBlocks = numBlocks;
7348 * Mark the extra blocks as free
7350 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7352 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7353 StorageImpl_FreeBigBlock(This->parentStorage,
7354 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7355 if (last_run->lastOffset == last_run->firstOffset)
7356 This->indexCacheLen--;
7357 else
7358 last_run->lastOffset--;
7362 * Reset the last accessed block cache.
7364 for (i=0; i<2; i++)
7366 if (This->cachedBlocks[i].index >= numBlocks)
7368 This->cachedBlocks[i].index = 0xffffffff;
7369 This->cachedBlocks[i].dirty = FALSE;
7373 return TRUE;
7376 /******************************************************************************
7377 * BlockChainStream_Enlarge
7379 * Grows this chain in the big block depot.
7381 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7382 ULARGE_INTEGER newSize)
7384 ULONG blockIndex, currentBlock;
7385 ULONG newNumBlocks;
7386 ULONG oldNumBlocks = 0;
7388 blockIndex = BlockChainStream_GetHeadOfChain(This);
7391 * Empty chain. Create the head.
7393 if (blockIndex == BLOCK_END_OF_CHAIN)
7395 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, 1);
7396 StorageImpl_SetNextBlockInChain(This->parentStorage,
7397 blockIndex,
7398 BLOCK_END_OF_CHAIN);
7400 if (This->headOfStreamPlaceHolder != 0)
7402 *(This->headOfStreamPlaceHolder) = blockIndex;
7404 else
7406 DirEntry chainEntry;
7407 assert(This->ownerDirEntry != DIRENTRY_NULL);
7409 StorageImpl_ReadDirEntry(
7410 This->parentStorage,
7411 This->ownerDirEntry,
7412 &chainEntry);
7414 chainEntry.startingBlock = blockIndex;
7416 StorageImpl_WriteDirEntry(
7417 This->parentStorage,
7418 This->ownerDirEntry,
7419 &chainEntry);
7422 This->tailIndex = blockIndex;
7423 This->numBlocks = 1;
7427 * Figure out how many blocks are needed to contain this stream
7429 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7431 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7432 newNumBlocks++;
7435 * Go to the current end of chain
7437 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7439 currentBlock = blockIndex;
7441 while (blockIndex != BLOCK_END_OF_CHAIN)
7443 This->numBlocks++;
7444 currentBlock = blockIndex;
7446 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7447 &blockIndex)))
7448 return FALSE;
7451 This->tailIndex = currentBlock;
7454 currentBlock = This->tailIndex;
7455 oldNumBlocks = This->numBlocks;
7458 * Add new blocks to the chain
7460 if (oldNumBlocks < newNumBlocks)
7462 while (oldNumBlocks < newNumBlocks)
7464 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, newNumBlocks - oldNumBlocks);
7466 StorageImpl_SetNextBlockInChain(
7467 This->parentStorage,
7468 currentBlock,
7469 blockIndex);
7471 StorageImpl_SetNextBlockInChain(
7472 This->parentStorage,
7473 blockIndex,
7474 BLOCK_END_OF_CHAIN);
7476 currentBlock = blockIndex;
7477 oldNumBlocks++;
7480 This->tailIndex = blockIndex;
7481 This->numBlocks = newNumBlocks;
7484 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7485 return FALSE;
7487 return TRUE;
7491 /******************************************************************************
7492 * BlockChainStream_GetSize
7494 * Returns the size of this chain.
7495 * Will return the block count if this chain doesn't have a directory entry.
7497 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7499 DirEntry chainEntry;
7501 if(This->headOfStreamPlaceHolder == NULL)
7504 * This chain has a directory entry so use the size value from there.
7506 StorageImpl_ReadDirEntry(
7507 This->parentStorage,
7508 This->ownerDirEntry,
7509 &chainEntry);
7511 return chainEntry.size;
7513 else
7516 * this chain is a chain that does not have a directory entry, figure out the
7517 * size by making the product number of used blocks times the
7518 * size of them
7520 ULARGE_INTEGER result;
7521 result.QuadPart =
7522 (ULONGLONG)BlockChainStream_GetCount(This) *
7523 This->parentStorage->bigBlockSize;
7525 return result;
7529 /******************************************************************************
7530 * BlockChainStream_SetSize
7532 * Sets the size of this stream. The big block depot will be updated.
7533 * The file will grow if we grow the chain.
7535 * TODO: Free the actual blocks in the file when we shrink the chain.
7536 * Currently, the blocks are still in the file. So the file size
7537 * doesn't shrink even if we shrink streams.
7539 BOOL BlockChainStream_SetSize(
7540 BlockChainStream* This,
7541 ULARGE_INTEGER newSize)
7543 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7545 if (newSize.QuadPart == size.QuadPart)
7546 return TRUE;
7548 if (newSize.QuadPart < size.QuadPart)
7550 BlockChainStream_Shrink(This, newSize);
7552 else
7554 BlockChainStream_Enlarge(This, newSize);
7557 return TRUE;
7560 /******************************************************************************
7561 * BlockChainStream_ReadAt
7563 * Reads a specified number of bytes from this chain at the specified offset.
7564 * bytesRead may be NULL.
7565 * Failure will be returned if the specified number of bytes has not been read.
7567 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7568 ULARGE_INTEGER offset,
7569 ULONG size,
7570 void* buffer,
7571 ULONG* bytesRead)
7573 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7574 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7575 ULONG bytesToReadInBuffer;
7576 ULONG blockIndex;
7577 BYTE* bufferWalker;
7578 ULARGE_INTEGER stream_size;
7579 HRESULT hr;
7580 BlockChainBlock *cachedBlock;
7582 TRACE("%p, %li, %p, %lu, %p.\n",This, offset.u.LowPart, buffer, size, bytesRead);
7585 * Find the first block in the stream that contains part of the buffer.
7587 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7589 *bytesRead = 0;
7591 stream_size = BlockChainStream_GetSize(This);
7592 if (stream_size.QuadPart > offset.QuadPart)
7593 size = min(stream_size.QuadPart - offset.QuadPart, size);
7594 else
7595 return S_OK;
7598 * Start reading the buffer.
7600 bufferWalker = buffer;
7602 while (size > 0)
7604 ULARGE_INTEGER ulOffset;
7605 DWORD bytesReadAt;
7608 * Calculate how many bytes we can copy from this big block.
7610 bytesToReadInBuffer =
7611 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7613 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7615 if (FAILED(hr))
7616 return hr;
7618 if (!cachedBlock)
7620 /* Not in cache, and we're going to read past the end of the block. */
7621 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7622 offsetInBlock;
7624 StorageImpl_ReadAt(This->parentStorage,
7625 ulOffset,
7626 bufferWalker,
7627 bytesToReadInBuffer,
7628 &bytesReadAt);
7630 else
7632 if (!cachedBlock->read)
7634 ULONG read;
7635 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7636 return STG_E_READFAULT;
7638 cachedBlock->read = TRUE;
7641 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7642 bytesReadAt = bytesToReadInBuffer;
7645 blockNoInSequence++;
7646 bufferWalker += bytesReadAt;
7647 size -= bytesReadAt;
7648 *bytesRead += bytesReadAt;
7649 offsetInBlock = 0; /* There is no offset on the next block */
7651 if (bytesToReadInBuffer != bytesReadAt)
7652 break;
7655 return S_OK;
7658 /******************************************************************************
7659 * BlockChainStream_WriteAt
7661 * Writes the specified number of bytes to this chain at the specified offset.
7662 * Will fail if not all specified number of bytes have been written.
7664 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7665 ULARGE_INTEGER offset,
7666 ULONG size,
7667 const void* buffer,
7668 ULONG* bytesWritten)
7670 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7671 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7672 ULONG bytesToWrite;
7673 ULONG blockIndex;
7674 const BYTE* bufferWalker;
7675 HRESULT hr;
7676 BlockChainBlock *cachedBlock;
7678 *bytesWritten = 0;
7679 bufferWalker = buffer;
7681 while (size > 0)
7683 ULARGE_INTEGER ulOffset;
7684 DWORD bytesWrittenAt;
7687 * Calculate how many bytes we can copy to this big block.
7689 bytesToWrite =
7690 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7692 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7694 /* BlockChainStream_SetSize should have already been called to ensure we have
7695 * enough blocks in the chain to write into */
7696 if (FAILED(hr))
7698 ERR("not enough blocks in chain to write data\n");
7699 return hr;
7702 if (!cachedBlock)
7704 /* Not in cache, and we're going to write past the end of the block. */
7705 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7706 offsetInBlock;
7708 StorageImpl_WriteAt(This->parentStorage,
7709 ulOffset,
7710 bufferWalker,
7711 bytesToWrite,
7712 &bytesWrittenAt);
7714 else
7716 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7718 ULONG read;
7719 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7720 return STG_E_READFAULT;
7723 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7724 bytesWrittenAt = bytesToWrite;
7725 cachedBlock->read = TRUE;
7726 cachedBlock->dirty = TRUE;
7729 blockNoInSequence++;
7730 bufferWalker += bytesWrittenAt;
7731 size -= bytesWrittenAt;
7732 *bytesWritten += bytesWrittenAt;
7733 offsetInBlock = 0; /* There is no offset on the next block */
7735 if (bytesWrittenAt != bytesToWrite)
7736 break;
7739 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7743 /************************************************************************
7744 * SmallBlockChainStream implementation
7745 ***********************************************************************/
7747 SmallBlockChainStream* SmallBlockChainStream_Construct(
7748 StorageImpl* parentStorage,
7749 ULONG* headOfStreamPlaceHolder,
7750 DirRef dirEntry)
7752 SmallBlockChainStream* newStream;
7754 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7756 newStream->parentStorage = parentStorage;
7757 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7758 newStream->ownerDirEntry = dirEntry;
7760 return newStream;
7763 void SmallBlockChainStream_Destroy(
7764 SmallBlockChainStream* This)
7766 HeapFree(GetProcessHeap(), 0, This);
7769 /******************************************************************************
7770 * SmallBlockChainStream_GetHeadOfChain
7772 * Returns the head of this chain of small blocks.
7774 static ULONG SmallBlockChainStream_GetHeadOfChain(
7775 SmallBlockChainStream* This)
7777 DirEntry chainEntry;
7778 HRESULT hr;
7780 if (This->headOfStreamPlaceHolder != NULL)
7781 return *(This->headOfStreamPlaceHolder);
7783 if (This->ownerDirEntry)
7785 hr = StorageImpl_ReadDirEntry(
7786 This->parentStorage,
7787 This->ownerDirEntry,
7788 &chainEntry);
7790 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7791 return chainEntry.startingBlock;
7794 return BLOCK_END_OF_CHAIN;
7797 /******************************************************************************
7798 * SmallBlockChainStream_GetNextBlockInChain
7800 * Returns the index of the next small block in this chain.
7802 * Return Values:
7803 * - BLOCK_END_OF_CHAIN: end of this chain
7804 * - BLOCK_UNUSED: small block 'blockIndex' is free
7806 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7807 SmallBlockChainStream* This,
7808 ULONG blockIndex,
7809 ULONG* nextBlockInChain)
7811 ULARGE_INTEGER offsetOfBlockInDepot;
7812 DWORD buffer;
7813 ULONG bytesRead;
7814 HRESULT res;
7816 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7818 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7821 * Read those bytes in the buffer from the small block file.
7823 res = BlockChainStream_ReadAt(
7824 This->parentStorage->smallBlockDepotChain,
7825 offsetOfBlockInDepot,
7826 sizeof(DWORD),
7827 &buffer,
7828 &bytesRead);
7830 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7831 res = STG_E_READFAULT;
7833 if (SUCCEEDED(res))
7835 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7836 return S_OK;
7839 return res;
7842 /******************************************************************************
7843 * SmallBlockChainStream_SetNextBlockInChain
7845 * Writes the index of the next block of the specified block in the small
7846 * block depot.
7847 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7848 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7850 static void SmallBlockChainStream_SetNextBlockInChain(
7851 SmallBlockChainStream* This,
7852 ULONG blockIndex,
7853 ULONG nextBlock)
7855 ULARGE_INTEGER offsetOfBlockInDepot;
7856 DWORD buffer;
7857 ULONG bytesWritten;
7859 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7861 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
7864 * Read those bytes in the buffer from the small block file.
7866 BlockChainStream_WriteAt(
7867 This->parentStorage->smallBlockDepotChain,
7868 offsetOfBlockInDepot,
7869 sizeof(DWORD),
7870 &buffer,
7871 &bytesWritten);
7874 /******************************************************************************
7875 * SmallBlockChainStream_FreeBlock
7877 * Flag small block 'blockIndex' as free in the small block depot.
7879 static void SmallBlockChainStream_FreeBlock(
7880 SmallBlockChainStream* This,
7881 ULONG blockIndex)
7883 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7886 /******************************************************************************
7887 * SmallBlockChainStream_GetNextFreeBlock
7889 * Returns the index of a free small block. The small block depot will be
7890 * enlarged if necessary. The small block chain will also be enlarged if
7891 * necessary.
7893 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7894 SmallBlockChainStream* This)
7896 ULARGE_INTEGER offsetOfBlockInDepot;
7897 DWORD buffer;
7898 ULONG bytesRead;
7899 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7900 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7901 HRESULT res = S_OK;
7902 ULONG smallBlocksPerBigBlock;
7903 DirEntry rootEntry;
7904 ULONG blocksRequired;
7905 ULARGE_INTEGER old_size, size_required;
7907 offsetOfBlockInDepot.u.HighPart = 0;
7910 * Scan the small block depot for a free block
7912 while (nextBlockIndex != BLOCK_UNUSED)
7914 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7916 res = BlockChainStream_ReadAt(
7917 This->parentStorage->smallBlockDepotChain,
7918 offsetOfBlockInDepot,
7919 sizeof(DWORD),
7920 &buffer,
7921 &bytesRead);
7924 * If we run out of space for the small block depot, enlarge it
7926 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7928 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7930 if (nextBlockIndex != BLOCK_UNUSED)
7931 blockIndex++;
7933 else
7935 ULONG count =
7936 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7938 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7939 ULARGE_INTEGER newSize, offset;
7940 ULONG bytesWritten;
7942 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7943 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7946 * Initialize all the small blocks to free
7948 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7949 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7950 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7951 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7953 StorageImpl_SaveFileHeader(This->parentStorage);
7957 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7959 smallBlocksPerBigBlock =
7960 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7963 * Verify if we have to allocate big blocks to contain small blocks
7965 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7967 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7969 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7971 if (size_required.QuadPart > old_size.QuadPart)
7973 BlockChainStream_SetSize(
7974 This->parentStorage->smallBlockRootChain,
7975 size_required);
7977 StorageImpl_ReadDirEntry(
7978 This->parentStorage,
7979 This->parentStorage->base.storageDirEntry,
7980 &rootEntry);
7982 rootEntry.size = size_required;
7984 StorageImpl_WriteDirEntry(
7985 This->parentStorage,
7986 This->parentStorage->base.storageDirEntry,
7987 &rootEntry);
7990 return blockIndex;
7993 /******************************************************************************
7994 * SmallBlockChainStream_ReadAt
7996 * Reads a specified number of bytes from this chain at the specified offset.
7997 * bytesRead may be NULL.
7998 * Failure will be returned if the specified number of bytes has not been read.
8000 HRESULT SmallBlockChainStream_ReadAt(
8001 SmallBlockChainStream* This,
8002 ULARGE_INTEGER offset,
8003 ULONG size,
8004 void* buffer,
8005 ULONG* bytesRead)
8007 HRESULT rc = S_OK;
8008 ULARGE_INTEGER offsetInBigBlockFile;
8009 ULONG blockNoInSequence =
8010 offset.u.LowPart / This->parentStorage->smallBlockSize;
8012 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8013 ULONG bytesToReadInBuffer;
8014 ULONG blockIndex;
8015 ULONG bytesReadFromBigBlockFile;
8016 BYTE* bufferWalker;
8017 ULARGE_INTEGER stream_size;
8020 * This should never happen on a small block file.
8022 assert(offset.u.HighPart==0);
8024 *bytesRead = 0;
8026 stream_size = SmallBlockChainStream_GetSize(This);
8027 if (stream_size.QuadPart > offset.QuadPart)
8028 size = min(stream_size.QuadPart - offset.QuadPart, size);
8029 else
8030 return S_OK;
8033 * Find the first block in the stream that contains part of the buffer.
8035 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8037 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8039 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8040 if(FAILED(rc))
8041 return rc;
8042 blockNoInSequence--;
8046 * Start reading the buffer.
8048 bufferWalker = buffer;
8050 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8053 * Calculate how many bytes we can copy from this small block.
8055 bytesToReadInBuffer =
8056 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8059 * Calculate the offset of the small block in the small block file.
8061 offsetInBigBlockFile.QuadPart =
8062 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8064 offsetInBigBlockFile.QuadPart += offsetInBlock;
8067 * Read those bytes in the buffer from the small block file.
8068 * The small block has already been identified so it shouldn't fail
8069 * unless the file is corrupt.
8071 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8072 offsetInBigBlockFile,
8073 bytesToReadInBuffer,
8074 bufferWalker,
8075 &bytesReadFromBigBlockFile);
8077 if (FAILED(rc))
8078 return rc;
8080 if (!bytesReadFromBigBlockFile)
8081 return STG_E_DOCFILECORRUPT;
8084 * Step to the next big block.
8086 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8087 if(FAILED(rc))
8088 return STG_E_DOCFILECORRUPT;
8090 bufferWalker += bytesReadFromBigBlockFile;
8091 size -= bytesReadFromBigBlockFile;
8092 *bytesRead += bytesReadFromBigBlockFile;
8093 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8096 return S_OK;
8099 /******************************************************************************
8100 * SmallBlockChainStream_WriteAt
8102 * Writes the specified number of bytes to this chain at the specified offset.
8103 * Will fail if not all specified number of bytes have been written.
8105 HRESULT SmallBlockChainStream_WriteAt(
8106 SmallBlockChainStream* This,
8107 ULARGE_INTEGER offset,
8108 ULONG size,
8109 const void* buffer,
8110 ULONG* bytesWritten)
8112 ULARGE_INTEGER offsetInBigBlockFile;
8113 ULONG blockNoInSequence =
8114 offset.u.LowPart / This->parentStorage->smallBlockSize;
8116 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8117 ULONG bytesToWriteInBuffer;
8118 ULONG blockIndex;
8119 ULONG bytesWrittenToBigBlockFile;
8120 const BYTE* bufferWalker;
8121 HRESULT res;
8124 * This should never happen on a small block file.
8126 assert(offset.u.HighPart==0);
8129 * Find the first block in the stream that contains part of the buffer.
8131 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8133 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8135 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8136 return STG_E_DOCFILECORRUPT;
8137 blockNoInSequence--;
8141 * Start writing the buffer.
8143 *bytesWritten = 0;
8144 bufferWalker = buffer;
8145 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8148 * Calculate how many bytes we can copy to this small block.
8150 bytesToWriteInBuffer =
8151 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8154 * Calculate the offset of the small block in the small block file.
8156 offsetInBigBlockFile.QuadPart =
8157 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8159 offsetInBigBlockFile.QuadPart += offsetInBlock;
8162 * Write those bytes in the buffer to the small block file.
8164 res = BlockChainStream_WriteAt(
8165 This->parentStorage->smallBlockRootChain,
8166 offsetInBigBlockFile,
8167 bytesToWriteInBuffer,
8168 bufferWalker,
8169 &bytesWrittenToBigBlockFile);
8170 if (FAILED(res))
8171 return res;
8174 * Step to the next big block.
8176 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8177 if (FAILED(res))
8178 return res;
8179 bufferWalker += bytesWrittenToBigBlockFile;
8180 size -= bytesWrittenToBigBlockFile;
8181 *bytesWritten += bytesWrittenToBigBlockFile;
8182 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8185 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8188 /******************************************************************************
8189 * SmallBlockChainStream_Shrink
8191 * Shrinks this chain in the small block depot.
8193 static BOOL SmallBlockChainStream_Shrink(
8194 SmallBlockChainStream* This,
8195 ULARGE_INTEGER newSize)
8197 ULONG blockIndex, extraBlock;
8198 ULONG numBlocks;
8199 ULONG count = 0;
8201 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8203 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8204 numBlocks++;
8206 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8209 * Go to the new end of chain
8211 while (count < numBlocks)
8213 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8214 &blockIndex)))
8215 return FALSE;
8216 count++;
8220 * If the count is 0, we have a special case, the head of the chain was
8221 * just freed.
8223 if (count == 0)
8225 DirEntry chainEntry;
8227 StorageImpl_ReadDirEntry(This->parentStorage,
8228 This->ownerDirEntry,
8229 &chainEntry);
8231 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8233 StorageImpl_WriteDirEntry(This->parentStorage,
8234 This->ownerDirEntry,
8235 &chainEntry);
8238 * We start freeing the chain at the head block.
8240 extraBlock = blockIndex;
8242 else
8244 /* Get the next block before marking the new end */
8245 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8246 &extraBlock)))
8247 return FALSE;
8249 /* Mark the new end of chain */
8250 SmallBlockChainStream_SetNextBlockInChain(
8251 This,
8252 blockIndex,
8253 BLOCK_END_OF_CHAIN);
8257 * Mark the extra blocks as free
8259 while (extraBlock != BLOCK_END_OF_CHAIN)
8261 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8262 &blockIndex)))
8263 return FALSE;
8264 SmallBlockChainStream_FreeBlock(This, extraBlock);
8265 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8266 extraBlock = blockIndex;
8269 return TRUE;
8272 /******************************************************************************
8273 * SmallBlockChainStream_Enlarge
8275 * Grows this chain in the small block depot.
8277 static BOOL SmallBlockChainStream_Enlarge(
8278 SmallBlockChainStream* This,
8279 ULARGE_INTEGER newSize)
8281 ULONG blockIndex, currentBlock;
8282 ULONG newNumBlocks;
8283 ULONG oldNumBlocks = 0;
8285 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8288 * Empty chain. Create the head.
8290 if (blockIndex == BLOCK_END_OF_CHAIN)
8292 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8293 SmallBlockChainStream_SetNextBlockInChain(
8294 This,
8295 blockIndex,
8296 BLOCK_END_OF_CHAIN);
8298 if (This->headOfStreamPlaceHolder != NULL)
8300 *(This->headOfStreamPlaceHolder) = blockIndex;
8302 else
8304 DirEntry chainEntry;
8306 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8307 &chainEntry);
8309 chainEntry.startingBlock = blockIndex;
8311 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8312 &chainEntry);
8316 currentBlock = blockIndex;
8319 * Figure out how many blocks are needed to contain this stream
8321 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8323 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8324 newNumBlocks++;
8327 * Go to the current end of chain
8329 while (blockIndex != BLOCK_END_OF_CHAIN)
8331 oldNumBlocks++;
8332 currentBlock = blockIndex;
8333 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8334 return FALSE;
8338 * Add new blocks to the chain
8340 while (oldNumBlocks < newNumBlocks)
8342 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8343 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8345 SmallBlockChainStream_SetNextBlockInChain(
8346 This,
8347 blockIndex,
8348 BLOCK_END_OF_CHAIN);
8350 currentBlock = blockIndex;
8351 oldNumBlocks++;
8354 return TRUE;
8357 /******************************************************************************
8358 * SmallBlockChainStream_SetSize
8360 * Sets the size of this stream.
8361 * The file will grow if we grow the chain.
8363 * TODO: Free the actual blocks in the file when we shrink the chain.
8364 * Currently, the blocks are still in the file. So the file size
8365 * doesn't shrink even if we shrink streams.
8367 BOOL SmallBlockChainStream_SetSize(
8368 SmallBlockChainStream* This,
8369 ULARGE_INTEGER newSize)
8371 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8373 if (newSize.u.LowPart == size.u.LowPart)
8374 return TRUE;
8376 if (newSize.u.LowPart < size.u.LowPart)
8378 SmallBlockChainStream_Shrink(This, newSize);
8380 else
8382 SmallBlockChainStream_Enlarge(This, newSize);
8385 return TRUE;
8388 /******************************************************************************
8389 * SmallBlockChainStream_GetCount
8391 * Returns the number of small blocks that comprises this chain.
8392 * This is not the size of the stream as the last block may not be full!
8395 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8397 ULONG blockIndex;
8398 ULONG count = 0;
8400 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8402 while(blockIndex != BLOCK_END_OF_CHAIN)
8404 count++;
8406 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8407 blockIndex, &blockIndex)))
8408 return 0;
8411 return count;
8414 /******************************************************************************
8415 * SmallBlockChainStream_GetSize
8417 * Returns the size of this chain.
8419 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8421 DirEntry chainEntry;
8423 if(This->headOfStreamPlaceHolder != NULL)
8425 ULARGE_INTEGER result;
8426 result.u.HighPart = 0;
8428 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8429 This->parentStorage->smallBlockSize;
8431 return result;
8434 StorageImpl_ReadDirEntry(
8435 This->parentStorage,
8436 This->ownerDirEntry,
8437 &chainEntry);
8439 return chainEntry.size;
8443 /************************************************************************
8444 * Miscellaneous storage functions
8445 ***********************************************************************/
8447 static HRESULT create_storagefile(
8448 LPCOLESTR pwcsName,
8449 DWORD grfMode,
8450 DWORD grfAttrs,
8451 STGOPTIONS* pStgOptions,
8452 REFIID riid,
8453 void** ppstgOpen)
8455 StorageBaseImpl* newStorage = 0;
8456 HANDLE hFile = INVALID_HANDLE_VALUE;
8457 HRESULT hr = STG_E_INVALIDFLAG;
8458 DWORD shareMode;
8459 DWORD accessMode;
8460 DWORD creationMode;
8461 DWORD fileAttributes;
8462 WCHAR tempFileName[MAX_PATH];
8464 if (ppstgOpen == 0)
8465 return STG_E_INVALIDPOINTER;
8467 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8468 return STG_E_INVALIDPARAMETER;
8470 /* if no share mode given then DENY_NONE is the default */
8471 if (STGM_SHARE_MODE(grfMode) == 0)
8472 grfMode |= STGM_SHARE_DENY_NONE;
8474 if ( FAILED( validateSTGM(grfMode) ))
8475 goto end;
8477 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8478 switch(STGM_ACCESS_MODE(grfMode))
8480 case STGM_WRITE:
8481 case STGM_READWRITE:
8482 break;
8483 default:
8484 goto end;
8487 /* in direct mode, can only use SHARE_EXCLUSIVE */
8488 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8489 goto end;
8491 /* but in transacted mode, any share mode is valid */
8494 * Generate a unique name.
8496 if (pwcsName == 0)
8498 WCHAR tempPath[MAX_PATH];
8500 memset(tempPath, 0, sizeof(tempPath));
8501 memset(tempFileName, 0, sizeof(tempFileName));
8503 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8504 tempPath[0] = '.';
8506 if (GetTempFileNameW(tempPath, L"STO", 0, tempFileName) != 0)
8507 pwcsName = tempFileName;
8508 else
8510 hr = STG_E_INSUFFICIENTMEMORY;
8511 goto end;
8514 creationMode = TRUNCATE_EXISTING;
8516 else
8518 creationMode = GetCreationModeFromSTGM(grfMode);
8522 * Interpret the STGM value grfMode
8524 shareMode = GetShareModeFromSTGM(grfMode);
8525 accessMode = GetAccessModeFromSTGM(grfMode);
8527 if (grfMode & STGM_DELETEONRELEASE)
8528 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8529 else
8530 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8532 *ppstgOpen = 0;
8534 hFile = CreateFileW(pwcsName,
8535 accessMode,
8536 shareMode,
8537 NULL,
8538 creationMode,
8539 fileAttributes,
8542 if (hFile == INVALID_HANDLE_VALUE)
8544 if(GetLastError() == ERROR_FILE_EXISTS)
8545 hr = STG_E_FILEALREADYEXISTS;
8546 else
8547 hr = E_FAIL;
8548 goto end;
8552 * Allocate and initialize the new IStorage object.
8554 hr = Storage_Construct(
8555 hFile,
8556 pwcsName,
8557 NULL,
8558 grfMode,
8559 TRUE,
8560 TRUE,
8561 pStgOptions->ulSectorSize,
8562 &newStorage);
8564 if (FAILED(hr))
8566 goto end;
8569 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8570 IStorage_Release(&newStorage->IStorage_iface);
8572 end:
8573 TRACE("<-- %p r = %#lx\n", *ppstgOpen, hr);
8575 return hr;
8578 /******************************************************************************
8579 * StgCreateDocfile [OLE32.@]
8580 * Creates a new compound file storage object
8582 * PARAMS
8583 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8584 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8585 * reserved [ ?] unused?, usually 0
8586 * ppstgOpen [IO] A pointer to IStorage pointer to the new object
8588 * RETURNS
8589 * S_OK if the file was successfully created
8590 * some STG_E_ value if error
8591 * NOTES
8592 * if pwcsName is NULL, create file with new unique name
8593 * the function can returns
8594 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8595 * (unrealized now)
8597 HRESULT WINAPI StgCreateDocfile(
8598 LPCOLESTR pwcsName,
8599 DWORD grfMode,
8600 DWORD reserved,
8601 IStorage **ppstgOpen)
8603 STGOPTIONS stgoptions = {1, 0, 512};
8605 TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName), grfMode, reserved, ppstgOpen);
8607 if (ppstgOpen == 0)
8608 return STG_E_INVALIDPOINTER;
8609 if (reserved != 0)
8610 return STG_E_INVALIDPARAMETER;
8612 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8615 /******************************************************************************
8616 * StgCreateStorageEx [OLE32.@]
8618 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8620 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName),
8621 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8623 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8625 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8626 return STG_E_INVALIDPARAMETER;
8629 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8631 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8632 return STG_E_INVALIDPARAMETER;
8635 if (stgfmt == STGFMT_FILE)
8637 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8638 return STG_E_INVALIDPARAMETER;
8641 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8643 STGOPTIONS defaultOptions = {1, 0, 512};
8645 if (!pStgOptions) pStgOptions = &defaultOptions;
8646 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8650 ERR("Invalid stgfmt argument\n");
8651 return STG_E_INVALIDPARAMETER;
8654 /******************************************************************************
8655 * StgCreatePropSetStg [OLE32.@]
8657 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8658 IPropertySetStorage **propset)
8660 TRACE("%p, %#lx, %p.\n", pstg, reserved, propset);
8661 if (reserved)
8662 return STG_E_INVALIDPARAMETER;
8664 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8667 /******************************************************************************
8668 * StgOpenStorageEx [OLE32.@]
8670 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8672 TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName),
8673 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8675 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8677 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8678 return STG_E_INVALIDPARAMETER;
8681 switch (stgfmt)
8683 case STGFMT_FILE:
8684 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8685 return STG_E_INVALIDPARAMETER;
8687 case STGFMT_STORAGE:
8688 break;
8690 case STGFMT_DOCFILE:
8691 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8693 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8694 return STG_E_INVALIDPARAMETER;
8696 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8697 break;
8699 case STGFMT_ANY:
8700 WARN("STGFMT_ANY assuming storage\n");
8701 break;
8703 default:
8704 return STG_E_INVALIDPARAMETER;
8707 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8711 /******************************************************************************
8712 * StgOpenStorage [OLE32.@]
8714 HRESULT WINAPI StgOpenStorage(
8715 const OLECHAR *pwcsName,
8716 IStorage *pstgPriority,
8717 DWORD grfMode,
8718 SNB snbExclude,
8719 DWORD reserved,
8720 IStorage **ppstgOpen)
8722 StorageBaseImpl* newStorage = 0;
8723 HRESULT hr = S_OK;
8724 HANDLE hFile = 0;
8725 DWORD shareMode;
8726 DWORD accessMode;
8727 LPWSTR temp_name = NULL;
8729 TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName), pstgPriority, grfMode,
8730 snbExclude, reserved, ppstgOpen);
8732 if (pstgPriority)
8734 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8735 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8736 if (FAILED(hr)) goto end;
8737 pwcsName = temp_name;
8738 TRACE("using filename %s\n", debugstr_w(temp_name));
8741 if (pwcsName == 0)
8743 hr = STG_E_INVALIDNAME;
8744 goto end;
8747 if (ppstgOpen == 0)
8749 hr = STG_E_INVALIDPOINTER;
8750 goto end;
8753 if (reserved)
8755 hr = STG_E_INVALIDPARAMETER;
8756 goto end;
8759 if (grfMode & STGM_PRIORITY)
8761 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8762 return STG_E_INVALIDFLAG;
8763 if (grfMode & STGM_DELETEONRELEASE)
8764 return STG_E_INVALIDFUNCTION;
8765 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8766 return STG_E_INVALIDFLAG;
8767 grfMode &= ~0xf0; /* remove the existing sharing mode */
8768 grfMode |= STGM_SHARE_DENY_NONE;
8772 * Validate the sharing mode
8774 if (grfMode & STGM_DIRECT_SWMR)
8776 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8777 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8779 hr = STG_E_INVALIDFLAG;
8780 goto end;
8783 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8784 switch(STGM_SHARE_MODE(grfMode))
8786 case STGM_SHARE_EXCLUSIVE:
8787 case STGM_SHARE_DENY_WRITE:
8788 break;
8789 default:
8790 hr = STG_E_INVALIDFLAG;
8791 goto end;
8794 if ( FAILED( validateSTGM(grfMode) ) ||
8795 (grfMode&STGM_CREATE))
8797 hr = STG_E_INVALIDFLAG;
8798 goto end;
8801 /* shared reading requires transacted or single writer mode */
8802 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8803 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8804 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8806 hr = STG_E_INVALIDFLAG;
8807 goto end;
8811 * Interpret the STGM value grfMode
8813 shareMode = GetShareModeFromSTGM(grfMode);
8814 accessMode = GetAccessModeFromSTGM(grfMode);
8816 *ppstgOpen = 0;
8818 hFile = CreateFileW( pwcsName,
8819 accessMode,
8820 shareMode,
8821 NULL,
8822 OPEN_EXISTING,
8823 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8826 if (hFile==INVALID_HANDLE_VALUE)
8828 DWORD last_error = GetLastError();
8830 hr = E_FAIL;
8832 switch (last_error)
8834 case ERROR_FILE_NOT_FOUND:
8835 hr = STG_E_FILENOTFOUND;
8836 break;
8838 case ERROR_PATH_NOT_FOUND:
8839 hr = STG_E_PATHNOTFOUND;
8840 break;
8842 case ERROR_ACCESS_DENIED:
8843 case ERROR_WRITE_PROTECT:
8844 hr = STG_E_ACCESSDENIED;
8845 break;
8847 case ERROR_SHARING_VIOLATION:
8848 hr = STG_E_SHAREVIOLATION;
8849 break;
8851 default:
8852 hr = E_FAIL;
8855 goto end;
8859 * Refuse to open the file if it's too small to be a structured storage file
8860 * FIXME: verify the file when reading instead of here
8862 if (GetFileSize(hFile, NULL) < HEADER_SIZE)
8864 CloseHandle(hFile);
8865 hr = STG_E_FILEALREADYEXISTS;
8866 goto end;
8870 * Allocate and initialize the new IStorage object.
8872 hr = Storage_Construct(
8873 hFile,
8874 pwcsName,
8875 NULL,
8876 grfMode,
8877 TRUE,
8878 FALSE,
8879 512,
8880 &newStorage);
8882 if (FAILED(hr))
8885 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8887 if(hr == STG_E_INVALIDHEADER)
8888 hr = STG_E_FILEALREADYEXISTS;
8889 goto end;
8892 *ppstgOpen = &newStorage->IStorage_iface;
8894 end:
8895 CoTaskMemFree(temp_name);
8896 if (pstgPriority) IStorage_Release(pstgPriority);
8897 TRACE("<-- %#lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8898 return hr;
8901 /******************************************************************************
8902 * StgCreateDocfileOnILockBytes [OLE32.@]
8904 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8905 ILockBytes *plkbyt,
8906 DWORD grfMode,
8907 DWORD reserved,
8908 IStorage** ppstgOpen)
8910 StorageBaseImpl* newStorage = 0;
8911 HRESULT hr = S_OK;
8913 if ((ppstgOpen == 0) || (plkbyt == 0))
8914 return STG_E_INVALIDPOINTER;
8917 * Allocate and initialize the new IStorage object.
8919 hr = Storage_Construct(
8922 plkbyt,
8923 grfMode,
8924 FALSE,
8925 TRUE,
8926 512,
8927 &newStorage);
8929 if (FAILED(hr))
8931 return hr;
8934 *ppstgOpen = &newStorage->IStorage_iface;
8936 return hr;
8939 /******************************************************************************
8940 * StgOpenStorageOnILockBytes [OLE32.@]
8942 HRESULT WINAPI StgOpenStorageOnILockBytes(
8943 ILockBytes *plkbyt,
8944 IStorage *pstgPriority,
8945 DWORD grfMode,
8946 SNB snbExclude,
8947 DWORD reserved,
8948 IStorage **ppstgOpen)
8950 StorageBaseImpl* newStorage = 0;
8951 HRESULT hr = S_OK;
8953 if ((plkbyt == 0) || (ppstgOpen == 0))
8954 return STG_E_INVALIDPOINTER;
8956 if ( FAILED( validateSTGM(grfMode) ))
8957 return STG_E_INVALIDFLAG;
8959 *ppstgOpen = 0;
8962 * Allocate and initialize the new IStorage object.
8964 hr = Storage_Construct(
8967 plkbyt,
8968 grfMode,
8969 FALSE,
8970 FALSE,
8971 512,
8972 &newStorage);
8974 if (FAILED(hr))
8976 return hr;
8979 *ppstgOpen = &newStorage->IStorage_iface;
8981 return hr;
8984 /******************************************************************************
8985 * StgSetTimes [ole32.@]
8986 * StgSetTimes [OLE32.@]
8990 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8991 FILETIME const *patime, FILETIME const *pmtime)
8993 IStorage *stg = NULL;
8994 HRESULT r;
8996 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8998 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8999 0, 0, &stg);
9000 if( SUCCEEDED(r) )
9002 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9003 IStorage_Release(stg);
9006 return r;
9009 /******************************************************************************
9010 * StgIsStorageILockBytes [OLE32.@]
9012 * Determines if the ILockBytes contains a storage object.
9014 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9016 BYTE sig[sizeof(STORAGE_magic)];
9017 ULARGE_INTEGER offset;
9018 ULONG read = 0;
9020 offset.u.HighPart = 0;
9021 offset.u.LowPart = 0;
9023 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9025 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9026 return S_OK;
9028 return S_FALSE;
9031 /******************************************************************************
9032 * WriteClassStg [OLE32.@]
9034 * This method will store the specified CLSID in the specified storage object
9036 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9038 if(!pStg)
9039 return E_INVALIDARG;
9041 if(!rclsid)
9042 return STG_E_INVALIDPOINTER;
9044 return IStorage_SetClass(pStg, rclsid);
9047 /***********************************************************************
9048 * ReadClassStg (OLE32.@)
9050 * This method reads the CLSID previously written to a storage object with
9051 * the WriteClassStg.
9053 * PARAMS
9054 * pstg [I] IStorage pointer
9055 * pclsid [O] Pointer to where the CLSID is written
9057 * RETURNS
9058 * Success: S_OK.
9059 * Failure: HRESULT code.
9061 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9063 STATSTG pstatstg;
9064 HRESULT hRes;
9066 TRACE("(%p, %p)\n", pstg, pclsid);
9068 if(!pstg || !pclsid)
9069 return E_INVALIDARG;
9072 * read a STATSTG structure (contains the clsid) from the storage
9074 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9076 if(SUCCEEDED(hRes))
9077 *pclsid=pstatstg.clsid;
9079 return hRes;
9082 /***********************************************************************
9083 * OleLoadFromStream (OLE32.@)
9085 * This function loads an object from stream
9087 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9089 CLSID clsid;
9090 HRESULT res;
9091 LPPERSISTSTREAM xstm;
9093 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9095 res=ReadClassStm(pStm,&clsid);
9096 if (FAILED(res))
9097 return res;
9098 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9099 if (FAILED(res))
9100 return res;
9101 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9102 if (FAILED(res)) {
9103 IUnknown_Release((IUnknown*)*ppvObj);
9104 return res;
9106 res=IPersistStream_Load(xstm,pStm);
9107 IPersistStream_Release(xstm);
9108 /* FIXME: all refcounts ok at this point? I think they should be:
9109 * pStm : unchanged
9110 * ppvObj : 1
9111 * xstm : 0 (released)
9113 return res;
9116 /***********************************************************************
9117 * OleSaveToStream (OLE32.@)
9119 * This function saves an object with the IPersistStream interface on it
9120 * to the specified stream.
9122 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9125 CLSID clsid;
9126 HRESULT res;
9128 TRACE("(%p,%p)\n",pPStm,pStm);
9130 res=IPersistStream_GetClassID(pPStm,&clsid);
9132 if (SUCCEEDED(res)){
9134 res=WriteClassStm(pStm,&clsid);
9136 if (SUCCEEDED(res))
9138 res=IPersistStream_Save(pPStm,pStm,TRUE);
9141 TRACE("Finished Save\n");
9142 return res;
9145 /*************************************************************************
9146 * STORAGE_CreateOleStream [Internal]
9148 * Creates the "\001OLE" stream in the IStorage if necessary.
9150 * PARAMS
9151 * storage [I] Dest storage to create the stream in
9152 * flags [I] flags to be set for newly created stream
9154 * RETURNS
9155 * HRESULT return value
9157 * NOTES
9159 * This stream is still unknown, MS Word seems to have extra data
9160 * but since the data is stored in the OLESTREAM there should be
9161 * no need to recreate the stream. If the stream is manually
9162 * deleted it will create it with this default data.
9165 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9167 static const DWORD version_magic = 0x02000001;
9168 IStream *stream;
9169 HRESULT hr;
9171 hr = IStorage_CreateStream(storage, L"\1Ole", STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9172 if (hr == S_OK)
9174 struct empty_1ole_stream {
9175 DWORD version_magic;
9176 DWORD flags;
9177 DWORD update_options;
9178 DWORD reserved;
9179 DWORD mon_stream_size;
9181 struct empty_1ole_stream stream_data;
9183 stream_data.version_magic = version_magic;
9184 stream_data.flags = flags;
9185 stream_data.update_options = 0;
9186 stream_data.reserved = 0;
9187 stream_data.mon_stream_size = 0;
9189 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9190 IStream_Release(stream);
9193 return hr;
9196 /* write a string to a stream, preceded by its length */
9197 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9199 HRESULT r;
9200 LPSTR str;
9201 DWORD len = 0;
9203 if( string )
9204 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9205 r = IStream_Write( stm, &len, sizeof(len), NULL);
9206 if( FAILED( r ) )
9207 return r;
9208 if(len == 0)
9209 return r;
9210 str = CoTaskMemAlloc( len );
9211 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9212 r = IStream_Write( stm, str, len, NULL);
9213 CoTaskMemFree( str );
9214 return r;
9217 /* read a string preceded by its length from a stream */
9218 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9220 HRESULT r;
9221 DWORD len, count = 0;
9222 LPSTR str;
9223 LPWSTR wstr;
9225 r = IStream_Read( stm, &len, sizeof(len), &count );
9226 if( FAILED( r ) )
9227 return r;
9228 if( count != sizeof(len) )
9229 return E_OUTOFMEMORY;
9231 TRACE("%ld bytes\n",len);
9233 str = CoTaskMemAlloc( len );
9234 if( !str )
9235 return E_OUTOFMEMORY;
9236 count = 0;
9237 r = IStream_Read( stm, str, len, &count );
9238 if( FAILED( r ) )
9240 CoTaskMemFree( str );
9241 return r;
9243 if( count != len )
9245 CoTaskMemFree( str );
9246 return E_OUTOFMEMORY;
9249 TRACE("Read string %s\n",debugstr_an(str,len));
9251 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9252 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9253 if( wstr )
9255 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9256 wstr[len] = 0;
9258 CoTaskMemFree( str );
9260 *string = wstr;
9262 return r;
9266 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9267 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9269 IStream *pstm;
9270 HRESULT r = S_OK;
9272 static const BYTE unknown1[12] =
9273 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9274 0xFF, 0xFF, 0xFF, 0xFF};
9275 static const BYTE unknown2[16] =
9276 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9279 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9280 debugstr_w(lpszUserType), debugstr_w(szClipName),
9281 debugstr_w(szProgIDName));
9283 /* Create a CompObj stream */
9284 r = IStorage_CreateStream(pstg, L"\1CompObj",
9285 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9286 if( FAILED (r) )
9287 return r;
9289 /* Write CompObj Structure to stream */
9290 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9292 if( SUCCEEDED( r ) )
9293 r = WriteClassStm( pstm, clsid );
9295 if( SUCCEEDED( r ) )
9296 r = STREAM_WriteString( pstm, lpszUserType );
9297 if( SUCCEEDED( r ) )
9298 r = STREAM_WriteString( pstm, szClipName );
9299 if( SUCCEEDED( r ) )
9300 r = STREAM_WriteString( pstm, szProgIDName );
9301 if( SUCCEEDED( r ) )
9302 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9304 IStream_Release( pstm );
9306 return r;
9309 /***********************************************************************
9310 * WriteFmtUserTypeStg (OLE32.@)
9312 HRESULT WINAPI WriteFmtUserTypeStg(
9313 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9315 STATSTG stat;
9316 HRESULT r;
9317 WCHAR szwClipName[0x40];
9318 CLSID clsid;
9319 LPWSTR wstrProgID = NULL;
9320 DWORD n;
9322 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9324 /* get the clipboard format name */
9325 if( cf )
9327 n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName));
9328 szwClipName[n]=0;
9331 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9333 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9334 if(SUCCEEDED(r))
9335 clsid = stat.clsid;
9336 else
9337 clsid = CLSID_NULL;
9339 ProgIDFromCLSID(&clsid, &wstrProgID);
9341 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9343 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9344 cf ? szwClipName : NULL, wstrProgID );
9346 CoTaskMemFree(wstrProgID);
9348 return r;
9352 /******************************************************************************
9353 * ReadFmtUserTypeStg [OLE32.@]
9355 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9357 HRESULT r;
9358 IStream *stm = 0;
9359 unsigned char unknown1[12];
9360 unsigned char unknown2[16];
9361 DWORD count;
9362 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9363 CLSID clsid;
9365 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9367 r = IStorage_OpenStream( pstg, L"\1CompObj", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9368 if( FAILED ( r ) )
9370 WARN("Failed to open stream r = %#lx\n", r);
9371 return r;
9374 /* read the various parts of the structure */
9375 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9376 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9377 goto end;
9378 r = ReadClassStm( stm, &clsid );
9379 if( FAILED( r ) )
9380 goto end;
9382 r = STREAM_ReadString( stm, &szCLSIDName );
9383 if( FAILED( r ) )
9384 goto end;
9386 r = STREAM_ReadString( stm, &szOleTypeName );
9387 if( FAILED( r ) )
9388 goto end;
9390 r = STREAM_ReadString( stm, &szProgIDName );
9391 if( FAILED( r ) )
9392 goto end;
9394 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9395 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9396 goto end;
9398 /* ok, success... now we just need to store what we found */
9399 if( pcf )
9400 *pcf = RegisterClipboardFormatW( szOleTypeName );
9402 if( lplpszUserType )
9404 *lplpszUserType = szCLSIDName;
9405 szCLSIDName = NULL;
9408 end:
9409 CoTaskMemFree( szCLSIDName );
9410 CoTaskMemFree( szOleTypeName );
9411 CoTaskMemFree( szProgIDName );
9412 IStream_Release( stm );
9414 return r;
9417 /******************************************************************************
9418 * StgIsStorageFile [OLE32.@]
9419 * Verify if the file contains a storage object
9421 * PARAMS
9422 * fn [ I] Filename
9424 * RETURNS
9425 * S_OK if file has magic bytes as a storage object
9426 * S_FALSE if file is not storage
9428 HRESULT WINAPI
9429 StgIsStorageFile(LPCOLESTR fn)
9431 HANDLE hf;
9432 BYTE magic[8];
9433 DWORD bytes_read;
9435 TRACE("%s\n", debugstr_w(fn));
9436 hf = CreateFileW(fn, GENERIC_READ,
9437 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9438 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9440 if (hf == INVALID_HANDLE_VALUE)
9441 return STG_E_FILENOTFOUND;
9443 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9445 WARN(" unable to read file\n");
9446 CloseHandle(hf);
9447 return S_FALSE;
9450 CloseHandle(hf);
9452 if (bytes_read != 8) {
9453 TRACE(" too short\n");
9454 return S_FALSE;
9457 if (!memcmp(magic,STORAGE_magic,8)) {
9458 TRACE(" -> YES\n");
9459 return S_OK;
9462 TRACE(" -> Invalid header.\n");
9463 return S_FALSE;
9466 /***********************************************************************
9467 * WriteClassStm (OLE32.@)
9469 * Writes a CLSID to a stream.
9471 * PARAMS
9472 * pStm [I] Stream to write to.
9473 * rclsid [I] CLSID to write.
9475 * RETURNS
9476 * Success: S_OK.
9477 * Failure: HRESULT code.
9479 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9481 TRACE("(%p,%p)\n",pStm,rclsid);
9483 if (!pStm || !rclsid)
9484 return E_INVALIDARG;
9486 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9489 /***********************************************************************
9490 * ReadClassStm (OLE32.@)
9492 * Reads a CLSID from a stream.
9494 * PARAMS
9495 * pStm [I] Stream to read from.
9496 * rclsid [O] CLSID to read.
9498 * RETURNS
9499 * Success: S_OK.
9500 * Failure: HRESULT code.
9502 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9504 ULONG nbByte;
9505 HRESULT res;
9507 TRACE("(%p,%p)\n",pStm,pclsid);
9509 if (!pStm || !pclsid)
9510 return E_INVALIDARG;
9512 /* clear the output args */
9513 *pclsid = CLSID_NULL;
9515 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9517 if (FAILED(res))
9518 return res;
9520 if (nbByte != sizeof(CLSID))
9521 return STG_E_READFAULT;
9522 else
9523 return S_OK;
9527 /************************************************************************
9528 * OleConvert Functions
9529 ***********************************************************************/
9531 #define OLESTREAM_ID 0x501
9532 #define OLESTREAM_MAX_STR_LEN 255
9534 /* OLESTREAM memory structure to use for Get and Put Routines */
9535 typedef struct
9537 DWORD dwOleID;
9538 DWORD dwTypeID;
9539 DWORD dwOleTypeNameLength;
9540 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9541 CHAR *pstrOleObjFileName;
9542 DWORD dwOleObjFileNameLength;
9543 DWORD dwMetaFileWidth;
9544 DWORD dwMetaFileHeight;
9545 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9546 DWORD dwDataLength;
9547 BYTE *pData;
9548 } OLECONVERT_OLESTREAM_DATA;
9550 /* CompObj Stream structure */
9551 typedef struct
9553 BYTE byUnknown1[12];
9554 CLSID clsid;
9555 DWORD dwCLSIDNameLength;
9556 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9557 DWORD dwOleTypeNameLength;
9558 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9559 DWORD dwProgIDNameLength;
9560 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9561 BYTE byUnknown2[16];
9562 } OLECONVERT_ISTORAGE_COMPOBJ;
9564 /* Ole Presentation Stream structure */
9565 typedef struct
9567 BYTE byUnknown1[28];
9568 DWORD dwExtentX;
9569 DWORD dwExtentY;
9570 DWORD dwSize;
9571 BYTE *pData;
9572 } OLECONVERT_ISTORAGE_OLEPRES;
9575 /*************************************************************************
9576 * OLECONVERT_LoadOLE10 [Internal]
9578 * Loads the OLE10 STREAM to memory
9580 * PARAMS
9581 * pOleStream [I] The OLESTREAM
9582 * pData [I] Data Structure for the OLESTREAM Data
9584 * RETURNS
9585 * Success: S_OK
9586 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9587 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9589 * NOTES
9590 * This function is used by OleConvertOLESTREAMToIStorage only.
9592 * Memory allocated for pData must be freed by the caller
9594 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStream1)
9596 DWORD dwSize;
9597 HRESULT hRes = S_OK;
9598 int nTryCnt=0;
9599 int max_try = 6;
9601 pData->pData = NULL;
9602 pData->pstrOleObjFileName = NULL;
9604 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9606 /* Get the OleID */
9607 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9608 if(dwSize != sizeof(pData->dwOleID))
9610 hRes = CONVERT10_E_OLESTREAM_GET;
9612 else if(pData->dwOleID != OLESTREAM_ID)
9614 hRes = CONVERT10_E_OLESTREAM_FMT;
9616 else
9618 hRes = S_OK;
9619 break;
9623 if(hRes == S_OK)
9625 /* Get the TypeID... more info needed for this field */
9626 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9627 if(dwSize != sizeof(pData->dwTypeID))
9629 hRes = CONVERT10_E_OLESTREAM_GET;
9632 if(hRes == S_OK)
9634 if(pData->dwTypeID != 0)
9636 /* Get the length of the OleTypeName */
9637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9638 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9640 hRes = CONVERT10_E_OLESTREAM_GET;
9643 if(hRes == S_OK)
9645 if(pData->dwOleTypeNameLength > 0)
9647 /* Get the OleTypeName */
9648 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9649 if(dwSize != pData->dwOleTypeNameLength)
9651 hRes = CONVERT10_E_OLESTREAM_GET;
9655 if(bStream1)
9657 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9658 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9660 hRes = CONVERT10_E_OLESTREAM_GET;
9662 if(hRes == S_OK)
9664 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9665 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9666 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9667 if(pData->pstrOleObjFileName)
9669 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9670 if(dwSize != pData->dwOleObjFileNameLength)
9672 hRes = CONVERT10_E_OLESTREAM_GET;
9675 else
9676 hRes = CONVERT10_E_OLESTREAM_GET;
9679 else
9681 /* Get the Width of the Metafile */
9682 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9683 if(dwSize != sizeof(pData->dwMetaFileWidth))
9685 hRes = CONVERT10_E_OLESTREAM_GET;
9687 if(hRes == S_OK)
9689 /* Get the Height of the Metafile */
9690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9691 if(dwSize != sizeof(pData->dwMetaFileHeight))
9693 hRes = CONVERT10_E_OLESTREAM_GET;
9697 if(hRes == S_OK)
9699 /* Get the Length of the Data */
9700 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9701 if(dwSize != sizeof(pData->dwDataLength))
9703 hRes = CONVERT10_E_OLESTREAM_GET;
9707 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9709 if(!bStream1) /* if it is a second OLE stream data */
9711 pData->dwDataLength -= 8;
9712 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9713 if(dwSize != sizeof(pData->strUnknown))
9715 hRes = CONVERT10_E_OLESTREAM_GET;
9719 if(hRes == S_OK)
9721 if(pData->dwDataLength > 0)
9723 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9725 /* Get Data (ex. IStorage, Metafile, or BMP) */
9726 if(pData->pData)
9728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9729 if(dwSize != pData->dwDataLength)
9731 hRes = CONVERT10_E_OLESTREAM_GET;
9734 else
9736 hRes = CONVERT10_E_OLESTREAM_GET;
9742 return hRes;
9745 /*************************************************************************
9746 * OLECONVERT_SaveOLE10 [Internal]
9748 * Saves the OLE10 STREAM From memory
9750 * PARAMS
9751 * pData [I] Data Structure for the OLESTREAM Data
9752 * pOleStream [I] The OLESTREAM to save
9754 * RETURNS
9755 * Success: S_OK
9756 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9758 * NOTES
9759 * This function is used by OleConvertIStorageToOLESTREAM only.
9762 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9764 DWORD dwSize;
9765 HRESULT hRes = S_OK;
9768 /* Set the OleID */
9769 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9770 if(dwSize != sizeof(pData->dwOleID))
9772 hRes = CONVERT10_E_OLESTREAM_PUT;
9775 if(hRes == S_OK)
9777 /* Set the TypeID */
9778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9779 if(dwSize != sizeof(pData->dwTypeID))
9781 hRes = CONVERT10_E_OLESTREAM_PUT;
9785 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9787 /* Set the Length of the OleTypeName */
9788 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9789 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9791 hRes = CONVERT10_E_OLESTREAM_PUT;
9794 if(hRes == S_OK)
9796 if(pData->dwOleTypeNameLength > 0)
9798 /* Set the OleTypeName */
9799 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9800 if(dwSize != pData->dwOleTypeNameLength)
9802 hRes = CONVERT10_E_OLESTREAM_PUT;
9807 if(hRes == S_OK)
9809 /* Set the width of the Metafile */
9810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9811 if(dwSize != sizeof(pData->dwMetaFileWidth))
9813 hRes = CONVERT10_E_OLESTREAM_PUT;
9817 if(hRes == S_OK)
9819 /* Set the height of the Metafile */
9820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9821 if(dwSize != sizeof(pData->dwMetaFileHeight))
9823 hRes = CONVERT10_E_OLESTREAM_PUT;
9827 if(hRes == S_OK)
9829 /* Set the length of the Data */
9830 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9831 if(dwSize != sizeof(pData->dwDataLength))
9833 hRes = CONVERT10_E_OLESTREAM_PUT;
9837 if(hRes == S_OK)
9839 if(pData->dwDataLength > 0)
9841 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9842 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9843 if(dwSize != pData->dwDataLength)
9845 hRes = CONVERT10_E_OLESTREAM_PUT;
9850 return hRes;
9853 /*************************************************************************
9854 * OLECONVERT_GetOLE20FromOLE10[Internal]
9856 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9857 * opens it, and copies the content to the dest IStorage for
9858 * OleConvertOLESTREAMToIStorage
9861 * PARAMS
9862 * pDestStorage [I] The IStorage to copy the data to
9863 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9864 * nBufferLength [I] The size of the buffer
9866 * RETURNS
9867 * Nothing
9869 * NOTES
9873 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9875 HRESULT hRes;
9876 HANDLE hFile;
9877 IStorage *pTempStorage;
9878 DWORD dwNumOfBytesWritten;
9879 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9881 /* Create a temp File */
9882 GetTempPathW(MAX_PATH, wstrTempDir);
9883 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9884 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9886 if(hFile != INVALID_HANDLE_VALUE)
9888 /* Write IStorage Data to File */
9889 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9890 CloseHandle(hFile);
9892 /* Open and copy temp storage to the Dest Storage */
9893 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9894 if(hRes == S_OK)
9896 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9897 IStorage_Release(pTempStorage);
9899 DeleteFileW(wstrTempFile);
9904 /*************************************************************************
9905 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9907 * Saves the OLE10 STREAM From memory
9909 * PARAMS
9910 * pStorage [I] The Src IStorage to copy
9911 * pData [I] The Dest Memory to write to.
9913 * RETURNS
9914 * The size in bytes allocated for pData
9916 * NOTES
9917 * Memory allocated for pData must be freed by the caller
9919 * Used by OleConvertIStorageToOLESTREAM only.
9922 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9924 HANDLE hFile;
9925 HRESULT hRes;
9926 DWORD nDataLength = 0;
9927 IStorage *pTempStorage;
9928 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9930 *pData = NULL;
9932 /* Create temp Storage */
9933 GetTempPathW(MAX_PATH, wstrTempDir);
9934 GetTempFileNameW(wstrTempDir, L"sis", 0, wstrTempFile);
9935 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9937 if(hRes == S_OK)
9939 /* Copy Src Storage to the Temp Storage */
9940 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9941 IStorage_Release(pTempStorage);
9943 /* Open Temp Storage as a file and copy to memory */
9944 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9945 if(hFile != INVALID_HANDLE_VALUE)
9947 nDataLength = GetFileSize(hFile, NULL);
9948 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9949 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9950 CloseHandle(hFile);
9952 DeleteFileW(wstrTempFile);
9954 return nDataLength;
9957 /*************************************************************************
9958 * OLECONVERT_CreateCompObjStream [Internal]
9960 * Creates a "\001CompObj" is the destination IStorage if necessary.
9962 * PARAMS
9963 * pStorage [I] The dest IStorage to create the CompObj Stream
9964 * if necessary.
9965 * strOleTypeName [I] The ProgID
9967 * RETURNS
9968 * Success: S_OK
9969 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9971 * NOTES
9972 * This function is used by OleConvertOLESTREAMToIStorage only.
9974 * The stream data is stored in the OLESTREAM and there should be
9975 * no need to recreate the stream. If the stream is manually
9976 * deleted it will attempt to create it by querying the registry.
9980 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9982 IStream *pStream;
9983 HRESULT hStorageRes, hRes = S_OK;
9984 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9985 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9987 static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9988 static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9990 /* Initialize the CompObj structure */
9991 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9992 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9993 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9996 /* Create a CompObj stream if it doesn't exist */
9997 hStorageRes = IStorage_CreateStream(pStorage, L"\1CompObj",
9998 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9999 if(hStorageRes == S_OK)
10001 /* copy the OleTypeName to the compobj struct */
10002 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10003 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10005 /* copy the OleTypeName to the compobj struct */
10006 /* Note: in the test made, these were Identical */
10007 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10008 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10010 /* Get the CLSID */
10011 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10012 bufferW, OLESTREAM_MAX_STR_LEN );
10013 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10015 if(hRes == S_OK)
10017 HKEY hKey;
10018 LONG hErr;
10019 /* Get the CLSID Default Name from the Registry */
10020 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10021 if(hErr == ERROR_SUCCESS)
10023 char strTemp[OLESTREAM_MAX_STR_LEN];
10024 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10025 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10026 if(hErr == ERROR_SUCCESS)
10028 strcpy(IStorageCompObj.strCLSIDName, strTemp);
10030 RegCloseKey(hKey);
10034 /* Write CompObj Structure to stream */
10035 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10037 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10039 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10040 if(IStorageCompObj.dwCLSIDNameLength > 0)
10042 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10044 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10045 if(IStorageCompObj.dwOleTypeNameLength > 0)
10047 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10049 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10050 if(IStorageCompObj.dwProgIDNameLength > 0)
10052 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10054 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10055 IStream_Release(pStream);
10057 return hRes;
10061 /*************************************************************************
10062 * OLECONVERT_CreateOlePresStream[Internal]
10064 * Creates the "\002OlePres000" Stream with the Metafile data
10066 * PARAMS
10067 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10068 * dwExtentX [I] Width of the Metafile
10069 * dwExtentY [I] Height of the Metafile
10070 * pData [I] Metafile data
10071 * dwDataLength [I] Size of the Metafile data
10073 * RETURNS
10074 * Success: S_OK
10075 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10077 * NOTES
10078 * This function is used by OleConvertOLESTREAMToIStorage only.
10081 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10083 HRESULT hRes;
10084 IStream *pStream;
10085 static const BYTE pOlePresStreamHeader[] =
10087 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10088 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10089 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10090 0x00, 0x00, 0x00, 0x00
10093 static const BYTE pOlePresStreamHeaderEmpty[] =
10095 0x00, 0x00, 0x00, 0x00,
10096 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10097 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10098 0x00, 0x00, 0x00, 0x00
10101 /* Create the OlePres000 Stream */
10102 hRes = IStorage_CreateStream(pStorage, L"\2OlePres000",
10103 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10105 if(hRes == S_OK)
10107 DWORD nHeaderSize;
10108 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10110 memset(&OlePres, 0, sizeof(OlePres));
10111 /* Do we have any metafile data to save */
10112 if(dwDataLength > 0)
10114 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10115 nHeaderSize = sizeof(pOlePresStreamHeader);
10117 else
10119 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10120 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10122 /* Set width and height of the metafile */
10123 OlePres.dwExtentX = dwExtentX;
10124 OlePres.dwExtentY = -dwExtentY;
10126 /* Set Data and Length */
10127 if(dwDataLength > sizeof(METAFILEPICT16))
10129 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10130 OlePres.pData = &(pData[8]);
10132 /* Save OlePres000 Data to Stream */
10133 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10134 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10135 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10136 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10137 if(OlePres.dwSize > 0)
10139 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10141 IStream_Release(pStream);
10145 /*************************************************************************
10146 * OLECONVERT_CreateOle10NativeStream [Internal]
10148 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10150 * PARAMS
10151 * pStorage [I] Dest storage to create the stream in
10152 * pData [I] Ole10 Native Data (ex. bmp)
10153 * dwDataLength [I] Size of the Ole10 Native Data
10155 * RETURNS
10156 * Nothing
10158 * NOTES
10159 * This function is used by OleConvertOLESTREAMToIStorage only.
10161 * Might need to verify the data and return appropriate error message
10164 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10166 HRESULT hRes;
10167 IStream *pStream;
10169 /* Create the Ole10Native Stream */
10170 hRes = IStorage_CreateStream(pStorage, L"\1Ole10Native",
10171 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10173 if(hRes == S_OK)
10175 /* Write info to stream */
10176 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10177 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10178 IStream_Release(pStream);
10183 /*************************************************************************
10184 * OLECONVERT_GetOLE10ProgID [Internal]
10186 * Finds the ProgID (or OleTypeID) from the IStorage
10188 * PARAMS
10189 * pStorage [I] The Src IStorage to get the ProgID
10190 * strProgID [I] the ProgID string to get
10191 * dwSize [I] the size of the string
10193 * RETURNS
10194 * Success: S_OK
10195 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10197 * NOTES
10198 * This function is used by OleConvertIStorageToOLESTREAM only.
10202 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10204 HRESULT hRes;
10205 IStream *pStream;
10206 LARGE_INTEGER iSeekPos;
10207 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10209 /* Open the CompObj Stream */
10210 hRes = IStorage_OpenStream(pStorage, L"\1CompObj", NULL,
10211 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10212 if(hRes == S_OK)
10215 /*Get the OleType from the CompObj Stream */
10216 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10217 iSeekPos.u.HighPart = 0;
10219 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10220 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10221 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10222 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10223 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10224 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10225 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10227 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10228 if(*dwSize > 0)
10230 IStream_Read(pStream, strProgID, *dwSize, NULL);
10232 IStream_Release(pStream);
10234 else
10236 STATSTG stat;
10237 LPOLESTR wstrProgID;
10239 /* Get the OleType from the registry */
10240 REFCLSID clsid = &(stat.clsid);
10241 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10242 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10243 if(hRes == S_OK)
10245 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10246 CoTaskMemFree(wstrProgID);
10250 return hRes;
10253 /*************************************************************************
10254 * OLECONVERT_GetOle10PresData [Internal]
10256 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10258 * PARAMS
10259 * pStorage [I] Src IStroage
10260 * pOleStream [I] Dest OleStream Mem Struct
10262 * RETURNS
10263 * Nothing
10265 * NOTES
10266 * This function is used by OleConvertIStorageToOLESTREAM only.
10268 * Memory allocated for pData must be freed by the caller
10272 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10275 HRESULT hRes;
10276 IStream *pStream;
10278 /* Initialize Default data for OLESTREAM */
10279 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10280 pOleStreamData[0].dwTypeID = 2;
10281 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10282 pOleStreamData[1].dwTypeID = 0;
10283 pOleStreamData[0].dwMetaFileWidth = 0;
10284 pOleStreamData[0].dwMetaFileHeight = 0;
10285 pOleStreamData[0].pData = NULL;
10286 pOleStreamData[1].pData = NULL;
10288 /* Open Ole10Native Stream */
10289 hRes = IStorage_OpenStream(pStorage, L"\1Ole10Native", NULL,
10290 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10291 if(hRes == S_OK)
10294 /* Read Size and Data */
10295 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10296 if(pOleStreamData->dwDataLength > 0)
10298 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10299 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10301 IStream_Release(pStream);
10307 /*************************************************************************
10308 * OLECONVERT_GetOle20PresData[Internal]
10310 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10312 * PARAMS
10313 * pStorage [I] Src IStroage
10314 * pOleStreamData [I] Dest OleStream Mem Struct
10316 * RETURNS
10317 * Nothing
10319 * NOTES
10320 * This function is used by OleConvertIStorageToOLESTREAM only.
10322 * Memory allocated for pData must be freed by the caller
10324 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10326 HRESULT hRes;
10327 IStream *pStream;
10328 OLECONVERT_ISTORAGE_OLEPRES olePress;
10330 /* Initialize Default data for OLESTREAM */
10331 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10332 pOleStreamData[0].dwTypeID = 2;
10333 pOleStreamData[0].dwMetaFileWidth = 0;
10334 pOleStreamData[0].dwMetaFileHeight = 0;
10335 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10336 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10337 pOleStreamData[1].dwTypeID = 0;
10338 pOleStreamData[1].dwOleTypeNameLength = 0;
10339 pOleStreamData[1].strOleTypeName[0] = 0;
10340 pOleStreamData[1].dwMetaFileWidth = 0;
10341 pOleStreamData[1].dwMetaFileHeight = 0;
10342 pOleStreamData[1].pData = NULL;
10343 pOleStreamData[1].dwDataLength = 0;
10346 /* Open OlePress000 stream */
10347 hRes = IStorage_OpenStream(pStorage, L"\2OlePres000", NULL,
10348 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10349 if(hRes == S_OK)
10351 LARGE_INTEGER iSeekPos;
10352 METAFILEPICT16 MetaFilePict;
10353 static const char strMetafilePictName[] = "METAFILEPICT";
10355 /* Set the TypeID for a Metafile */
10356 pOleStreamData[1].dwTypeID = 5;
10358 /* Set the OleTypeName to Metafile */
10359 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10360 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10362 iSeekPos.u.HighPart = 0;
10363 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10365 /* Get Presentation Data */
10366 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10367 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10368 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10369 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10371 /*Set width and Height */
10372 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10373 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10374 if(olePress.dwSize > 0)
10376 /* Set Length */
10377 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10379 /* Set MetaFilePict struct */
10380 MetaFilePict.mm = 8;
10381 MetaFilePict.xExt = olePress.dwExtentX;
10382 MetaFilePict.yExt = olePress.dwExtentY;
10383 MetaFilePict.hMF = 0;
10385 /* Get Metafile Data */
10386 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10387 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10388 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10390 IStream_Release(pStream);
10394 /*************************************************************************
10395 * OleConvertOLESTREAMToIStorage [OLE32.@]
10397 * Read info on MSDN
10399 * TODO
10400 * DVTARGETDEVICE parameter is not handled
10401 * Still unsure of some mem fields for OLE 10 Stream
10402 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10403 * and "\001OLE" streams
10406 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10407 LPOLESTREAM pOleStream,
10408 LPSTORAGE pstg,
10409 const DVTARGETDEVICE* ptd)
10411 int i;
10412 HRESULT hRes=S_OK;
10413 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10415 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10417 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10419 if(ptd != NULL)
10421 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10424 if(pstg == NULL || pOleStream == NULL)
10426 hRes = E_INVALIDARG;
10429 if(hRes == S_OK)
10431 /* Load the OLESTREAM to Memory */
10432 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10435 if(hRes == S_OK)
10437 /* Load the OLESTREAM to Memory (part 2)*/
10438 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10441 if(hRes == S_OK)
10444 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10446 /* Do we have the IStorage Data in the OLESTREAM */
10447 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10449 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10450 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10452 else
10454 /* It must be an original OLE 1.0 source */
10455 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10458 else
10460 /* It must be an original OLE 1.0 source */
10461 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10464 /* Create CompObj Stream if necessary */
10465 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10466 if(hRes == S_OK)
10468 /*Create the Ole Stream if necessary */
10469 STORAGE_CreateOleStream(pstg, 0);
10474 /* Free allocated memory */
10475 for(i=0; i < 2; i++)
10477 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10478 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10479 pOleStreamData[i].pstrOleObjFileName = NULL;
10481 return hRes;
10484 /*************************************************************************
10485 * OleConvertIStorageToOLESTREAM [OLE32.@]
10487 * Read info on MSDN
10489 * Read info on MSDN
10491 * TODO
10492 * Still unsure of some mem fields for OLE 10 Stream
10493 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10494 * and "\001OLE" streams.
10497 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10498 LPSTORAGE pstg,
10499 LPOLESTREAM pOleStream)
10501 int i;
10502 HRESULT hRes = S_OK;
10503 IStream *pStream;
10504 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10506 TRACE("%p %p\n", pstg, pOleStream);
10508 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10510 if(pstg == NULL || pOleStream == NULL)
10512 hRes = E_INVALIDARG;
10514 if(hRes == S_OK)
10516 /* Get the ProgID */
10517 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10518 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10520 if(hRes == S_OK)
10522 /* Was it originally Ole10 */
10523 hRes = IStorage_OpenStream(pstg, L"\1Ole10Native", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10524 if(hRes == S_OK)
10526 IStream_Release(pStream);
10527 /* Get Presentation Data for Ole10Native */
10528 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10530 else
10532 /* Get Presentation Data (OLE20) */
10533 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10536 /* Save OLESTREAM */
10537 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10538 if(hRes == S_OK)
10540 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10545 /* Free allocated memory */
10546 for(i=0; i < 2; i++)
10548 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10551 return hRes;
10554 enum stream_1ole_flags {
10555 OleStream_LinkedObject = 0x00000001,
10556 OleStream_Convert = 0x00000004
10559 /*************************************************************************
10560 * OleConvertIStorageToOLESTREAMEx [OLE32.@]
10562 HRESULT WINAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg, CLIPFORMAT cf, LONG width, LONG height,
10563 DWORD size, LPSTGMEDIUM medium, LPOLESTREAM olestream )
10565 FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg, cf, width, height, size, medium, olestream);
10567 return E_NOTIMPL;
10570 /***********************************************************************
10571 * GetConvertStg (OLE32.@)
10573 HRESULT WINAPI GetConvertStg(IStorage *stg)
10575 static const DWORD version_magic = 0x02000001;
10576 DWORD header[2];
10577 IStream *stream;
10578 HRESULT hr;
10580 TRACE("%p\n", stg);
10582 if (!stg) return E_INVALIDARG;
10584 hr = IStorage_OpenStream(stg, L"\1Ole", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10585 if (FAILED(hr)) return hr;
10587 hr = IStream_Read(stream, header, sizeof(header), NULL);
10588 IStream_Release(stream);
10589 if (FAILED(hr)) return hr;
10591 if (header[0] != version_magic)
10593 ERR("got wrong version magic for 1Ole stream, %#lx.\n", header[0]);
10594 return E_FAIL;
10597 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10600 /***********************************************************************
10601 * SetConvertStg (OLE32.@)
10603 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10605 DWORD flags = convert ? OleStream_Convert : 0;
10606 IStream *stream;
10607 DWORD header[2];
10608 HRESULT hr;
10610 TRACE("(%p, %d)\n", storage, convert);
10612 hr = IStorage_OpenStream(storage, L"\1Ole", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10613 if (FAILED(hr))
10615 if (hr != STG_E_FILENOTFOUND)
10616 return hr;
10618 return STORAGE_CreateOleStream(storage, flags);
10621 hr = IStream_Read(stream, header, sizeof(header), NULL);
10622 if (FAILED(hr))
10624 IStream_Release(stream);
10625 return hr;
10628 /* update flag if differs */
10629 if ((header[1] ^ flags) & OleStream_Convert)
10631 LARGE_INTEGER pos = {{0}};
10633 if (header[1] & OleStream_Convert)
10634 flags = header[1] & ~OleStream_Convert;
10635 else
10636 flags = header[1] | OleStream_Convert;
10638 pos.QuadPart = sizeof(DWORD);
10639 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10640 if (FAILED(hr))
10642 IStream_Release(stream);
10643 return hr;
10646 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10649 IStream_Release(stream);
10650 return hr;