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
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.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
70 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base
;
85 * Entry in the parent's stream tracking list
87 struct list ParentListEntry
;
89 StorageBaseImpl
*parentStorage
;
91 typedef struct StorageInternalImpl StorageInternalImpl
;
93 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
94 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
96 /* Method definitions for the Storage32InternalImpl class. */
97 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
98 DWORD openFlags
, DirRef storageDirEntry
);
99 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
100 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
101 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
102 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
103 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
104 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
105 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
106 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
108 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
109 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
110 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
111 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
112 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
114 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
115 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
116 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
118 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
119 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
120 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
121 ULONG blockIndex
, ULONG offset
, DWORD value
);
122 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
123 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
125 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
126 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
128 typedef struct TransactedDirEntry
130 /* If applicable, a reference to the original DirEntry in the transacted
131 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
132 DirRef transactedParentEntry
;
134 /* True if this entry is being used. */
137 /* True if data is up to date. */
140 /* True if this entry has been modified. */
143 /* True if this entry's stream has been modified. */
146 /* True if this entry has been deleted in the transacted storage, but the
147 * delete has not yet been committed. */
150 /* If this entry's stream has been modified, a reference to where the stream
151 * is stored in the snapshot file. */
154 /* This directory entry's data, including any changes that have been made. */
157 /* A reference to the parent of this node. This is only valid while we are
158 * committing changes. */
161 /* A reference to a newly-created entry in the transacted parent. This is
162 * always equal to transactedParentEntry except when committing changes. */
163 DirRef newTransactedParentEntry
;
164 } TransactedDirEntry
;
166 /****************************************************************************
167 * Transacted storage object.
169 typedef struct TransactedSnapshotImpl
171 struct StorageBaseImpl base
;
174 * Modified streams are temporarily saved to the scratch file.
176 StorageBaseImpl
*scratch
;
178 /* The directory structure is kept here, so that we can track how these
179 * entries relate to those in the parent storage. */
180 TransactedDirEntry
*entries
;
182 ULONG firstFreeEntry
;
185 * Changes are committed to the transacted parent.
187 StorageBaseImpl
*transactedParent
;
188 } TransactedSnapshotImpl
;
190 /* Generic function to create a transacted wrapper for a direct storage object. */
191 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
193 /* OLESTREAM memory structure to use for Get and Put Routines */
194 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
199 DWORD dwOleTypeNameLength
;
200 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
201 CHAR
*pstrOleObjFileName
;
202 DWORD dwOleObjFileNameLength
;
203 DWORD dwMetaFileWidth
;
204 DWORD dwMetaFileHeight
;
205 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
208 }OLECONVERT_OLESTREAM_DATA
;
210 /* CompObj Stream structure */
211 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
216 DWORD dwCLSIDNameLength
;
217 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
218 DWORD dwOleTypeNameLength
;
219 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
220 DWORD dwProgIDNameLength
;
221 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
223 }OLECONVERT_ISTORAGE_COMPOBJ
;
226 /* Ole Presentation Stream structure */
227 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
235 }OLECONVERT_ISTORAGE_OLEPRES
;
239 /***********************************************************************
240 * Forward declaration of internal functions used by the method DestroyElement
242 static HRESULT
deleteStorageContents(
243 StorageBaseImpl
*parentStorage
,
244 DirRef indexToDelete
,
245 DirEntry entryDataToDelete
);
247 static HRESULT
deleteStreamContents(
248 StorageBaseImpl
*parentStorage
,
249 DirRef indexToDelete
,
250 DirEntry entryDataToDelete
);
252 static HRESULT
removeFromTree(
253 StorageBaseImpl
*This
,
254 DirRef parentStorageIndex
,
255 DirRef deletedIndex
);
257 /***********************************************************************
258 * Declaration of the functions used to manipulate DirEntry
261 static HRESULT
insertIntoTree(
262 StorageBaseImpl
*This
,
263 DirRef parentStorageIndex
,
264 DirRef newEntryIndex
);
266 static LONG
entryNameCmp(
267 const OLECHAR
*name1
,
268 const OLECHAR
*name2
);
270 static DirRef
findElement(
271 StorageBaseImpl
*storage
,
276 static HRESULT
findTreeParent(
277 StorageBaseImpl
*storage
,
279 const OLECHAR
*childName
,
280 DirEntry
*parentData
,
284 /***********************************************************************
285 * Declaration of miscellaneous functions...
287 static HRESULT
validateSTGM(DWORD stgmValue
);
289 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
290 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
291 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
293 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
296 /****************************************************************************
297 * IEnumSTATSTGImpl definitions.
299 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
300 * This class allows iterating through the content of a storage and to find
301 * specific items inside it.
303 struct IEnumSTATSTGImpl
305 IEnumSTATSTG IEnumSTATSTG_iface
;
307 LONG ref
; /* Reference count */
308 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
309 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
311 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
314 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
316 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
320 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
323 /************************************************************************
327 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
329 return (index
+1) * This
->bigBlockSize
;
332 /************************************************************************
333 ** Storage32BaseImpl implementation
335 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
336 ULARGE_INTEGER offset
,
341 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
344 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
345 ULARGE_INTEGER offset
,
350 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
353 /************************************************************************
354 * Storage32BaseImpl_QueryInterface (IUnknown)
356 * This method implements the common QueryInterface for all IStorage32
357 * implementations contained in this file.
359 * See Windows documentation for more details on IUnknown methods.
361 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
366 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
368 if ( (This
==0) || (ppvObject
==0) )
373 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
374 IsEqualGUID(&IID_IStorage
, riid
))
378 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
380 *ppvObject
= &This
->pssVtbl
;
384 return E_NOINTERFACE
;
386 IStorage_AddRef(iface
);
391 /************************************************************************
392 * Storage32BaseImpl_AddRef (IUnknown)
394 * This method implements the common AddRef for all IStorage32
395 * implementations contained in this file.
397 * See Windows documentation for more details on IUnknown methods.
399 static ULONG WINAPI
StorageBaseImpl_AddRef(
402 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
403 ULONG ref
= InterlockedIncrement(&This
->ref
);
405 TRACE("(%p) AddRef to %d\n", This
, ref
);
410 /************************************************************************
411 * Storage32BaseImpl_Release (IUnknown)
413 * This method implements the common Release for all IStorage32
414 * implementations contained in this file.
416 * See Windows documentation for more details on IUnknown methods.
418 static ULONG WINAPI
StorageBaseImpl_Release(
421 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
423 ULONG ref
= InterlockedDecrement(&This
->ref
);
425 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
430 * Since we are using a system of base-classes, we want to call the
431 * destructor of the appropriate derived class. To do this, we are
432 * using virtual functions to implement the destructor.
434 StorageBaseImpl_Destroy(This
);
440 /************************************************************************
441 * Storage32BaseImpl_OpenStream (IStorage)
443 * This method will open the specified stream object from the current storage.
445 * See Windows documentation for more details on IStorage methods.
447 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
449 const OLECHAR
* pwcsName
, /* [string][in] */
450 void* reserved1
, /* [unique][in] */
451 DWORD grfMode
, /* [in] */
452 DWORD reserved2
, /* [in] */
453 IStream
** ppstm
) /* [out] */
455 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
456 StgStreamImpl
* newStream
;
457 DirEntry currentEntry
;
458 DirRef streamEntryRef
;
459 HRESULT res
= STG_E_UNKNOWN
;
461 TRACE("(%p, %s, %p, %x, %d, %p)\n",
462 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
464 if ( (pwcsName
==NULL
) || (ppstm
==0) )
472 if ( FAILED( validateSTGM(grfMode
) ) ||
473 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
475 res
= STG_E_INVALIDFLAG
;
482 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
484 res
= STG_E_INVALIDFUNCTION
;
490 res
= STG_E_REVERTED
;
495 * Check that we're compatible with the parent's storage mode, but
496 * only if we are not in transacted mode
498 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
499 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
501 res
= STG_E_INVALIDFLAG
;
507 * Search for the element with the given name
509 streamEntryRef
= findElement(
511 This
->storageDirEntry
,
516 * If it was found, construct the stream object and return a pointer to it.
518 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
519 (currentEntry
.stgType
==STGTY_STREAM
) )
521 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
523 /* A single stream cannot be opened a second time. */
524 res
= STG_E_ACCESSDENIED
;
528 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
532 newStream
->grfMode
= grfMode
;
533 *ppstm
= &newStream
->IStream_iface
;
535 IStream_AddRef(*ppstm
);
545 res
= STG_E_FILENOTFOUND
;
549 TRACE("<-- IStream %p\n", *ppstm
);
550 TRACE("<-- %08x\n", res
);
554 /************************************************************************
555 * Storage32BaseImpl_OpenStorage (IStorage)
557 * This method will open a new storage object from the current storage.
559 * See Windows documentation for more details on IStorage methods.
561 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
563 const OLECHAR
* pwcsName
, /* [string][unique][in] */
564 IStorage
* pstgPriority
, /* [unique][in] */
565 DWORD grfMode
, /* [in] */
566 SNB snbExclude
, /* [unique][in] */
567 DWORD reserved
, /* [in] */
568 IStorage
** ppstg
) /* [out] */
570 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
571 StorageInternalImpl
* newStorage
;
572 StorageBaseImpl
* newTransactedStorage
;
573 DirEntry currentEntry
;
574 DirRef storageEntryRef
;
575 HRESULT res
= STG_E_UNKNOWN
;
577 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
578 iface
, debugstr_w(pwcsName
), pstgPriority
,
579 grfMode
, snbExclude
, reserved
, ppstg
);
581 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
587 if (This
->openFlags
& STGM_SIMPLE
)
589 res
= STG_E_INVALIDFUNCTION
;
594 if (snbExclude
!= NULL
)
596 res
= STG_E_INVALIDPARAMETER
;
600 if ( FAILED( validateSTGM(grfMode
) ))
602 res
= STG_E_INVALIDFLAG
;
609 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
610 (grfMode
& STGM_DELETEONRELEASE
) ||
611 (grfMode
& STGM_PRIORITY
) )
613 res
= STG_E_INVALIDFUNCTION
;
618 return STG_E_REVERTED
;
621 * Check that we're compatible with the parent's storage mode,
622 * but only if we are not transacted
624 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
625 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
627 res
= STG_E_ACCESSDENIED
;
634 storageEntryRef
= findElement(
636 This
->storageDirEntry
,
640 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
641 (currentEntry
.stgType
==STGTY_STORAGE
) )
643 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
645 /* A single storage cannot be opened a second time. */
646 res
= STG_E_ACCESSDENIED
;
650 newStorage
= StorageInternalImpl_Construct(
657 if (grfMode
& STGM_TRANSACTED
)
659 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
663 HeapFree(GetProcessHeap(), 0, newStorage
);
667 *ppstg
= &newTransactedStorage
->IStorage_iface
;
671 *ppstg
= &newStorage
->base
.IStorage_iface
;
674 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
680 res
= STG_E_INSUFFICIENTMEMORY
;
684 res
= STG_E_FILENOTFOUND
;
687 TRACE("<-- %08x\n", res
);
691 /************************************************************************
692 * Storage32BaseImpl_EnumElements (IStorage)
694 * This method will create an enumerator object that can be used to
695 * retrieve information about all the elements in the storage object.
697 * See Windows documentation for more details on IStorage methods.
699 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
701 DWORD reserved1
, /* [in] */
702 void* reserved2
, /* [size_is][unique][in] */
703 DWORD reserved3
, /* [in] */
704 IEnumSTATSTG
** ppenum
) /* [out] */
706 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
707 IEnumSTATSTGImpl
* newEnum
;
709 TRACE("(%p, %d, %p, %d, %p)\n",
710 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
712 if ( (This
==0) || (ppenum
==0))
716 return STG_E_REVERTED
;
718 newEnum
= IEnumSTATSTGImpl_Construct(
720 This
->storageDirEntry
);
724 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
726 IEnumSTATSTG_AddRef(*ppenum
);
731 return E_OUTOFMEMORY
;
734 /************************************************************************
735 * Storage32BaseImpl_Stat (IStorage)
737 * This method will retrieve information about this storage object.
739 * See Windows documentation for more details on IStorage methods.
741 static HRESULT WINAPI
StorageBaseImpl_Stat(
743 STATSTG
* pstatstg
, /* [out] */
744 DWORD grfStatFlag
) /* [in] */
746 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
747 DirEntry currentEntry
;
748 HRESULT res
= STG_E_UNKNOWN
;
750 TRACE("(%p, %p, %x)\n",
751 iface
, pstatstg
, grfStatFlag
);
753 if ( (This
==0) || (pstatstg
==0))
761 res
= STG_E_REVERTED
;
765 res
= StorageBaseImpl_ReadDirEntry(
767 This
->storageDirEntry
,
772 StorageUtl_CopyDirEntryToSTATSTG(
778 pstatstg
->grfMode
= This
->openFlags
;
779 pstatstg
->grfStateBits
= This
->stateBits
;
785 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
787 TRACE("<-- %08x\n", res
);
791 /************************************************************************
792 * Storage32BaseImpl_RenameElement (IStorage)
794 * This method will rename the specified element.
796 * See Windows documentation for more details on IStorage methods.
798 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
800 const OLECHAR
* pwcsOldName
, /* [in] */
801 const OLECHAR
* pwcsNewName
) /* [in] */
803 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
804 DirEntry currentEntry
;
805 DirRef currentEntryRef
;
807 TRACE("(%p, %s, %s)\n",
808 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
811 return STG_E_REVERTED
;
813 currentEntryRef
= findElement(This
,
814 This
->storageDirEntry
,
818 if (currentEntryRef
!= DIRENTRY_NULL
)
821 * There is already an element with the new name
823 return STG_E_FILEALREADYEXISTS
;
827 * Search for the old element name
829 currentEntryRef
= findElement(This
,
830 This
->storageDirEntry
,
834 if (currentEntryRef
!= DIRENTRY_NULL
)
836 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
837 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
839 WARN("Element is already open; cannot rename.\n");
840 return STG_E_ACCESSDENIED
;
843 /* Remove the element from its current position in the tree */
844 removeFromTree(This
, This
->storageDirEntry
,
847 /* Change the name of the element */
848 strcpyW(currentEntry
.name
, pwcsNewName
);
850 /* Delete any sibling links */
851 currentEntry
.leftChild
= DIRENTRY_NULL
;
852 currentEntry
.rightChild
= DIRENTRY_NULL
;
854 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
857 /* Insert the element in a new position in the tree */
858 insertIntoTree(This
, This
->storageDirEntry
,
864 * There is no element with the old name
866 return STG_E_FILENOTFOUND
;
869 return StorageBaseImpl_Flush(This
);
872 /************************************************************************
873 * Storage32BaseImpl_CreateStream (IStorage)
875 * This method will create a stream object within this storage
877 * See Windows documentation for more details on IStorage methods.
879 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
881 const OLECHAR
* pwcsName
, /* [string][in] */
882 DWORD grfMode
, /* [in] */
883 DWORD reserved1
, /* [in] */
884 DWORD reserved2
, /* [in] */
885 IStream
** ppstm
) /* [out] */
887 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
888 StgStreamImpl
* newStream
;
889 DirEntry currentEntry
, newStreamEntry
;
890 DirRef currentEntryRef
, newStreamEntryRef
;
893 TRACE("(%p, %s, %x, %d, %d, %p)\n",
894 iface
, debugstr_w(pwcsName
), grfMode
,
895 reserved1
, reserved2
, ppstm
);
898 return STG_E_INVALIDPOINTER
;
901 return STG_E_INVALIDNAME
;
903 if (reserved1
|| reserved2
)
904 return STG_E_INVALIDPARAMETER
;
906 if ( FAILED( validateSTGM(grfMode
) ))
907 return STG_E_INVALIDFLAG
;
909 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
910 return STG_E_INVALIDFLAG
;
913 return STG_E_REVERTED
;
918 if ((grfMode
& STGM_DELETEONRELEASE
) ||
919 (grfMode
& STGM_TRANSACTED
))
920 return STG_E_INVALIDFUNCTION
;
923 * Don't worry about permissions in transacted mode, as we can always write
924 * changes; we just can't always commit them.
926 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
927 /* Can't create a stream on read-only storage */
928 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
929 return STG_E_ACCESSDENIED
;
931 /* Can't create a stream with greater access than the parent. */
932 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
933 return STG_E_ACCESSDENIED
;
936 if(This
->openFlags
& STGM_SIMPLE
)
937 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
941 currentEntryRef
= findElement(This
,
942 This
->storageDirEntry
,
946 if (currentEntryRef
!= DIRENTRY_NULL
)
949 * An element with this name already exists
951 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
953 IStorage_DestroyElement(iface
, pwcsName
);
956 return STG_E_FILEALREADYEXISTS
;
960 * memset the empty entry
962 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
964 newStreamEntry
.sizeOfNameString
=
965 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
967 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
968 return STG_E_INVALIDNAME
;
970 strcpyW(newStreamEntry
.name
, pwcsName
);
972 newStreamEntry
.stgType
= STGTY_STREAM
;
973 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
974 newStreamEntry
.size
.u
.LowPart
= 0;
975 newStreamEntry
.size
.u
.HighPart
= 0;
977 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
978 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
979 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
981 /* call CoFileTime to get the current time
986 /* newStreamEntry.clsid */
989 * Create an entry with the new data
991 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
996 * Insert the new entry in the parent storage's tree.
1000 This
->storageDirEntry
,
1004 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1009 * Open the stream to return it.
1011 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1015 *ppstm
= &newStream
->IStream_iface
;
1016 IStream_AddRef(*ppstm
);
1020 return STG_E_INSUFFICIENTMEMORY
;
1023 return StorageBaseImpl_Flush(This
);
1026 /************************************************************************
1027 * Storage32BaseImpl_SetClass (IStorage)
1029 * This method will write the specified CLSID in the directory entry of this
1032 * See Windows documentation for more details on IStorage methods.
1034 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1036 REFCLSID clsid
) /* [in] */
1038 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1040 DirEntry currentEntry
;
1042 TRACE("(%p, %p)\n", iface
, clsid
);
1045 return STG_E_REVERTED
;
1047 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1048 This
->storageDirEntry
,
1050 if (SUCCEEDED(hRes
))
1052 currentEntry
.clsid
= *clsid
;
1054 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1055 This
->storageDirEntry
,
1059 if (SUCCEEDED(hRes
))
1060 hRes
= StorageBaseImpl_Flush(This
);
1065 /************************************************************************
1066 ** Storage32Impl implementation
1069 /************************************************************************
1070 * Storage32BaseImpl_CreateStorage (IStorage)
1072 * This method will create the storage object within the provided storage.
1074 * See Windows documentation for more details on IStorage methods.
1076 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1078 const OLECHAR
*pwcsName
, /* [string][in] */
1079 DWORD grfMode
, /* [in] */
1080 DWORD reserved1
, /* [in] */
1081 DWORD reserved2
, /* [in] */
1082 IStorage
**ppstg
) /* [out] */
1084 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1086 DirEntry currentEntry
;
1088 DirRef currentEntryRef
;
1092 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1093 iface
, debugstr_w(pwcsName
), grfMode
,
1094 reserved1
, reserved2
, ppstg
);
1097 return STG_E_INVALIDPOINTER
;
1099 if (This
->openFlags
& STGM_SIMPLE
)
1101 return STG_E_INVALIDFUNCTION
;
1105 return STG_E_INVALIDNAME
;
1109 if ( FAILED( validateSTGM(grfMode
) ) ||
1110 (grfMode
& STGM_DELETEONRELEASE
) )
1112 WARN("bad grfMode: 0x%x\n", grfMode
);
1113 return STG_E_INVALIDFLAG
;
1117 return STG_E_REVERTED
;
1120 * Check that we're compatible with the parent's storage mode
1122 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1123 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1125 WARN("access denied\n");
1126 return STG_E_ACCESSDENIED
;
1129 currentEntryRef
= findElement(This
,
1130 This
->storageDirEntry
,
1134 if (currentEntryRef
!= DIRENTRY_NULL
)
1137 * An element with this name already exists
1139 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1140 ((This
->openFlags
& STGM_TRANSACTED
) ||
1141 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1143 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1149 WARN("file already exists\n");
1150 return STG_E_FILEALREADYEXISTS
;
1153 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1154 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1156 WARN("read-only storage\n");
1157 return STG_E_ACCESSDENIED
;
1160 memset(&newEntry
, 0, sizeof(DirEntry
));
1162 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1164 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1166 FIXME("name too long\n");
1167 return STG_E_INVALIDNAME
;
1170 strcpyW(newEntry
.name
, pwcsName
);
1172 newEntry
.stgType
= STGTY_STORAGE
;
1173 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1174 newEntry
.size
.u
.LowPart
= 0;
1175 newEntry
.size
.u
.HighPart
= 0;
1177 newEntry
.leftChild
= DIRENTRY_NULL
;
1178 newEntry
.rightChild
= DIRENTRY_NULL
;
1179 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1181 /* call CoFileTime to get the current time
1186 /* newEntry.clsid */
1189 * Create a new directory entry for the storage
1191 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1196 * Insert the new directory entry into the parent storage's tree
1198 hr
= insertIntoTree(
1200 This
->storageDirEntry
,
1204 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1209 * Open it to get a pointer to return.
1211 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1213 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1219 hr
= StorageBaseImpl_Flush(This
);
1225 /***************************************************************************
1229 * Reserve a directory entry in the file and initialize it.
1231 static HRESULT
StorageImpl_CreateDirEntry(
1232 StorageBaseImpl
*base
,
1233 const DirEntry
*newData
,
1236 StorageImpl
*storage
= (StorageImpl
*)base
;
1237 ULONG currentEntryIndex
= 0;
1238 ULONG newEntryIndex
= DIRENTRY_NULL
;
1240 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1241 WORD sizeOfNameString
;
1245 hr
= StorageImpl_ReadRawDirEntry(storage
,
1251 StorageUtl_ReadWord(
1253 OFFSET_PS_NAMELENGTH
,
1256 if (sizeOfNameString
== 0)
1259 * The entry exists and is available, we found it.
1261 newEntryIndex
= currentEntryIndex
;
1267 * We exhausted the directory entries, we will create more space below
1269 newEntryIndex
= currentEntryIndex
;
1271 currentEntryIndex
++;
1273 } while (newEntryIndex
== DIRENTRY_NULL
);
1276 * grow the directory stream
1280 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1281 ULARGE_INTEGER newSize
;
1283 ULONG lastEntry
= 0;
1284 ULONG blockCount
= 0;
1287 * obtain the new count of blocks in the directory stream
1289 blockCount
= BlockChainStream_GetCount(
1290 storage
->rootBlockChain
)+1;
1293 * initialize the size used by the directory stream
1295 newSize
.u
.HighPart
= 0;
1296 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1299 * add a block to the directory stream
1301 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1304 * memset the empty entry in order to initialize the unused newly
1307 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1312 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1315 entryIndex
= newEntryIndex
+ 1;
1316 entryIndex
< lastEntry
;
1319 StorageImpl_WriteRawDirEntry(
1325 StorageImpl_SaveFileHeader(storage
);
1328 UpdateRawDirEntry(currentData
, newData
);
1330 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1333 *index
= newEntryIndex
;
1338 /***************************************************************************
1342 * Mark a directory entry in the file as free.
1344 static HRESULT
StorageImpl_DestroyDirEntry(
1345 StorageBaseImpl
*base
,
1348 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1349 StorageImpl
*storage
= (StorageImpl
*)base
;
1351 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1353 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1357 /****************************************************************************
1361 * Case insensitive comparison of DirEntry.name by first considering
1364 * Returns <0 when name1 < name2
1365 * >0 when name1 > name2
1366 * 0 when name1 == name2
1368 static LONG
entryNameCmp(
1369 const OLECHAR
*name1
,
1370 const OLECHAR
*name2
)
1372 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1374 while (diff
== 0 && *name1
!= 0)
1377 * We compare the string themselves only when they are of the same length
1379 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1385 /****************************************************************************
1389 * Add a directory entry to a storage
1391 static HRESULT
insertIntoTree(
1392 StorageBaseImpl
*This
,
1393 DirRef parentStorageIndex
,
1394 DirRef newEntryIndex
)
1396 DirEntry currentEntry
;
1400 * Read the inserted entry
1402 StorageBaseImpl_ReadDirEntry(This
,
1407 * Read the storage entry
1409 StorageBaseImpl_ReadDirEntry(This
,
1413 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1416 * The root storage contains some element, therefore, start the research
1417 * for the appropriate location.
1420 DirRef current
, next
, previous
, currentEntryId
;
1423 * Keep a reference to the root of the storage's element tree
1425 currentEntryId
= currentEntry
.dirRootEntry
;
1430 StorageBaseImpl_ReadDirEntry(This
,
1431 currentEntry
.dirRootEntry
,
1434 previous
= currentEntry
.leftChild
;
1435 next
= currentEntry
.rightChild
;
1436 current
= currentEntryId
;
1440 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1444 if (previous
!= DIRENTRY_NULL
)
1446 StorageBaseImpl_ReadDirEntry(This
,
1453 currentEntry
.leftChild
= newEntryIndex
;
1454 StorageBaseImpl_WriteDirEntry(This
,
1462 if (next
!= DIRENTRY_NULL
)
1464 StorageBaseImpl_ReadDirEntry(This
,
1471 currentEntry
.rightChild
= newEntryIndex
;
1472 StorageBaseImpl_WriteDirEntry(This
,
1481 * Trying to insert an item with the same name in the
1482 * subtree structure.
1484 return STG_E_FILEALREADYEXISTS
;
1487 previous
= currentEntry
.leftChild
;
1488 next
= currentEntry
.rightChild
;
1494 * The storage is empty, make the new entry the root of its element tree
1496 currentEntry
.dirRootEntry
= newEntryIndex
;
1497 StorageBaseImpl_WriteDirEntry(This
,
1505 /****************************************************************************
1509 * Find and read the element of a storage with the given name.
1511 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1512 const OLECHAR
*name
, DirEntry
*data
)
1514 DirRef currentEntry
;
1516 /* Read the storage entry to find the root of the tree. */
1517 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1519 currentEntry
= data
->dirRootEntry
;
1521 while (currentEntry
!= DIRENTRY_NULL
)
1525 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1527 cmp
= entryNameCmp(name
, data
->name
);
1534 currentEntry
= data
->leftChild
;
1537 currentEntry
= data
->rightChild
;
1540 return currentEntry
;
1543 /****************************************************************************
1547 * Find and read the binary tree parent of the element with the given name.
1549 * If there is no such element, find a place where it could be inserted and
1550 * return STG_E_FILENOTFOUND.
1552 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1553 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1559 /* Read the storage entry to find the root of the tree. */
1560 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1562 *parentEntry
= storageEntry
;
1563 *relation
= DIRENTRY_RELATION_DIR
;
1565 childEntry
= parentData
->dirRootEntry
;
1567 while (childEntry
!= DIRENTRY_NULL
)
1571 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1573 cmp
= entryNameCmp(childName
, childData
.name
);
1581 *parentData
= childData
;
1582 *parentEntry
= childEntry
;
1583 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1585 childEntry
= parentData
->leftChild
;
1590 *parentData
= childData
;
1591 *parentEntry
= childEntry
;
1592 *relation
= DIRENTRY_RELATION_NEXT
;
1594 childEntry
= parentData
->rightChild
;
1598 if (childEntry
== DIRENTRY_NULL
)
1599 return STG_E_FILENOTFOUND
;
1605 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1606 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1607 SNB snbExclude
, IStorage
*pstgDest
);
1609 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1610 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1611 SNB snbExclude
, IStorage
*pstgDest
)
1617 IStream
*pstrChild
, *pstrTmp
;
1620 if (srcEntry
== DIRENTRY_NULL
)
1623 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1630 WCHAR
**snb
= snbExclude
;
1632 while ( *snb
!= NULL
&& !skip
)
1634 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1642 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1645 * create a new storage in destination storage
1647 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1648 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1653 * if it already exist, don't create a new one use this one
1655 if (hr
== STG_E_FILEALREADYEXISTS
)
1657 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1658 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1659 NULL
, 0, &pstgTmp
);
1664 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1665 skip_stream
, NULL
, pstgTmp
);
1667 IStorage_Release(pstgTmp
);
1670 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1677 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1681 * open child stream storage. This operation must succeed even if the
1682 * stream is already open, so we use internal functions to do it.
1686 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1690 pstrChild
= &streamimpl
->IStream_iface
;
1692 IStream_AddRef(pstrChild
);
1704 * Get the size of the source stream
1706 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1709 * Set the size of the destination stream.
1711 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1716 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1719 IStream_Release( pstrChild
);
1722 IStream_Release( pstrTmp
);
1728 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1729 skip_stream
, snbExclude
, pstgDest
);
1732 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1733 skip_stream
, snbExclude
, pstgDest
);
1738 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1739 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1740 SNB snbExclude
, IStorage
*pstgDest
)
1745 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1748 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1751 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1752 skip_stream
, snbExclude
, pstgDest
);
1757 /*************************************************************************
1760 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1762 DWORD ciidExclude
, /* [in] */
1763 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1764 SNB snbExclude
, /* [unique][in] */
1765 IStorage
* pstgDest
) /* [unique][in] */
1767 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1769 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1772 TRACE("(%p, %d, %p, %p, %p)\n",
1773 iface
, ciidExclude
, rgiidExclude
,
1774 snbExclude
, pstgDest
);
1776 if ( pstgDest
== 0 )
1777 return STG_E_INVALIDPOINTER
;
1779 for(i
= 0; i
< ciidExclude
; ++i
)
1781 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1782 skip_storage
= TRUE
;
1783 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1786 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1791 /* Give up early if it looks like this would be infinitely recursive.
1792 * Oddly enough, this includes some cases that aren't really recursive, like
1793 * copying to a transacted child. */
1794 IStorage
*pstgDestAncestor
= pstgDest
;
1795 IStorage
*pstgDestAncestorChild
= NULL
;
1797 /* Go up the chain from the destination until we find the source storage. */
1798 while (pstgDestAncestor
!= iface
) {
1799 pstgDestAncestorChild
= pstgDest
;
1801 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1803 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1805 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1807 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1809 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1811 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1817 if (pstgDestAncestor
== iface
)
1821 if (pstgDestAncestorChild
&& snbExclude
)
1823 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1825 WCHAR
**snb
= snbExclude
;
1827 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1829 while ( *snb
!= NULL
&& fail
)
1831 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1838 return STG_E_ACCESSDENIED
;
1842 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1843 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1846 /*************************************************************************
1847 * MoveElementTo (IStorage)
1849 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1851 const OLECHAR
*pwcsName
, /* [string][in] */
1852 IStorage
*pstgDest
, /* [unique][in] */
1853 const OLECHAR
*pwcsNewName
,/* [string][in] */
1854 DWORD grfFlags
) /* [in] */
1856 FIXME("(%p %s %p %s %u): stub\n", iface
,
1857 debugstr_w(pwcsName
), pstgDest
,
1858 debugstr_w(pwcsNewName
), grfFlags
);
1862 /*************************************************************************
1865 * Ensures that any changes made to a storage object open in transacted mode
1866 * are reflected in the parent storage
1868 * In a non-transacted mode, this ensures all cached writes are completed.
1870 static HRESULT WINAPI
StorageImpl_Commit(
1872 DWORD grfCommitFlags
)/* [in] */
1874 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1875 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1876 return StorageBaseImpl_Flush(This
);
1879 /*************************************************************************
1882 * Discard all changes that have been made since the last commit operation
1884 static HRESULT WINAPI
StorageImpl_Revert(
1887 TRACE("(%p)\n", iface
);
1891 /*************************************************************************
1892 * DestroyElement (IStorage)
1894 * Strategy: This implementation is built this way for simplicity not for speed.
1895 * I always delete the topmost element of the enumeration and adjust
1896 * the deleted element pointer all the time. This takes longer to
1897 * do but allow to reinvoke DestroyElement whenever we encounter a
1898 * storage object. The optimisation resides in the usage of another
1899 * enumeration strategy that would give all the leaves of a storage
1900 * first. (postfix order)
1902 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1904 const OLECHAR
*pwcsName
)/* [string][in] */
1906 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1909 DirEntry entryToDelete
;
1910 DirRef entryToDeleteRef
;
1913 iface
, debugstr_w(pwcsName
));
1916 return STG_E_INVALIDPOINTER
;
1919 return STG_E_REVERTED
;
1921 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1922 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1923 return STG_E_ACCESSDENIED
;
1925 entryToDeleteRef
= findElement(
1927 This
->storageDirEntry
,
1931 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1933 return STG_E_FILENOTFOUND
;
1936 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1938 hr
= deleteStorageContents(
1943 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1945 hr
= deleteStreamContents(
1955 * Remove the entry from its parent storage
1957 hr
= removeFromTree(
1959 This
->storageDirEntry
,
1963 * Invalidate the entry
1966 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1969 hr
= StorageBaseImpl_Flush(This
);
1975 /******************************************************************************
1976 * Internal stream list handlers
1979 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1981 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1982 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1985 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1987 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1988 list_remove(&(strm
->StrmListEntry
));
1991 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1993 StgStreamImpl
*strm
;
1995 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1997 if (strm
->dirEntry
== streamEntry
)
2006 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2008 StorageInternalImpl
*childstg
;
2010 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2012 if (childstg
->base
.storageDirEntry
== storageEntry
)
2021 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2023 struct list
*cur
, *cur2
;
2024 StgStreamImpl
*strm
=NULL
;
2025 StorageInternalImpl
*childstg
=NULL
;
2027 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2028 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2029 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2030 strm
->parentStorage
= NULL
;
2034 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2035 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2036 StorageBaseImpl_Invalidate( &childstg
->base
);
2039 if (stg
->transactedChild
)
2041 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2043 stg
->transactedChild
= NULL
;
2048 /*********************************************************************
2052 * Delete the contents of a storage entry.
2055 static HRESULT
deleteStorageContents(
2056 StorageBaseImpl
*parentStorage
,
2057 DirRef indexToDelete
,
2058 DirEntry entryDataToDelete
)
2060 IEnumSTATSTG
*elements
= 0;
2061 IStorage
*childStorage
= 0;
2062 STATSTG currentElement
;
2064 HRESULT destroyHr
= S_OK
;
2065 StorageInternalImpl
*stg
, *stg2
;
2067 /* Invalidate any open storage objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2070 if (stg
->base
.storageDirEntry
== indexToDelete
)
2072 StorageBaseImpl_Invalidate(&stg
->base
);
2077 * Open the storage and enumerate it
2079 hr
= StorageBaseImpl_OpenStorage(
2080 &parentStorage
->IStorage_iface
,
2081 entryDataToDelete
.name
,
2083 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2094 * Enumerate the elements
2096 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2101 * Obtain the next element
2103 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2106 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2108 CoTaskMemFree(currentElement
.pwcsName
);
2112 * We need to Reset the enumeration every time because we delete elements
2113 * and the enumeration could be invalid
2115 IEnumSTATSTG_Reset(elements
);
2117 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2119 IStorage_Release(childStorage
);
2120 IEnumSTATSTG_Release(elements
);
2125 /*********************************************************************
2129 * Perform the deletion of a stream's data
2132 static HRESULT
deleteStreamContents(
2133 StorageBaseImpl
*parentStorage
,
2134 DirRef indexToDelete
,
2135 DirEntry entryDataToDelete
)
2139 ULARGE_INTEGER size
;
2140 StgStreamImpl
*strm
, *strm2
;
2142 /* Invalidate any open stream objects. */
2143 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2145 if (strm
->dirEntry
== indexToDelete
)
2147 TRACE("Stream deleted %p\n", strm
);
2148 strm
->parentStorage
= NULL
;
2149 list_remove(&strm
->StrmListEntry
);
2153 size
.u
.HighPart
= 0;
2156 hr
= StorageBaseImpl_OpenStream(&parentStorage
->IStorage_iface
,
2157 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2167 hr
= IStream_SetSize(pis
, size
);
2175 * Release the stream object.
2177 IStream_Release(pis
);
2182 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2186 case DIRENTRY_RELATION_PREVIOUS
:
2187 entry
->leftChild
= new_target
;
2189 case DIRENTRY_RELATION_NEXT
:
2190 entry
->rightChild
= new_target
;
2192 case DIRENTRY_RELATION_DIR
:
2193 entry
->dirRootEntry
= new_target
;
2200 /*************************************************************************
2204 * This method removes a directory entry from its parent storage tree without
2205 * freeing any resources attached to it.
2207 static HRESULT
removeFromTree(
2208 StorageBaseImpl
*This
,
2209 DirRef parentStorageIndex
,
2210 DirRef deletedIndex
)
2213 DirEntry entryToDelete
;
2214 DirEntry parentEntry
;
2215 DirRef parentEntryRef
;
2216 ULONG typeOfRelation
;
2218 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2224 * Find the element that links to the one we want to delete.
2226 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2227 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2232 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2235 * Replace the deleted entry with its left child
2237 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2239 hr
= StorageBaseImpl_WriteDirEntry(
2248 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2251 * We need to reinsert the right child somewhere. We already know it and
2252 * its children are greater than everything in the left tree, so we
2253 * insert it at the rightmost point in the left tree.
2255 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2256 DirEntry newRightChildParentEntry
;
2260 hr
= StorageBaseImpl_ReadDirEntry(
2262 newRightChildParent
,
2263 &newRightChildParentEntry
);
2269 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2270 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2271 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2273 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2275 hr
= StorageBaseImpl_WriteDirEntry(
2277 newRightChildParent
,
2278 &newRightChildParentEntry
);
2288 * Replace the deleted entry with its right child
2290 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2292 hr
= StorageBaseImpl_WriteDirEntry(
2306 /******************************************************************************
2307 * SetElementTimes (IStorage)
2309 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2311 const OLECHAR
*pwcsName
,/* [string][in] */
2312 const FILETIME
*pctime
, /* [in] */
2313 const FILETIME
*patime
, /* [in] */
2314 const FILETIME
*pmtime
) /* [in] */
2316 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2320 /******************************************************************************
2321 * SetStateBits (IStorage)
2323 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2325 DWORD grfStateBits
,/* [in] */
2326 DWORD grfMask
) /* [in] */
2328 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2331 return STG_E_REVERTED
;
2333 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2337 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2338 DirRef index
, const DirEntry
*data
)
2340 StorageImpl
*This
= (StorageImpl
*)base
;
2341 return StorageImpl_WriteDirEntry(This
, index
, data
);
2344 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2345 DirRef index
, DirEntry
*data
)
2347 StorageImpl
*This
= (StorageImpl
*)base
;
2348 return StorageImpl_ReadDirEntry(This
, index
, data
);
2351 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2355 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2357 if (!This
->blockChainCache
[i
])
2359 return &This
->blockChainCache
[i
];
2363 i
= This
->blockChainToEvict
;
2365 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2366 This
->blockChainCache
[i
] = NULL
;
2368 This
->blockChainToEvict
++;
2369 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2370 This
->blockChainToEvict
= 0;
2372 return &This
->blockChainCache
[i
];
2375 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2378 int i
, free_index
=-1;
2380 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2382 if (!This
->blockChainCache
[i
])
2384 if (free_index
== -1) free_index
= i
;
2386 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2388 return &This
->blockChainCache
[i
];
2392 if (free_index
== -1)
2394 free_index
= This
->blockChainToEvict
;
2396 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2397 This
->blockChainCache
[free_index
] = NULL
;
2399 This
->blockChainToEvict
++;
2400 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2401 This
->blockChainToEvict
= 0;
2404 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2405 return &This
->blockChainCache
[free_index
];
2408 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2412 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2414 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2416 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2417 This
->blockChainCache
[i
] = NULL
;
2423 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2424 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2426 StorageImpl
*This
= (StorageImpl
*)base
;
2431 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2432 if (FAILED(hr
)) return hr
;
2434 if (data
.size
.QuadPart
== 0)
2440 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2442 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2449 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2451 SmallBlockChainStream
*stream
;
2453 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2454 if (!stream
) return E_OUTOFMEMORY
;
2456 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2458 SmallBlockChainStream_Destroy(stream
);
2464 BlockChainStream
*stream
= NULL
;
2466 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2467 if (!stream
) return E_OUTOFMEMORY
;
2469 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2475 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2476 ULARGE_INTEGER newsize
)
2478 StorageImpl
*This
= (StorageImpl
*)base
;
2481 SmallBlockChainStream
*smallblock
=NULL
;
2482 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2484 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2485 if (FAILED(hr
)) return hr
;
2487 /* In simple mode keep the stream size above the small block limit */
2488 if (This
->base
.openFlags
& STGM_SIMPLE
)
2489 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2491 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2494 /* Create a block chain object of the appropriate type */
2495 if (data
.size
.QuadPart
== 0)
2497 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2499 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2500 if (!smallblock
) return E_OUTOFMEMORY
;
2504 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2505 bigblock
= *pbigblock
;
2506 if (!bigblock
) return E_OUTOFMEMORY
;
2509 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2511 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2512 if (!smallblock
) return E_OUTOFMEMORY
;
2516 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2517 bigblock
= *pbigblock
;
2518 if (!bigblock
) return E_OUTOFMEMORY
;
2521 /* Change the block chain type if necessary. */
2522 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2524 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2527 SmallBlockChainStream_Destroy(smallblock
);
2531 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2532 *pbigblock
= bigblock
;
2534 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2536 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2541 /* Set the size of the block chain. */
2544 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2545 SmallBlockChainStream_Destroy(smallblock
);
2549 BlockChainStream_SetSize(bigblock
, newsize
);
2552 /* Set the size in the directory entry. */
2553 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2556 data
.size
= newsize
;
2558 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2563 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2564 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2566 StorageImpl
*This
= (StorageImpl
*)base
;
2569 ULARGE_INTEGER newSize
;
2571 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2572 if (FAILED(hr
)) return hr
;
2574 /* Grow the stream if necessary */
2575 newSize
.QuadPart
= 0;
2576 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2578 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2580 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2584 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2585 if (FAILED(hr
)) return hr
;
2588 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2590 SmallBlockChainStream
*stream
;
2592 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2593 if (!stream
) return E_OUTOFMEMORY
;
2595 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2597 SmallBlockChainStream_Destroy(stream
);
2603 BlockChainStream
*stream
;
2605 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2606 if (!stream
) return E_OUTOFMEMORY
;
2608 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2614 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2617 StorageImpl
*This
= (StorageImpl
*)base
;
2618 DirEntry dst_data
, src_data
;
2621 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2624 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2628 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2629 dst_data
.startingBlock
= src_data
.startingBlock
;
2630 dst_data
.size
= src_data
.size
;
2632 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2638 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2640 StorageImpl
*This
= (StorageImpl
*) iface
;
2644 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2646 *result
= statstg
.pwcsName
;
2652 * Virtual function table for the IStorage32Impl class.
2654 static const IStorageVtbl Storage32Impl_Vtbl
=
2656 StorageBaseImpl_QueryInterface
,
2657 StorageBaseImpl_AddRef
,
2658 StorageBaseImpl_Release
,
2659 StorageBaseImpl_CreateStream
,
2660 StorageBaseImpl_OpenStream
,
2661 StorageBaseImpl_CreateStorage
,
2662 StorageBaseImpl_OpenStorage
,
2663 StorageBaseImpl_CopyTo
,
2664 StorageBaseImpl_MoveElementTo
,
2667 StorageBaseImpl_EnumElements
,
2668 StorageBaseImpl_DestroyElement
,
2669 StorageBaseImpl_RenameElement
,
2670 StorageBaseImpl_SetElementTimes
,
2671 StorageBaseImpl_SetClass
,
2672 StorageBaseImpl_SetStateBits
,
2673 StorageBaseImpl_Stat
2676 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2678 StorageImpl_Destroy
,
2679 StorageImpl_Invalidate
,
2681 StorageImpl_GetFilename
,
2682 StorageImpl_CreateDirEntry
,
2683 StorageImpl_BaseWriteDirEntry
,
2684 StorageImpl_BaseReadDirEntry
,
2685 StorageImpl_DestroyDirEntry
,
2686 StorageImpl_StreamReadAt
,
2687 StorageImpl_StreamWriteAt
,
2688 StorageImpl_StreamSetSize
,
2689 StorageImpl_StreamLink
2692 static HRESULT
StorageImpl_Construct(
2700 StorageImpl
** result
)
2704 DirEntry currentEntry
;
2705 DirRef currentEntryRef
;
2707 if ( FAILED( validateSTGM(openFlags
) ))
2708 return STG_E_INVALIDFLAG
;
2710 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2712 return E_OUTOFMEMORY
;
2714 memset(This
, 0, sizeof(StorageImpl
));
2716 list_init(&This
->base
.strmHead
);
2718 list_init(&This
->base
.storageHead
);
2720 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
2721 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2722 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2723 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2725 This
->base
.create
= create
;
2727 This
->base
.reverted
= 0;
2730 * Initialize the big block cache.
2732 This
->bigBlockSize
= sector_size
;
2733 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2735 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2738 This
->lockBytes
= pLkbyt
;
2739 ILockBytes_AddRef(pLkbyt
);
2747 ULARGE_INTEGER size
;
2748 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2750 /* Discard any existing data. */
2752 ILockBytes_SetSize(This
->lockBytes
, size
);
2755 * Initialize all header variables:
2756 * - The big block depot consists of one block and it is at block 0
2757 * - The directory table starts at block 1
2758 * - There is no small block depot
2760 memset( This
->bigBlockDepotStart
,
2762 sizeof(This
->bigBlockDepotStart
));
2764 This
->bigBlockDepotCount
= 1;
2765 This
->bigBlockDepotStart
[0] = 0;
2766 This
->rootStartBlock
= 1;
2767 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2768 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2769 if (sector_size
== 4096)
2770 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2772 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2773 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2774 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2775 This
->extBigBlockDepotCount
= 0;
2777 StorageImpl_SaveFileHeader(This
);
2780 * Add one block for the big block depot and one block for the directory table
2782 size
.u
.HighPart
= 0;
2783 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2784 ILockBytes_SetSize(This
->lockBytes
, size
);
2787 * Initialize the big block depot
2789 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2790 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2791 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2792 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2797 * Load the header for the file.
2799 hr
= StorageImpl_LoadFileHeader(This
);
2808 * There is no block depot cached yet.
2810 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2811 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
2814 * Start searching for free blocks with block 0.
2816 This
->prevFreeBlock
= 0;
2818 This
->firstFreeSmallBlock
= 0;
2820 /* Read the extended big block depot locations. */
2821 if (This
->extBigBlockDepotCount
!= 0)
2823 ULONG current_block
= This
->extBigBlockDepotStart
;
2824 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2827 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2828 if (!This
->extBigBlockDepotLocations
)
2834 This
->extBigBlockDepotLocationsSize
= cache_size
;
2836 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2838 if (current_block
== BLOCK_END_OF_CHAIN
)
2840 WARN("File has too few extended big block depot blocks.\n");
2841 hr
= STG_E_DOCFILECORRUPT
;
2844 This
->extBigBlockDepotLocations
[i
] = current_block
;
2845 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2850 This
->extBigBlockDepotLocations
= NULL
;
2851 This
->extBigBlockDepotLocationsSize
= 0;
2855 * Create the block chain abstractions.
2857 if(!(This
->rootBlockChain
=
2858 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2860 hr
= STG_E_READFAULT
;
2864 if(!(This
->smallBlockDepotChain
=
2865 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2868 hr
= STG_E_READFAULT
;
2873 * Write the root storage entry (memory only)
2877 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2880 * Initialize the directory table
2882 memset(&rootEntry
, 0, sizeof(rootEntry
));
2883 strcpyW(rootEntry
.name
, rootentryW
);
2884 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
2885 rootEntry
.stgType
= STGTY_ROOT
;
2886 rootEntry
.leftChild
= DIRENTRY_NULL
;
2887 rootEntry
.rightChild
= DIRENTRY_NULL
;
2888 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2889 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2890 rootEntry
.size
.u
.HighPart
= 0;
2891 rootEntry
.size
.u
.LowPart
= 0;
2893 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2897 * Find the ID of the root storage.
2899 currentEntryRef
= 0;
2903 hr
= StorageImpl_ReadDirEntry(
2910 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2911 (currentEntry
.stgType
== STGTY_ROOT
) )
2913 This
->base
.storageDirEntry
= currentEntryRef
;
2919 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2923 hr
= STG_E_READFAULT
;
2928 * Create the block chain abstraction for the small block root chain.
2930 if(!(This
->smallBlockRootChain
=
2931 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2933 hr
= STG_E_READFAULT
;
2939 IStorage_Release(&This
->base
.IStorage_iface
);
2944 StorageImpl_Flush((StorageBaseImpl
*)This
);
2951 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2953 StorageImpl
*This
= (StorageImpl
*) iface
;
2955 StorageBaseImpl_DeleteAll(&This
->base
);
2957 This
->base
.reverted
= 1;
2960 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2962 StorageImpl
*This
= (StorageImpl
*) iface
;
2964 TRACE("(%p)\n", This
);
2966 StorageImpl_Flush(iface
);
2968 StorageImpl_Invalidate(iface
);
2970 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
2972 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2973 BlockChainStream_Destroy(This
->rootBlockChain
);
2974 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2976 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2977 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2979 if (This
->lockBytes
)
2980 ILockBytes_Release(This
->lockBytes
);
2981 HeapFree(GetProcessHeap(), 0, This
);
2984 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
)
2986 StorageImpl
*This
= (StorageImpl
*) iface
;
2989 TRACE("(%p)\n", This
);
2991 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
2994 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
2997 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
2999 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3000 if (This
->blockChainCache
[i
])
3001 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3004 hr
= ILockBytes_Flush(This
->lockBytes
);
3009 /******************************************************************************
3010 * Storage32Impl_GetNextFreeBigBlock
3012 * Returns the index of the next free big block.
3013 * If the big block depot is filled, this method will enlarge it.
3016 static ULONG
StorageImpl_GetNextFreeBigBlock(
3019 ULONG depotBlockIndexPos
;
3020 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3022 ULONG depotBlockOffset
;
3023 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3024 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3026 ULONG freeBlock
= BLOCK_UNUSED
;
3027 ULARGE_INTEGER neededSize
;
3030 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3031 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3034 * Scan the entire big block depot until we find a block marked free
3036 while (nextBlockIndex
!= BLOCK_UNUSED
)
3038 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3040 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3043 * Grow the primary depot.
3045 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3047 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3050 * Add a block depot.
3052 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3053 This
->bigBlockDepotCount
++;
3054 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3057 * Flag it as a block depot.
3059 StorageImpl_SetNextBlockInChain(This
,
3063 /* Save new header information.
3065 StorageImpl_SaveFileHeader(This
);
3070 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3072 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3075 * Grow the extended depot.
3077 ULONG extIndex
= BLOCK_UNUSED
;
3078 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3079 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3081 if (extBlockOffset
== 0)
3083 /* We need an extended block.
3085 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3086 This
->extBigBlockDepotCount
++;
3087 depotBlockIndexPos
= extIndex
+ 1;
3090 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3093 * Add a block depot and mark it in the extended block.
3095 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3096 This
->bigBlockDepotCount
++;
3097 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3099 /* Flag the block depot.
3101 StorageImpl_SetNextBlockInChain(This
,
3105 /* If necessary, flag the extended depot block.
3107 if (extIndex
!= BLOCK_UNUSED
)
3108 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3110 /* Save header information.
3112 StorageImpl_SaveFileHeader(This
);
3116 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3120 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3121 ( nextBlockIndex
!= BLOCK_UNUSED
))
3123 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3125 if (nextBlockIndex
== BLOCK_UNUSED
)
3127 freeBlock
= (depotIndex
* blocksPerDepot
) +
3128 (depotBlockOffset
/sizeof(ULONG
));
3131 depotBlockOffset
+= sizeof(ULONG
);
3136 depotBlockOffset
= 0;
3140 * make sure that the block physically exists before using it
3142 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3144 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3146 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3147 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3149 This
->prevFreeBlock
= freeBlock
;
3154 /******************************************************************************
3155 * Storage32Impl_AddBlockDepot
3157 * This will create a depot block, essentially it is a block initialized
3160 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3162 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3165 * Initialize blocks as free
3167 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3168 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3171 /******************************************************************************
3172 * Storage32Impl_GetExtDepotBlock
3174 * Returns the index of the block that corresponds to the specified depot
3175 * index. This method is only for depot indexes equal or greater than
3176 * COUNT_BBDEPOTINHEADER.
3178 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3180 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3181 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3182 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3183 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3184 ULONG blockIndex
= BLOCK_UNUSED
;
3185 ULONG extBlockIndex
;
3186 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3187 int index
, num_blocks
;
3189 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3191 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3192 return BLOCK_UNUSED
;
3194 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3196 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3198 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
);
3200 num_blocks
= This
->bigBlockSize
/ 4;
3202 for (index
= 0; index
< num_blocks
; index
++)
3204 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3205 This
->extBlockDepotCached
[index
] = blockIndex
;
3208 This
->indexExtBlockDepotCached
= extBlockCount
;
3211 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3216 /******************************************************************************
3217 * Storage32Impl_SetExtDepotBlock
3219 * Associates the specified block index to the specified depot index.
3220 * This method is only for depot indexes equal or greater than
3221 * COUNT_BBDEPOTINHEADER.
3223 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3225 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3226 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3227 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3228 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3229 ULONG extBlockIndex
;
3231 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3233 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3235 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3237 if (extBlockIndex
!= BLOCK_UNUSED
)
3239 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3240 extBlockOffset
* sizeof(ULONG
),
3244 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3246 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3250 /******************************************************************************
3251 * Storage32Impl_AddExtBlockDepot
3253 * Creates an extended depot block.
3255 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3257 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3258 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3259 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3260 ULONG index
= BLOCK_UNUSED
;
3261 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3262 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3263 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3265 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3266 blocksPerDepotBlock
;
3268 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3271 * The first extended block.
3273 This
->extBigBlockDepotStart
= index
;
3278 * Find the last existing extended block.
3280 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3283 * Add the new extended block to the chain.
3285 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3290 * Initialize this block.
3292 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3293 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3295 /* Add the block to our cache. */
3296 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3298 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3299 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3301 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3302 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3304 This
->extBigBlockDepotLocations
= new_cache
;
3305 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3307 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3312 /******************************************************************************
3313 * Storage32Impl_FreeBigBlock
3315 * This method will flag the specified block as free in the big block depot.
3317 static void StorageImpl_FreeBigBlock(
3321 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3323 if (blockIndex
< This
->prevFreeBlock
)
3324 This
->prevFreeBlock
= blockIndex
;
3327 /************************************************************************
3328 * Storage32Impl_GetNextBlockInChain
3330 * This method will retrieve the block index of the next big block in
3333 * Params: This - Pointer to the Storage object.
3334 * blockIndex - Index of the block to retrieve the chain
3336 * nextBlockIndex - receives the return value.
3338 * Returns: This method returns the index of the next block in the chain.
3339 * It will return the constants:
3340 * BLOCK_SPECIAL - If the block given was not part of a
3342 * BLOCK_END_OF_CHAIN - If the block given was the last in
3344 * BLOCK_UNUSED - If the block given was not past of a chain
3346 * BLOCK_EXTBBDEPOT - This block is part of the extended
3349 * See Windows documentation for more details on IStorage methods.
3351 static HRESULT
StorageImpl_GetNextBlockInChain(
3354 ULONG
* nextBlockIndex
)
3356 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3357 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3358 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3359 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3361 ULONG depotBlockIndexPos
;
3362 int index
, num_blocks
;
3364 *nextBlockIndex
= BLOCK_SPECIAL
;
3366 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3368 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3369 This
->bigBlockDepotCount
);
3370 return STG_E_READFAULT
;
3374 * Cache the currently accessed depot block.
3376 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3378 This
->indexBlockDepotCached
= depotBlockCount
;
3380 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3382 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3387 * We have to look in the extended depot.
3389 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3392 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3395 return STG_E_READFAULT
;
3397 num_blocks
= This
->bigBlockSize
/ 4;
3399 for (index
= 0; index
< num_blocks
; index
++)
3401 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3402 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3406 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3411 /******************************************************************************
3412 * Storage32Impl_GetNextExtendedBlock
3414 * Given an extended block this method will return the next extended block.
3417 * The last ULONG of an extended block is the block index of the next
3418 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3422 * - The index of the next extended block
3423 * - BLOCK_UNUSED: there is no next extended block.
3424 * - Any other return values denotes failure.
3426 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3428 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3429 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3431 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3434 return nextBlockIndex
;
3437 /******************************************************************************
3438 * Storage32Impl_SetNextBlockInChain
3440 * This method will write the index of the specified block's next block
3441 * in the big block depot.
3443 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3446 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3447 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3448 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3451 static void StorageImpl_SetNextBlockInChain(
3456 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3457 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3458 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3459 ULONG depotBlockIndexPos
;
3461 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3462 assert(blockIndex
!= nextBlock
);
3464 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3466 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3471 * We have to look in the extended depot.
3473 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3476 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3479 * Update the cached block depot, if necessary.
3481 if (depotBlockCount
== This
->indexBlockDepotCached
)
3483 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3487 /******************************************************************************
3488 * Storage32Impl_LoadFileHeader
3490 * This method will read in the file header
3492 static HRESULT
StorageImpl_LoadFileHeader(
3496 BYTE headerBigBlock
[HEADER_SIZE
];
3498 ULARGE_INTEGER offset
;
3503 * Get a pointer to the big block of data containing the header.
3505 offset
.u
.HighPart
= 0;
3506 offset
.u
.LowPart
= 0;
3507 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3508 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3509 hr
= STG_E_FILENOTFOUND
;
3512 * Extract the information from the header.
3517 * Check for the "magic number" signature and return an error if it is not
3520 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3522 return STG_E_OLDFORMAT
;
3525 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3527 return STG_E_INVALIDHEADER
;
3530 StorageUtl_ReadWord(
3532 OFFSET_BIGBLOCKSIZEBITS
,
3533 &This
->bigBlockSizeBits
);
3535 StorageUtl_ReadWord(
3537 OFFSET_SMALLBLOCKSIZEBITS
,
3538 &This
->smallBlockSizeBits
);
3540 StorageUtl_ReadDWord(
3542 OFFSET_BBDEPOTCOUNT
,
3543 &This
->bigBlockDepotCount
);
3545 StorageUtl_ReadDWord(
3547 OFFSET_ROOTSTARTBLOCK
,
3548 &This
->rootStartBlock
);
3550 StorageUtl_ReadDWord(
3552 OFFSET_SMALLBLOCKLIMIT
,
3553 &This
->smallBlockLimit
);
3555 StorageUtl_ReadDWord(
3557 OFFSET_SBDEPOTSTART
,
3558 &This
->smallBlockDepotStart
);
3560 StorageUtl_ReadDWord(
3562 OFFSET_EXTBBDEPOTSTART
,
3563 &This
->extBigBlockDepotStart
);
3565 StorageUtl_ReadDWord(
3567 OFFSET_EXTBBDEPOTCOUNT
,
3568 &This
->extBigBlockDepotCount
);
3570 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3572 StorageUtl_ReadDWord(
3574 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3575 &(This
->bigBlockDepotStart
[index
]));
3579 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3581 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3582 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3585 * Right now, the code is making some assumptions about the size of the
3586 * blocks, just make sure they are what we're expecting.
3588 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3589 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3590 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3592 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3593 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3594 hr
= STG_E_INVALIDHEADER
;
3603 /******************************************************************************
3604 * Storage32Impl_SaveFileHeader
3606 * This method will save to the file the header
3608 static void StorageImpl_SaveFileHeader(
3611 BYTE headerBigBlock
[HEADER_SIZE
];
3614 ULARGE_INTEGER offset
;
3615 DWORD bytes_read
, bytes_written
;
3616 DWORD major_version
, dirsectorcount
;
3619 * Get a pointer to the big block of data containing the header.
3621 offset
.u
.HighPart
= 0;
3622 offset
.u
.LowPart
= 0;
3623 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3624 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3625 hr
= STG_E_FILENOTFOUND
;
3627 if (This
->bigBlockSizeBits
== 0x9)
3629 else if (This
->bigBlockSizeBits
== 0xc)
3633 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3638 * If the block read failed, the file is probably new.
3643 * Initialize for all unknown fields.
3645 memset(headerBigBlock
, 0, HEADER_SIZE
);
3648 * Initialize the magic number.
3650 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3654 * Write the information to the header.
3656 StorageUtl_WriteWord(
3658 OFFSET_MINORVERSION
,
3661 StorageUtl_WriteWord(
3663 OFFSET_MAJORVERSION
,
3666 StorageUtl_WriteWord(
3668 OFFSET_BYTEORDERMARKER
,
3671 StorageUtl_WriteWord(
3673 OFFSET_BIGBLOCKSIZEBITS
,
3674 This
->bigBlockSizeBits
);
3676 StorageUtl_WriteWord(
3678 OFFSET_SMALLBLOCKSIZEBITS
,
3679 This
->smallBlockSizeBits
);
3681 if (major_version
>= 4)
3683 if (This
->rootBlockChain
)
3684 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3686 /* This file is being created, and it will start out with one block. */
3690 /* This field must be 0 in versions older than 4 */
3693 StorageUtl_WriteDWord(
3695 OFFSET_DIRSECTORCOUNT
,
3698 StorageUtl_WriteDWord(
3700 OFFSET_BBDEPOTCOUNT
,
3701 This
->bigBlockDepotCount
);
3703 StorageUtl_WriteDWord(
3705 OFFSET_ROOTSTARTBLOCK
,
3706 This
->rootStartBlock
);
3708 StorageUtl_WriteDWord(
3710 OFFSET_SMALLBLOCKLIMIT
,
3711 This
->smallBlockLimit
);
3713 StorageUtl_WriteDWord(
3715 OFFSET_SBDEPOTSTART
,
3716 This
->smallBlockDepotStart
);
3718 StorageUtl_WriteDWord(
3720 OFFSET_SBDEPOTCOUNT
,
3721 This
->smallBlockDepotChain
?
3722 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3724 StorageUtl_WriteDWord(
3726 OFFSET_EXTBBDEPOTSTART
,
3727 This
->extBigBlockDepotStart
);
3729 StorageUtl_WriteDWord(
3731 OFFSET_EXTBBDEPOTCOUNT
,
3732 This
->extBigBlockDepotCount
);
3734 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3736 StorageUtl_WriteDWord(
3738 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3739 (This
->bigBlockDepotStart
[index
]));
3743 * Write the big block back to the file.
3745 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3748 /******************************************************************************
3749 * StorageImpl_ReadRawDirEntry
3751 * This method will read the raw data from a directory entry in the file.
3753 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3755 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3757 ULARGE_INTEGER offset
;
3761 offset
.u
.HighPart
= 0;
3762 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3764 hr
= BlockChainStream_ReadAt(
3765 This
->rootBlockChain
,
3771 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3772 return STG_E_READFAULT
;
3777 /******************************************************************************
3778 * StorageImpl_WriteRawDirEntry
3780 * This method will write the raw data from a directory entry in the file.
3782 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3784 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3786 ULARGE_INTEGER offset
;
3790 offset
.u
.HighPart
= 0;
3791 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3793 hr
= BlockChainStream_WriteAt(
3794 This
->rootBlockChain
,
3803 /******************************************************************************
3806 * Update raw directory entry data from the fields in newData.
3808 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3810 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3812 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3815 buffer
+ OFFSET_PS_NAME
,
3817 DIRENTRY_NAME_BUFFER_LEN
);
3819 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3821 StorageUtl_WriteWord(
3823 OFFSET_PS_NAMELENGTH
,
3824 newData
->sizeOfNameString
);
3826 StorageUtl_WriteDWord(
3828 OFFSET_PS_LEFTCHILD
,
3829 newData
->leftChild
);
3831 StorageUtl_WriteDWord(
3833 OFFSET_PS_RIGHTCHILD
,
3834 newData
->rightChild
);
3836 StorageUtl_WriteDWord(
3839 newData
->dirRootEntry
);
3841 StorageUtl_WriteGUID(
3846 StorageUtl_WriteDWord(
3849 newData
->ctime
.dwLowDateTime
);
3851 StorageUtl_WriteDWord(
3853 OFFSET_PS_CTIMEHIGH
,
3854 newData
->ctime
.dwHighDateTime
);
3856 StorageUtl_WriteDWord(
3859 newData
->mtime
.dwLowDateTime
);
3861 StorageUtl_WriteDWord(
3863 OFFSET_PS_MTIMEHIGH
,
3864 newData
->ctime
.dwHighDateTime
);
3866 StorageUtl_WriteDWord(
3868 OFFSET_PS_STARTBLOCK
,
3869 newData
->startingBlock
);
3871 StorageUtl_WriteDWord(
3874 newData
->size
.u
.LowPart
);
3877 /******************************************************************************
3878 * Storage32Impl_ReadDirEntry
3880 * This method will read the specified directory entry.
3882 HRESULT
StorageImpl_ReadDirEntry(
3887 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3890 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3892 if (SUCCEEDED(readRes
))
3894 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3897 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3898 DIRENTRY_NAME_BUFFER_LEN
);
3899 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3901 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3903 StorageUtl_ReadWord(
3905 OFFSET_PS_NAMELENGTH
,
3906 &buffer
->sizeOfNameString
);
3908 StorageUtl_ReadDWord(
3910 OFFSET_PS_LEFTCHILD
,
3911 &buffer
->leftChild
);
3913 StorageUtl_ReadDWord(
3915 OFFSET_PS_RIGHTCHILD
,
3916 &buffer
->rightChild
);
3918 StorageUtl_ReadDWord(
3921 &buffer
->dirRootEntry
);
3923 StorageUtl_ReadGUID(
3928 StorageUtl_ReadDWord(
3931 &buffer
->ctime
.dwLowDateTime
);
3933 StorageUtl_ReadDWord(
3935 OFFSET_PS_CTIMEHIGH
,
3936 &buffer
->ctime
.dwHighDateTime
);
3938 StorageUtl_ReadDWord(
3941 &buffer
->mtime
.dwLowDateTime
);
3943 StorageUtl_ReadDWord(
3945 OFFSET_PS_MTIMEHIGH
,
3946 &buffer
->mtime
.dwHighDateTime
);
3948 StorageUtl_ReadDWord(
3950 OFFSET_PS_STARTBLOCK
,
3951 &buffer
->startingBlock
);
3953 StorageUtl_ReadDWord(
3956 &buffer
->size
.u
.LowPart
);
3958 buffer
->size
.u
.HighPart
= 0;
3964 /*********************************************************************
3965 * Write the specified directory entry to the file
3967 HRESULT
StorageImpl_WriteDirEntry(
3970 const DirEntry
* buffer
)
3972 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3975 UpdateRawDirEntry(currentEntry
, buffer
);
3977 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3981 static BOOL
StorageImpl_ReadBigBlock(
3986 ULARGE_INTEGER ulOffset
;
3989 ulOffset
.u
.HighPart
= 0;
3990 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3992 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3994 if (read
&& read
< This
->bigBlockSize
)
3996 /* File ends during this block; fill the rest with 0's. */
3997 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4003 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4009 ULARGE_INTEGER ulOffset
;
4013 ulOffset
.u
.HighPart
= 0;
4014 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4015 ulOffset
.u
.LowPart
+= offset
;
4017 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4018 *value
= lendian32toh(tmp
);
4019 return (read
== sizeof(DWORD
));
4022 static BOOL
StorageImpl_WriteBigBlock(
4027 ULARGE_INTEGER ulOffset
;
4030 ulOffset
.u
.HighPart
= 0;
4031 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4033 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4034 return (wrote
== This
->bigBlockSize
);
4037 static BOOL
StorageImpl_WriteDWordToBigBlock(
4043 ULARGE_INTEGER ulOffset
;
4046 ulOffset
.u
.HighPart
= 0;
4047 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4048 ulOffset
.u
.LowPart
+= offset
;
4050 value
= htole32(value
);
4051 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4052 return (wrote
== sizeof(DWORD
));
4055 /******************************************************************************
4056 * Storage32Impl_SmallBlocksToBigBlocks
4058 * This method will convert a small block chain to a big block chain.
4059 * The small block chain will be destroyed.
4061 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4063 SmallBlockChainStream
** ppsbChain
)
4065 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4066 ULARGE_INTEGER size
, offset
;
4067 ULONG cbRead
, cbWritten
;
4068 ULARGE_INTEGER cbTotalRead
;
4069 DirRef streamEntryRef
;
4070 HRESULT resWrite
= S_OK
;
4072 DirEntry streamEntry
;
4074 BlockChainStream
*bbTempChain
= NULL
;
4075 BlockChainStream
*bigBlockChain
= NULL
;
4078 * Create a temporary big block chain that doesn't have
4079 * an associated directory entry. This temporary chain will be
4080 * used to copy data from small blocks to big blocks.
4082 bbTempChain
= BlockChainStream_Construct(This
,
4085 if(!bbTempChain
) return NULL
;
4087 * Grow the big block chain.
4089 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4090 BlockChainStream_SetSize(bbTempChain
, size
);
4093 * Copy the contents of the small block chain to the big block chain
4094 * by small block size increments.
4096 offset
.u
.LowPart
= 0;
4097 offset
.u
.HighPart
= 0;
4098 cbTotalRead
.QuadPart
= 0;
4100 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4103 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4105 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4108 if (FAILED(resRead
))
4113 cbTotalRead
.QuadPart
+= cbRead
;
4115 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4121 if (FAILED(resWrite
))
4124 offset
.u
.LowPart
+= cbRead
;
4128 resRead
= STG_E_READFAULT
;
4131 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4132 HeapFree(GetProcessHeap(),0,buffer
);
4134 size
.u
.HighPart
= 0;
4137 if (FAILED(resRead
) || FAILED(resWrite
))
4139 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4140 BlockChainStream_SetSize(bbTempChain
, size
);
4141 BlockChainStream_Destroy(bbTempChain
);
4146 * Destroy the small block chain.
4148 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4149 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4150 SmallBlockChainStream_Destroy(*ppsbChain
);
4154 * Change the directory entry. This chain is now a big block chain
4155 * and it doesn't reside in the small blocks chain anymore.
4157 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4159 streamEntry
.startingBlock
= bbHeadOfChain
;
4161 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4164 * Destroy the temporary entryless big block chain.
4165 * Create a new big block chain associated with this entry.
4167 BlockChainStream_Destroy(bbTempChain
);
4168 bigBlockChain
= BlockChainStream_Construct(This
,
4172 return bigBlockChain
;
4175 /******************************************************************************
4176 * Storage32Impl_BigBlocksToSmallBlocks
4178 * This method will convert a big block chain to a small block chain.
4179 * The big block chain will be destroyed on success.
4181 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4183 BlockChainStream
** ppbbChain
,
4184 ULARGE_INTEGER newSize
)
4186 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4187 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4188 DirRef streamEntryRef
;
4189 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4190 DirEntry streamEntry
;
4192 SmallBlockChainStream
* sbTempChain
;
4194 TRACE("%p %p\n", This
, ppbbChain
);
4196 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4202 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4203 size
= BlockChainStream_GetSize(*ppbbChain
);
4204 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4206 offset
.u
.HighPart
= 0;
4207 offset
.u
.LowPart
= 0;
4208 cbTotalRead
.QuadPart
= 0;
4209 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4210 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4212 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4213 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4221 cbTotalRead
.QuadPart
+= cbRead
;
4223 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4224 cbRead
, buffer
, &cbWritten
);
4226 if(FAILED(resWrite
))
4229 offset
.u
.LowPart
+= cbRead
;
4233 resRead
= STG_E_READFAULT
;
4237 HeapFree(GetProcessHeap(), 0, buffer
);
4239 size
.u
.HighPart
= 0;
4242 if(FAILED(resRead
) || FAILED(resWrite
))
4244 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4245 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4246 SmallBlockChainStream_Destroy(sbTempChain
);
4250 /* destroy the original big block chain */
4251 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4252 BlockChainStream_SetSize(*ppbbChain
, size
);
4253 BlockChainStream_Destroy(*ppbbChain
);
4256 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4257 streamEntry
.startingBlock
= sbHeadOfChain
;
4258 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4260 SmallBlockChainStream_Destroy(sbTempChain
);
4261 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4264 static HRESULT
StorageBaseImpl_CopyStream(
4265 StorageBaseImpl
*dst
, DirRef dst_entry
,
4266 StorageBaseImpl
*src
, DirRef src_entry
)
4271 ULARGE_INTEGER bytes_copied
;
4272 ULONG bytestocopy
, bytesread
, byteswritten
;
4274 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4278 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4280 bytes_copied
.QuadPart
= 0;
4281 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4283 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4285 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4287 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4290 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4291 data
, &byteswritten
);
4294 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4295 bytes_copied
.QuadPart
+= byteswritten
;
4303 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4305 DirRef result
=This
->firstFreeEntry
;
4307 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4310 if (result
== This
->entries_size
)
4312 ULONG new_size
= This
->entries_size
* 2;
4313 TransactedDirEntry
*new_entries
;
4315 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4316 if (!new_entries
) return DIRENTRY_NULL
;
4318 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4319 HeapFree(GetProcessHeap(), 0, This
->entries
);
4321 This
->entries
= new_entries
;
4322 This
->entries_size
= new_size
;
4325 This
->entries
[result
].inuse
= 1;
4327 This
->firstFreeEntry
= result
+1;
4332 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4333 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4335 DirRef stubEntryRef
;
4336 TransactedDirEntry
*entry
;
4338 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4340 if (stubEntryRef
!= DIRENTRY_NULL
)
4342 entry
= &This
->entries
[stubEntryRef
];
4344 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4349 return stubEntryRef
;
4352 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4353 TransactedSnapshotImpl
*This
, DirRef entry
)
4358 if (!This
->entries
[entry
].read
)
4360 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4361 This
->entries
[entry
].transactedParentEntry
,
4364 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4366 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4368 if (data
.leftChild
== DIRENTRY_NULL
)
4372 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4374 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4376 if (data
.rightChild
== DIRENTRY_NULL
)
4380 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4382 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4384 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4390 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4391 This
->entries
[entry
].read
= 1;
4398 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4399 TransactedSnapshotImpl
*This
, DirRef entry
)
4403 if (!This
->entries
[entry
].stream_dirty
)
4405 DirEntry new_entrydata
;
4407 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4408 new_entrydata
.name
[0] = 'S';
4409 new_entrydata
.sizeOfNameString
= 1;
4410 new_entrydata
.stgType
= STGTY_STREAM
;
4411 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4412 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4413 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4414 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4416 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4417 &This
->entries
[entry
].stream_entry
);
4419 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4421 hr
= StorageBaseImpl_CopyStream(
4422 This
->scratch
, This
->entries
[entry
].stream_entry
,
4423 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4426 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4430 This
->entries
[entry
].stream_dirty
= 1;
4432 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4434 /* Since this entry is modified, and we aren't using its stream data, we
4435 * no longer care about the original entry. */
4437 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4439 if (delete_ref
!= DIRENTRY_NULL
)
4440 This
->entries
[delete_ref
].deleted
= 1;
4442 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4449 /* Find the first entry in a depth-first traversal. */
4450 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4451 TransactedSnapshotImpl
* This
, DirRef parent
)
4453 DirRef cursor
, prev
;
4454 TransactedDirEntry
*entry
;
4457 entry
= &This
->entries
[cursor
];
4460 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4463 cursor
= entry
->data
.leftChild
;
4464 entry
= &This
->entries
[cursor
];
4465 entry
->parent
= prev
;
4467 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4470 cursor
= entry
->data
.rightChild
;
4471 entry
= &This
->entries
[cursor
];
4472 entry
->parent
= prev
;
4474 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4477 cursor
= entry
->data
.dirRootEntry
;
4478 entry
= &This
->entries
[cursor
];
4479 entry
->parent
= prev
;
4488 /* Find the next entry in a depth-first traversal. */
4489 static DirRef
TransactedSnapshotImpl_FindNextChild(
4490 TransactedSnapshotImpl
* This
, DirRef current
)
4493 TransactedDirEntry
*parent_entry
;
4495 parent
= This
->entries
[current
].parent
;
4496 parent_entry
= &This
->entries
[parent
];
4498 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4500 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4502 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4503 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4506 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4508 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4509 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4516 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4517 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4518 TransactedSnapshotImpl
* This
, DirRef entry
)
4520 return entry
!= DIRENTRY_NULL
&&
4521 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4524 /* Destroy the entries created by CopyTree. */
4525 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4526 TransactedSnapshotImpl
* This
, DirRef stop
)
4529 TransactedDirEntry
*entry
;
4530 ULARGE_INTEGER zero
;
4534 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4537 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4539 if (cursor
== DIRENTRY_NULL
)
4542 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4544 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4546 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4548 entry
= &This
->entries
[cursor
];
4550 if (entry
->stream_dirty
)
4551 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4552 entry
->newTransactedParentEntry
, zero
);
4554 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4555 entry
->newTransactedParentEntry
);
4557 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4560 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4564 /* Make a copy of our edited tree that we can use in the parent. */
4565 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4568 TransactedDirEntry
*entry
;
4571 cursor
= This
->base
.storageDirEntry
;
4572 entry
= &This
->entries
[cursor
];
4573 entry
->parent
= DIRENTRY_NULL
;
4574 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4576 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4579 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4581 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4582 entry
= &This
->entries
[cursor
];
4584 while (cursor
!= DIRENTRY_NULL
)
4586 /* Make a copy of this entry in the transacted parent. */
4588 (!entry
->dirty
&& !entry
->stream_dirty
&&
4589 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4590 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4591 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4592 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4597 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4599 newData
.size
.QuadPart
= 0;
4600 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4602 if (newData
.leftChild
!= DIRENTRY_NULL
)
4603 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4605 if (newData
.rightChild
!= DIRENTRY_NULL
)
4606 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4608 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4609 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4611 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4612 &entry
->newTransactedParentEntry
);
4615 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4619 if (entry
->stream_dirty
)
4621 hr
= StorageBaseImpl_CopyStream(
4622 This
->transactedParent
, entry
->newTransactedParentEntry
,
4623 This
->scratch
, entry
->stream_entry
);
4625 else if (entry
->data
.size
.QuadPart
)
4627 hr
= StorageBaseImpl_StreamLink(
4628 This
->transactedParent
, entry
->newTransactedParentEntry
,
4629 entry
->transactedParentEntry
);
4634 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4635 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4640 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4641 entry
= &This
->entries
[cursor
];
4647 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4649 DWORD grfCommitFlags
) /* [in] */
4651 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4652 TransactedDirEntry
*root_entry
;
4653 DirRef i
, dir_root_ref
;
4655 ULARGE_INTEGER zero
;
4660 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4662 /* Cannot commit a read-only transacted storage */
4663 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4664 return STG_E_ACCESSDENIED
;
4666 /* To prevent data loss, we create the new structure in the file before we
4667 * delete the old one, so that in case of errors the old data is intact. We
4668 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4669 * needed in the rare situation where we have just enough free disk space to
4670 * overwrite the existing data. */
4672 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4674 if (!root_entry
->read
)
4677 hr
= TransactedSnapshotImpl_CopyTree(This
);
4678 if (FAILED(hr
)) return hr
;
4680 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4681 dir_root_ref
= DIRENTRY_NULL
;
4683 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4685 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4687 /* Update the storage to use the new data in one step. */
4689 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4690 root_entry
->transactedParentEntry
, &data
);
4694 data
.dirRootEntry
= dir_root_ref
;
4695 data
.clsid
= root_entry
->data
.clsid
;
4696 data
.ctime
= root_entry
->data
.ctime
;
4697 data
.mtime
= root_entry
->data
.mtime
;
4699 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4700 root_entry
->transactedParentEntry
, &data
);
4703 /* Try to flush after updating the root storage, but if the flush fails, keep
4704 * going, on the theory that it'll either succeed later or the subsequent
4705 * writes will fail. */
4706 StorageBaseImpl_Flush(This
->transactedParent
);
4710 /* Destroy the old now-orphaned data. */
4711 for (i
=0; i
<This
->entries_size
; i
++)
4713 TransactedDirEntry
*entry
= &This
->entries
[i
];
4718 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4719 entry
->transactedParentEntry
, zero
);
4720 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4721 entry
->transactedParentEntry
);
4722 memset(entry
, 0, sizeof(TransactedDirEntry
));
4723 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4725 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4727 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4728 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4729 entry
->transactedParentEntry
);
4730 if (entry
->stream_dirty
)
4732 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4733 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4734 entry
->stream_dirty
= 0;
4737 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4744 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4748 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4753 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4756 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4757 ULARGE_INTEGER zero
;
4760 TRACE("(%p)\n", iface
);
4762 /* Destroy the open objects. */
4763 StorageBaseImpl_DeleteAll(&This
->base
);
4765 /* Clear out the scratch file. */
4767 for (i
=0; i
<This
->entries_size
; i
++)
4769 if (This
->entries
[i
].stream_dirty
)
4771 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4774 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4778 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4780 This
->firstFreeEntry
= 0;
4781 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4786 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4788 if (!This
->reverted
)
4790 TRACE("Storage invalidated (stg=%p)\n", This
);
4794 StorageBaseImpl_DeleteAll(This
);
4798 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4800 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4802 TransactedSnapshotImpl_Revert(&This
->base
.IStorage_iface
);
4804 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
4806 IStorage_Release(&This
->scratch
->IStorage_iface
);
4808 HeapFree(GetProcessHeap(), 0, This
->entries
);
4810 HeapFree(GetProcessHeap(), 0, This
);
4813 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4815 /* We only need to flush when committing. */
4819 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4821 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4823 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4826 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4827 const DirEntry
*newData
, DirRef
*index
)
4829 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4831 TransactedDirEntry
*new_entry
;
4833 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4834 if (new_ref
== DIRENTRY_NULL
)
4835 return E_OUTOFMEMORY
;
4837 new_entry
= &This
->entries
[new_ref
];
4839 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4840 new_entry
->read
= 1;
4841 new_entry
->dirty
= 1;
4842 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4846 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4851 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4852 DirRef index
, const DirEntry
*data
)
4854 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4857 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4859 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4860 if (FAILED(hr
)) return hr
;
4862 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4864 if (index
!= This
->base
.storageDirEntry
)
4866 This
->entries
[index
].dirty
= 1;
4868 if (data
->size
.QuadPart
== 0 &&
4869 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4871 /* Since this entry is modified, and we aren't using its stream data, we
4872 * no longer care about the original entry. */
4874 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4876 if (delete_ref
!= DIRENTRY_NULL
)
4877 This
->entries
[delete_ref
].deleted
= 1;
4879 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4886 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4887 DirRef index
, DirEntry
*data
)
4889 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4892 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4893 if (FAILED(hr
)) return hr
;
4895 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4897 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4902 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4905 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4907 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4908 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4910 /* If we deleted this entry while it has stream data. We must have left the
4911 * data because some other entry is using it, and we need to leave the
4912 * original entry alone. */
4913 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4914 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4918 This
->entries
[index
].deleted
= 1;
4924 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4925 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4927 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4929 if (This
->entries
[index
].stream_dirty
)
4931 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4932 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4934 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4936 /* This stream doesn't live in the parent, and we haven't allocated storage
4943 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4944 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4948 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4949 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4951 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4954 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4955 if (FAILED(hr
)) return hr
;
4957 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4958 if (FAILED(hr
)) return hr
;
4960 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4961 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4963 if (SUCCEEDED(hr
) && size
!= 0)
4964 This
->entries
[index
].data
.size
.QuadPart
= max(
4965 This
->entries
[index
].data
.size
.QuadPart
,
4966 offset
.QuadPart
+ size
);
4971 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4972 DirRef index
, ULARGE_INTEGER newsize
)
4974 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4977 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4978 if (FAILED(hr
)) return hr
;
4980 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4983 if (newsize
.QuadPart
== 0)
4985 /* Destroy any parent references or entries in the scratch file. */
4986 if (This
->entries
[index
].stream_dirty
)
4988 ULARGE_INTEGER zero
;
4990 StorageBaseImpl_StreamSetSize(This
->scratch
,
4991 This
->entries
[index
].stream_entry
, zero
);
4992 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4993 This
->entries
[index
].stream_entry
);
4994 This
->entries
[index
].stream_dirty
= 0;
4996 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4999 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5001 if (delete_ref
!= DIRENTRY_NULL
)
5002 This
->entries
[delete_ref
].deleted
= 1;
5004 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5009 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5010 if (FAILED(hr
)) return hr
;
5012 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5013 This
->entries
[index
].stream_entry
, newsize
);
5017 This
->entries
[index
].data
.size
= newsize
;
5022 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5023 DirRef dst
, DirRef src
)
5025 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5027 TransactedDirEntry
*dst_entry
, *src_entry
;
5029 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5030 if (FAILED(hr
)) return hr
;
5032 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5033 if (FAILED(hr
)) return hr
;
5035 dst_entry
= &This
->entries
[dst
];
5036 src_entry
= &This
->entries
[src
];
5038 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5039 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5040 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5041 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5042 dst_entry
->data
.size
= src_entry
->data
.size
;
5047 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5049 StorageBaseImpl_QueryInterface
,
5050 StorageBaseImpl_AddRef
,
5051 StorageBaseImpl_Release
,
5052 StorageBaseImpl_CreateStream
,
5053 StorageBaseImpl_OpenStream
,
5054 StorageBaseImpl_CreateStorage
,
5055 StorageBaseImpl_OpenStorage
,
5056 StorageBaseImpl_CopyTo
,
5057 StorageBaseImpl_MoveElementTo
,
5058 TransactedSnapshotImpl_Commit
,
5059 TransactedSnapshotImpl_Revert
,
5060 StorageBaseImpl_EnumElements
,
5061 StorageBaseImpl_DestroyElement
,
5062 StorageBaseImpl_RenameElement
,
5063 StorageBaseImpl_SetElementTimes
,
5064 StorageBaseImpl_SetClass
,
5065 StorageBaseImpl_SetStateBits
,
5066 StorageBaseImpl_Stat
5069 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5071 TransactedSnapshotImpl_Destroy
,
5072 TransactedSnapshotImpl_Invalidate
,
5073 TransactedSnapshotImpl_Flush
,
5074 TransactedSnapshotImpl_GetFilename
,
5075 TransactedSnapshotImpl_CreateDirEntry
,
5076 TransactedSnapshotImpl_WriteDirEntry
,
5077 TransactedSnapshotImpl_ReadDirEntry
,
5078 TransactedSnapshotImpl_DestroyDirEntry
,
5079 TransactedSnapshotImpl_StreamReadAt
,
5080 TransactedSnapshotImpl_StreamWriteAt
,
5081 TransactedSnapshotImpl_StreamSetSize
,
5082 TransactedSnapshotImpl_StreamLink
5085 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5086 TransactedSnapshotImpl
** result
)
5090 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5095 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5097 /* This is OK because the property set storage functions use the IStorage functions. */
5098 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
5100 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5102 list_init(&(*result
)->base
.strmHead
);
5104 list_init(&(*result
)->base
.storageHead
);
5106 (*result
)->base
.ref
= 1;
5108 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5110 /* Create a new temporary storage to act as the scratch file. */
5111 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5113 (*result
)->scratch
= impl_from_IStorage(scratch
);
5117 ULONG num_entries
= 20;
5119 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5120 (*result
)->entries_size
= num_entries
;
5121 (*result
)->firstFreeEntry
= 0;
5123 if ((*result
)->entries
)
5125 /* parentStorage already has 1 reference, which we take over here. */
5126 (*result
)->transactedParent
= parentStorage
;
5128 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5130 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5134 IStorage_Release(scratch
);
5140 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5145 return E_OUTOFMEMORY
;
5148 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5149 StorageBaseImpl
** result
)
5153 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5155 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5158 return TransactedSnapshotImpl_Construct(parentStorage
,
5159 (TransactedSnapshotImpl
**)result
);
5162 static HRESULT
Storage_Construct(
5170 StorageBaseImpl
** result
)
5172 StorageImpl
*newStorage
;
5173 StorageBaseImpl
*newTransactedStorage
;
5176 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5177 if (FAILED(hr
)) goto end
;
5179 if (openFlags
& STGM_TRANSACTED
)
5181 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5183 IStorage_Release(&newStorage
->base
.IStorage_iface
);
5185 *result
= newTransactedStorage
;
5188 *result
= &newStorage
->base
;
5194 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5196 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5198 if (!This
->base
.reverted
)
5200 TRACE("Storage invalidated (stg=%p)\n", This
);
5202 This
->base
.reverted
= 1;
5204 This
->parentStorage
= NULL
;
5206 StorageBaseImpl_DeleteAll(&This
->base
);
5208 list_remove(&This
->ParentListEntry
);
5212 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5214 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5216 StorageInternalImpl_Invalidate(&This
->base
);
5218 HeapFree(GetProcessHeap(), 0, This
);
5221 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5223 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5225 return StorageBaseImpl_Flush(This
->parentStorage
);
5228 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5230 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5232 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5235 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5236 const DirEntry
*newData
, DirRef
*index
)
5238 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5240 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5244 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5245 DirRef index
, const DirEntry
*data
)
5247 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5249 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5253 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5254 DirRef index
, DirEntry
*data
)
5256 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5258 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5262 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5265 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5267 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5271 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5272 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5274 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5276 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5277 index
, offset
, size
, buffer
, bytesRead
);
5280 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5281 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5283 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5285 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5286 index
, offset
, size
, buffer
, bytesWritten
);
5289 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5290 DirRef index
, ULARGE_INTEGER newsize
)
5292 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5294 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5298 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5299 DirRef dst
, DirRef src
)
5301 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5303 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5307 /******************************************************************************
5309 ** Storage32InternalImpl_Commit
5312 static HRESULT WINAPI
StorageInternalImpl_Commit(
5314 DWORD grfCommitFlags
) /* [in] */
5316 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5317 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5318 return StorageBaseImpl_Flush(This
);
5321 /******************************************************************************
5323 ** Storage32InternalImpl_Revert
5326 static HRESULT WINAPI
StorageInternalImpl_Revert(
5329 FIXME("(%p): stub\n", iface
);
5333 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5335 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
5336 HeapFree(GetProcessHeap(), 0, This
);
5339 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5340 IEnumSTATSTG
* iface
,
5344 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5347 return E_INVALIDARG
;
5351 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5352 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5355 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5359 return E_NOINTERFACE
;
5362 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5363 IEnumSTATSTG
* iface
)
5365 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5366 return InterlockedIncrement(&This
->ref
);
5369 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5370 IEnumSTATSTG
* iface
)
5372 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5376 newRef
= InterlockedDecrement(&This
->ref
);
5380 IEnumSTATSTGImpl_Destroy(This
);
5386 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5387 IEnumSTATSTGImpl
* This
,
5390 DirRef result
= DIRENTRY_NULL
;
5394 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5396 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5397 This
->parentStorage
->storageDirEntry
, &entry
);
5398 searchNode
= entry
.dirRootEntry
;
5400 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5402 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5406 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5410 searchNode
= entry
.rightChild
;
5414 result
= searchNode
;
5415 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5416 searchNode
= entry
.leftChild
;
5424 if (result
!= DIRENTRY_NULL
)
5425 memcpy(This
->name
, result_name
, sizeof(result_name
));
5431 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5432 IEnumSTATSTG
* iface
,
5435 ULONG
* pceltFetched
)
5437 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5439 DirEntry currentEntry
;
5440 STATSTG
* currentReturnStruct
= rgelt
;
5441 ULONG objectFetched
= 0;
5442 DirRef currentSearchNode
;
5445 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5446 return E_INVALIDARG
;
5448 if (This
->parentStorage
->reverted
)
5449 return STG_E_REVERTED
;
5452 * To avoid the special case, get another pointer to a ULONG value if
5453 * the caller didn't supply one.
5455 if (pceltFetched
==0)
5456 pceltFetched
= &objectFetched
;
5459 * Start the iteration, we will iterate until we hit the end of the
5460 * linked list or until we hit the number of items to iterate through
5464 while ( *pceltFetched
< celt
)
5466 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5468 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5472 * Read the entry from the storage.
5474 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5479 * Copy the information to the return buffer.
5481 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5482 currentReturnStruct
,
5487 * Step to the next item in the iteration
5490 currentReturnStruct
++;
5493 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5500 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5501 IEnumSTATSTG
* iface
,
5504 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5506 ULONG objectFetched
= 0;
5507 DirRef currentSearchNode
;
5510 if (This
->parentStorage
->reverted
)
5511 return STG_E_REVERTED
;
5513 while ( (objectFetched
< celt
) )
5515 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5517 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5523 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5529 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5530 IEnumSTATSTG
* iface
)
5532 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5534 if (This
->parentStorage
->reverted
)
5535 return STG_E_REVERTED
;
5542 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5543 IEnumSTATSTG
* iface
,
5544 IEnumSTATSTG
** ppenum
)
5546 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5548 IEnumSTATSTGImpl
* newClone
;
5550 if (This
->parentStorage
->reverted
)
5551 return STG_E_REVERTED
;
5554 * Perform a sanity check on the parameters.
5557 return E_INVALIDARG
;
5559 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5560 This
->storageDirEntry
);
5564 * The new clone enumeration must point to the same current node as
5567 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5569 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5572 * Don't forget to nail down a reference to the clone before
5575 IEnumSTATSTGImpl_AddRef(*ppenum
);
5581 * Virtual function table for the IEnumSTATSTGImpl class.
5583 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5585 IEnumSTATSTGImpl_QueryInterface
,
5586 IEnumSTATSTGImpl_AddRef
,
5587 IEnumSTATSTGImpl_Release
,
5588 IEnumSTATSTGImpl_Next
,
5589 IEnumSTATSTGImpl_Skip
,
5590 IEnumSTATSTGImpl_Reset
,
5591 IEnumSTATSTGImpl_Clone
5594 /******************************************************************************
5595 ** IEnumSTATSTGImpl implementation
5598 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5599 StorageBaseImpl
* parentStorage
,
5600 DirRef storageDirEntry
)
5602 IEnumSTATSTGImpl
* newEnumeration
;
5604 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5606 if (newEnumeration
!=0)
5609 * Set-up the virtual function table and reference count.
5611 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5612 newEnumeration
->ref
= 0;
5615 * We want to nail-down the reference to the storage in case the
5616 * enumeration out-lives the storage in the client application.
5618 newEnumeration
->parentStorage
= parentStorage
;
5619 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
5621 newEnumeration
->storageDirEntry
= storageDirEntry
;
5624 * Make sure the current node of the iterator is the first one.
5626 IEnumSTATSTGImpl_Reset(&newEnumeration
->IEnumSTATSTG_iface
);
5629 return newEnumeration
;
5633 * Virtual function table for the Storage32InternalImpl class.
5635 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5637 StorageBaseImpl_QueryInterface
,
5638 StorageBaseImpl_AddRef
,
5639 StorageBaseImpl_Release
,
5640 StorageBaseImpl_CreateStream
,
5641 StorageBaseImpl_OpenStream
,
5642 StorageBaseImpl_CreateStorage
,
5643 StorageBaseImpl_OpenStorage
,
5644 StorageBaseImpl_CopyTo
,
5645 StorageBaseImpl_MoveElementTo
,
5646 StorageInternalImpl_Commit
,
5647 StorageInternalImpl_Revert
,
5648 StorageBaseImpl_EnumElements
,
5649 StorageBaseImpl_DestroyElement
,
5650 StorageBaseImpl_RenameElement
,
5651 StorageBaseImpl_SetElementTimes
,
5652 StorageBaseImpl_SetClass
,
5653 StorageBaseImpl_SetStateBits
,
5654 StorageBaseImpl_Stat
5657 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5659 StorageInternalImpl_Destroy
,
5660 StorageInternalImpl_Invalidate
,
5661 StorageInternalImpl_Flush
,
5662 StorageInternalImpl_GetFilename
,
5663 StorageInternalImpl_CreateDirEntry
,
5664 StorageInternalImpl_WriteDirEntry
,
5665 StorageInternalImpl_ReadDirEntry
,
5666 StorageInternalImpl_DestroyDirEntry
,
5667 StorageInternalImpl_StreamReadAt
,
5668 StorageInternalImpl_StreamWriteAt
,
5669 StorageInternalImpl_StreamSetSize
,
5670 StorageInternalImpl_StreamLink
5673 /******************************************************************************
5674 ** Storage32InternalImpl implementation
5677 static StorageInternalImpl
* StorageInternalImpl_Construct(
5678 StorageBaseImpl
* parentStorage
,
5680 DirRef storageDirEntry
)
5682 StorageInternalImpl
* newStorage
;
5684 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5688 list_init(&newStorage
->base
.strmHead
);
5690 list_init(&newStorage
->base
.storageHead
);
5693 * Initialize the virtual function table.
5695 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5696 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5697 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5698 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5700 newStorage
->base
.reverted
= 0;
5702 newStorage
->base
.ref
= 1;
5704 newStorage
->parentStorage
= parentStorage
;
5707 * Keep a reference to the directory entry of this storage
5709 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5711 newStorage
->base
.create
= 0;
5719 /******************************************************************************
5720 ** StorageUtl implementation
5723 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5727 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5728 *value
= lendian16toh(tmp
);
5731 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5733 value
= htole16(value
);
5734 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5737 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5741 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5742 *value
= lendian32toh(tmp
);
5745 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5747 value
= htole32(value
);
5748 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5751 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5752 ULARGE_INTEGER
* value
)
5754 #ifdef WORDS_BIGENDIAN
5757 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5758 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5759 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5761 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5765 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5766 const ULARGE_INTEGER
*value
)
5768 #ifdef WORDS_BIGENDIAN
5771 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5772 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5773 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5775 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5779 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5781 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5782 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5783 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5785 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5788 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5790 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5791 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5792 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5794 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5797 void StorageUtl_CopyDirEntryToSTATSTG(
5798 StorageBaseImpl
* storage
,
5799 STATSTG
* destination
,
5800 const DirEntry
* source
,
5804 * The copy of the string occurs only when the flag is not set
5806 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5808 /* Use the filename for the root storage. */
5809 destination
->pwcsName
= 0;
5810 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5812 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5813 (source
->name
[0] == 0) )
5815 destination
->pwcsName
= 0;
5819 destination
->pwcsName
=
5820 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5822 strcpyW(destination
->pwcsName
, source
->name
);
5825 switch (source
->stgType
)
5829 destination
->type
= STGTY_STORAGE
;
5832 destination
->type
= STGTY_STREAM
;
5835 destination
->type
= STGTY_STREAM
;
5839 destination
->cbSize
= source
->size
;
5841 currentReturnStruct->mtime = {0}; TODO
5842 currentReturnStruct->ctime = {0};
5843 currentReturnStruct->atime = {0};
5845 destination
->grfMode
= 0;
5846 destination
->grfLocksSupported
= 0;
5847 destination
->clsid
= source
->clsid
;
5848 destination
->grfStateBits
= 0;
5849 destination
->reserved
= 0;
5852 /******************************************************************************
5853 ** BlockChainStream implementation
5856 /* Read and save the index of all blocks in this stream. */
5857 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5859 ULONG next_sector
, next_offset
;
5861 struct BlockChainRun
*last_run
;
5863 if (This
->indexCacheLen
== 0)
5867 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5871 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5872 next_offset
= last_run
->lastOffset
+1;
5873 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5874 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5876 if (FAILED(hr
)) return hr
;
5879 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5881 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5883 /* Add the current block to the cache. */
5884 if (This
->indexCacheSize
== 0)
5886 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5887 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5888 This
->indexCacheSize
= 16;
5890 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5892 struct BlockChainRun
*new_cache
;
5895 new_size
= This
->indexCacheSize
* 2;
5896 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5897 if (!new_cache
) return E_OUTOFMEMORY
;
5898 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5900 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5901 This
->indexCache
= new_cache
;
5902 This
->indexCacheSize
= new_size
;
5905 This
->indexCacheLen
++;
5906 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5907 last_run
->firstSector
= next_sector
;
5908 last_run
->firstOffset
= next_offset
;
5911 last_run
->lastOffset
= next_offset
;
5913 /* Find the next block. */
5915 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5916 if (FAILED(hr
)) return hr
;
5919 if (This
->indexCacheLen
)
5921 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5922 This
->numBlocks
= last_run
->lastOffset
+1;
5926 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5927 This
->numBlocks
= 0;
5933 /* Locate the nth block in this stream. */
5934 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5936 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5937 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5939 if (offset
>= This
->numBlocks
)
5940 return BLOCK_END_OF_CHAIN
;
5942 while (min_run
< max_run
)
5944 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5945 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5947 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5948 max_run
= run_to_check
-1;
5950 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5952 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5953 min_run
= run_to_check
+1;
5956 /* Block is in this run. */
5957 min_run
= max_run
= run_to_check
;
5960 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5963 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5964 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5966 BlockChainBlock
*result
=NULL
;
5970 if (This
->cachedBlocks
[i
].index
== index
)
5972 *sector
= This
->cachedBlocks
[i
].sector
;
5973 *block
= &This
->cachedBlocks
[i
];
5977 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5978 if (*sector
== BLOCK_END_OF_CHAIN
)
5979 return STG_E_DOCFILECORRUPT
;
5983 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5984 result
= &This
->cachedBlocks
[0];
5985 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5986 result
= &This
->cachedBlocks
[1];
5989 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5990 if (This
->blockToEvict
== 2)
5991 This
->blockToEvict
= 0;
5996 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5997 return STG_E_WRITEFAULT
;
6002 result
->index
= index
;
6003 result
->sector
= *sector
;
6010 BlockChainStream
* BlockChainStream_Construct(
6011 StorageImpl
* parentStorage
,
6012 ULONG
* headOfStreamPlaceHolder
,
6015 BlockChainStream
* newStream
;
6017 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6019 newStream
->parentStorage
= parentStorage
;
6020 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6021 newStream
->ownerDirEntry
= dirEntry
;
6022 newStream
->indexCache
= NULL
;
6023 newStream
->indexCacheLen
= 0;
6024 newStream
->indexCacheSize
= 0;
6025 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6026 newStream
->cachedBlocks
[0].dirty
= 0;
6027 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6028 newStream
->cachedBlocks
[1].dirty
= 0;
6029 newStream
->blockToEvict
= 0;
6031 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6033 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6034 HeapFree(GetProcessHeap(), 0, newStream
);
6041 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6044 if (!This
) return S_OK
;
6047 if (This
->cachedBlocks
[i
].dirty
)
6049 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6050 This
->cachedBlocks
[i
].dirty
= 0;
6052 return STG_E_WRITEFAULT
;
6058 void BlockChainStream_Destroy(BlockChainStream
* This
)
6062 BlockChainStream_Flush(This
);
6063 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6065 HeapFree(GetProcessHeap(), 0, This
);
6068 /******************************************************************************
6069 * BlockChainStream_GetHeadOfChain
6071 * Returns the head of this stream chain.
6072 * Some special chains don't have directory entries, their heads are kept in
6073 * This->headOfStreamPlaceHolder.
6076 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6078 DirEntry chainEntry
;
6081 if (This
->headOfStreamPlaceHolder
!= 0)
6082 return *(This
->headOfStreamPlaceHolder
);
6084 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6086 hr
= StorageImpl_ReadDirEntry(
6087 This
->parentStorage
,
6088 This
->ownerDirEntry
,
6093 return chainEntry
.startingBlock
;
6097 return BLOCK_END_OF_CHAIN
;
6100 /******************************************************************************
6101 * BlockChainStream_GetCount
6103 * Returns the number of blocks that comprises this chain.
6104 * This is not the size of the stream as the last block may not be full!
6106 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6108 return This
->numBlocks
;
6111 /******************************************************************************
6112 * BlockChainStream_ReadAt
6114 * Reads a specified number of bytes from this chain at the specified offset.
6115 * bytesRead may be NULL.
6116 * Failure will be returned if the specified number of bytes has not been read.
6118 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6119 ULARGE_INTEGER offset
,
6124 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6125 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6126 ULONG bytesToReadInBuffer
;
6129 ULARGE_INTEGER stream_size
;
6131 BlockChainBlock
*cachedBlock
;
6133 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6136 * Find the first block in the stream that contains part of the buffer.
6138 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6142 stream_size
= BlockChainStream_GetSize(This
);
6143 if (stream_size
.QuadPart
> offset
.QuadPart
)
6144 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6149 * Start reading the buffer.
6151 bufferWalker
= buffer
;
6155 ULARGE_INTEGER ulOffset
;
6159 * Calculate how many bytes we can copy from this big block.
6161 bytesToReadInBuffer
=
6162 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6164 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6171 /* Not in cache, and we're going to read past the end of the block. */
6172 ulOffset
.u
.HighPart
= 0;
6173 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6176 StorageImpl_ReadAt(This
->parentStorage
,
6179 bytesToReadInBuffer
,
6184 if (!cachedBlock
->read
)
6186 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6187 return STG_E_READFAULT
;
6189 cachedBlock
->read
= 1;
6192 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6193 bytesReadAt
= bytesToReadInBuffer
;
6196 blockNoInSequence
++;
6197 bufferWalker
+= bytesReadAt
;
6198 size
-= bytesReadAt
;
6199 *bytesRead
+= bytesReadAt
;
6200 offsetInBlock
= 0; /* There is no offset on the next block */
6202 if (bytesToReadInBuffer
!= bytesReadAt
)
6209 /******************************************************************************
6210 * BlockChainStream_WriteAt
6212 * Writes the specified number of bytes to this chain at the specified offset.
6213 * Will fail if not all specified number of bytes have been written.
6215 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6216 ULARGE_INTEGER offset
,
6219 ULONG
* bytesWritten
)
6221 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6222 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6225 const BYTE
* bufferWalker
;
6227 BlockChainBlock
*cachedBlock
;
6230 bufferWalker
= buffer
;
6234 ULARGE_INTEGER ulOffset
;
6235 DWORD bytesWrittenAt
;
6238 * Calculate how many bytes we can copy to this big block.
6241 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6243 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6245 /* BlockChainStream_SetSize should have already been called to ensure we have
6246 * enough blocks in the chain to write into */
6249 ERR("not enough blocks in chain to write data\n");
6255 /* Not in cache, and we're going to write past the end of the block. */
6256 ulOffset
.u
.HighPart
= 0;
6257 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6260 StorageImpl_WriteAt(This
->parentStorage
,
6268 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6270 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6271 return STG_E_READFAULT
;
6274 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6275 bytesWrittenAt
= bytesToWrite
;
6276 cachedBlock
->read
= 1;
6277 cachedBlock
->dirty
= 1;
6280 blockNoInSequence
++;
6281 bufferWalker
+= bytesWrittenAt
;
6282 size
-= bytesWrittenAt
;
6283 *bytesWritten
+= bytesWrittenAt
;
6284 offsetInBlock
= 0; /* There is no offset on the next block */
6286 if (bytesWrittenAt
!= bytesToWrite
)
6290 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6293 /******************************************************************************
6294 * BlockChainStream_Shrink
6296 * Shrinks this chain in the big block depot.
6298 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6299 ULARGE_INTEGER newSize
)
6306 * Figure out how many blocks are needed to contain the new size
6308 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6310 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6316 * Go to the new end of chain
6318 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6320 /* Mark the new end of chain */
6321 StorageImpl_SetNextBlockInChain(
6322 This
->parentStorage
,
6324 BLOCK_END_OF_CHAIN
);
6326 This
->tailIndex
= blockIndex
;
6330 if (This
->headOfStreamPlaceHolder
!= 0)
6332 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6336 DirEntry chainEntry
;
6337 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6339 StorageImpl_ReadDirEntry(
6340 This
->parentStorage
,
6341 This
->ownerDirEntry
,
6344 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6346 StorageImpl_WriteDirEntry(
6347 This
->parentStorage
,
6348 This
->ownerDirEntry
,
6352 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6355 This
->numBlocks
= numBlocks
;
6358 * Mark the extra blocks as free
6360 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6362 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6363 StorageImpl_FreeBigBlock(This
->parentStorage
,
6364 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6365 if (last_run
->lastOffset
== last_run
->firstOffset
)
6366 This
->indexCacheLen
--;
6368 last_run
->lastOffset
--;
6372 * Reset the last accessed block cache.
6376 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6378 This
->cachedBlocks
[i
].index
= 0xffffffff;
6379 This
->cachedBlocks
[i
].dirty
= 0;
6386 /******************************************************************************
6387 * BlockChainStream_Enlarge
6389 * Grows this chain in the big block depot.
6391 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6392 ULARGE_INTEGER newSize
)
6394 ULONG blockIndex
, currentBlock
;
6396 ULONG oldNumBlocks
= 0;
6398 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6401 * Empty chain. Create the head.
6403 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6405 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6406 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6408 BLOCK_END_OF_CHAIN
);
6410 if (This
->headOfStreamPlaceHolder
!= 0)
6412 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6416 DirEntry chainEntry
;
6417 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6419 StorageImpl_ReadDirEntry(
6420 This
->parentStorage
,
6421 This
->ownerDirEntry
,
6424 chainEntry
.startingBlock
= blockIndex
;
6426 StorageImpl_WriteDirEntry(
6427 This
->parentStorage
,
6428 This
->ownerDirEntry
,
6432 This
->tailIndex
= blockIndex
;
6433 This
->numBlocks
= 1;
6437 * Figure out how many blocks are needed to contain this stream
6439 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6441 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6445 * Go to the current end of chain
6447 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6449 currentBlock
= blockIndex
;
6451 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6454 currentBlock
= blockIndex
;
6456 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6461 This
->tailIndex
= currentBlock
;
6464 currentBlock
= This
->tailIndex
;
6465 oldNumBlocks
= This
->numBlocks
;
6468 * Add new blocks to the chain
6470 if (oldNumBlocks
< newNumBlocks
)
6472 while (oldNumBlocks
< newNumBlocks
)
6474 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6476 StorageImpl_SetNextBlockInChain(
6477 This
->parentStorage
,
6481 StorageImpl_SetNextBlockInChain(
6482 This
->parentStorage
,
6484 BLOCK_END_OF_CHAIN
);
6486 currentBlock
= blockIndex
;
6490 This
->tailIndex
= blockIndex
;
6491 This
->numBlocks
= newNumBlocks
;
6494 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6500 /******************************************************************************
6501 * BlockChainStream_SetSize
6503 * Sets the size of this stream. The big block depot will be updated.
6504 * The file will grow if we grow the chain.
6506 * TODO: Free the actual blocks in the file when we shrink the chain.
6507 * Currently, the blocks are still in the file. So the file size
6508 * doesn't shrink even if we shrink streams.
6510 BOOL
BlockChainStream_SetSize(
6511 BlockChainStream
* This
,
6512 ULARGE_INTEGER newSize
)
6514 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6516 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6519 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6521 BlockChainStream_Shrink(This
, newSize
);
6525 BlockChainStream_Enlarge(This
, newSize
);
6531 /******************************************************************************
6532 * BlockChainStream_GetSize
6534 * Returns the size of this chain.
6535 * Will return the block count if this chain doesn't have a directory entry.
6537 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6539 DirEntry chainEntry
;
6541 if(This
->headOfStreamPlaceHolder
== NULL
)
6544 * This chain has a directory entry so use the size value from there.
6546 StorageImpl_ReadDirEntry(
6547 This
->parentStorage
,
6548 This
->ownerDirEntry
,
6551 return chainEntry
.size
;
6556 * this chain is a chain that does not have a directory entry, figure out the
6557 * size by making the product number of used blocks times the
6560 ULARGE_INTEGER result
;
6561 result
.u
.HighPart
= 0;
6564 BlockChainStream_GetCount(This
) *
6565 This
->parentStorage
->bigBlockSize
;
6571 /******************************************************************************
6572 ** SmallBlockChainStream implementation
6575 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6576 StorageImpl
* parentStorage
,
6577 ULONG
* headOfStreamPlaceHolder
,
6580 SmallBlockChainStream
* newStream
;
6582 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6584 newStream
->parentStorage
= parentStorage
;
6585 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6586 newStream
->ownerDirEntry
= dirEntry
;
6591 void SmallBlockChainStream_Destroy(
6592 SmallBlockChainStream
* This
)
6594 HeapFree(GetProcessHeap(), 0, This
);
6597 /******************************************************************************
6598 * SmallBlockChainStream_GetHeadOfChain
6600 * Returns the head of this chain of small blocks.
6602 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6603 SmallBlockChainStream
* This
)
6605 DirEntry chainEntry
;
6608 if (This
->headOfStreamPlaceHolder
!= NULL
)
6609 return *(This
->headOfStreamPlaceHolder
);
6611 if (This
->ownerDirEntry
)
6613 hr
= StorageImpl_ReadDirEntry(
6614 This
->parentStorage
,
6615 This
->ownerDirEntry
,
6620 return chainEntry
.startingBlock
;
6625 return BLOCK_END_OF_CHAIN
;
6628 /******************************************************************************
6629 * SmallBlockChainStream_GetNextBlockInChain
6631 * Returns the index of the next small block in this chain.
6634 * - BLOCK_END_OF_CHAIN: end of this chain
6635 * - BLOCK_UNUSED: small block 'blockIndex' is free
6637 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6638 SmallBlockChainStream
* This
,
6640 ULONG
* nextBlockInChain
)
6642 ULARGE_INTEGER offsetOfBlockInDepot
;
6647 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6649 offsetOfBlockInDepot
.u
.HighPart
= 0;
6650 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6653 * Read those bytes in the buffer from the small block file.
6655 res
= BlockChainStream_ReadAt(
6656 This
->parentStorage
->smallBlockDepotChain
,
6657 offsetOfBlockInDepot
,
6662 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6663 res
= STG_E_READFAULT
;
6667 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6674 /******************************************************************************
6675 * SmallBlockChainStream_SetNextBlockInChain
6677 * Writes the index of the next block of the specified block in the small
6679 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6680 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6682 static void SmallBlockChainStream_SetNextBlockInChain(
6683 SmallBlockChainStream
* This
,
6687 ULARGE_INTEGER offsetOfBlockInDepot
;
6691 offsetOfBlockInDepot
.u
.HighPart
= 0;
6692 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6694 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6697 * Read those bytes in the buffer from the small block file.
6699 BlockChainStream_WriteAt(
6700 This
->parentStorage
->smallBlockDepotChain
,
6701 offsetOfBlockInDepot
,
6707 /******************************************************************************
6708 * SmallBlockChainStream_FreeBlock
6710 * Flag small block 'blockIndex' as free in the small block depot.
6712 static void SmallBlockChainStream_FreeBlock(
6713 SmallBlockChainStream
* This
,
6716 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6719 /******************************************************************************
6720 * SmallBlockChainStream_GetNextFreeBlock
6722 * Returns the index of a free small block. The small block depot will be
6723 * enlarged if necessary. The small block chain will also be enlarged if
6726 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6727 SmallBlockChainStream
* This
)
6729 ULARGE_INTEGER offsetOfBlockInDepot
;
6732 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6733 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6735 ULONG smallBlocksPerBigBlock
;
6737 ULONG blocksRequired
;
6738 ULARGE_INTEGER old_size
, size_required
;
6740 offsetOfBlockInDepot
.u
.HighPart
= 0;
6743 * Scan the small block depot for a free block
6745 while (nextBlockIndex
!= BLOCK_UNUSED
)
6747 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6749 res
= BlockChainStream_ReadAt(
6750 This
->parentStorage
->smallBlockDepotChain
,
6751 offsetOfBlockInDepot
,
6757 * If we run out of space for the small block depot, enlarge it
6759 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6761 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6763 if (nextBlockIndex
!= BLOCK_UNUSED
)
6769 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6771 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6772 ULARGE_INTEGER newSize
, offset
;
6775 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6776 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6779 * Initialize all the small blocks to free
6781 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6782 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6783 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6784 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6786 StorageImpl_SaveFileHeader(This
->parentStorage
);
6790 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6792 smallBlocksPerBigBlock
=
6793 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6796 * Verify if we have to allocate big blocks to contain small blocks
6798 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6800 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6802 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6804 if (size_required
.QuadPart
> old_size
.QuadPart
)
6806 BlockChainStream_SetSize(
6807 This
->parentStorage
->smallBlockRootChain
,
6810 StorageImpl_ReadDirEntry(
6811 This
->parentStorage
,
6812 This
->parentStorage
->base
.storageDirEntry
,
6815 rootEntry
.size
= size_required
;
6817 StorageImpl_WriteDirEntry(
6818 This
->parentStorage
,
6819 This
->parentStorage
->base
.storageDirEntry
,
6826 /******************************************************************************
6827 * SmallBlockChainStream_ReadAt
6829 * Reads a specified number of bytes from this chain at the specified offset.
6830 * bytesRead may be NULL.
6831 * Failure will be returned if the specified number of bytes has not been read.
6833 HRESULT
SmallBlockChainStream_ReadAt(
6834 SmallBlockChainStream
* This
,
6835 ULARGE_INTEGER offset
,
6841 ULARGE_INTEGER offsetInBigBlockFile
;
6842 ULONG blockNoInSequence
=
6843 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6845 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6846 ULONG bytesToReadInBuffer
;
6848 ULONG bytesReadFromBigBlockFile
;
6850 ULARGE_INTEGER stream_size
;
6853 * This should never happen on a small block file.
6855 assert(offset
.u
.HighPart
==0);
6859 stream_size
= SmallBlockChainStream_GetSize(This
);
6860 if (stream_size
.QuadPart
> offset
.QuadPart
)
6861 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6866 * Find the first block in the stream that contains part of the buffer.
6868 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6870 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6872 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6875 blockNoInSequence
--;
6879 * Start reading the buffer.
6881 bufferWalker
= buffer
;
6883 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6886 * Calculate how many bytes we can copy from this small block.
6888 bytesToReadInBuffer
=
6889 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6892 * Calculate the offset of the small block in the small block file.
6894 offsetInBigBlockFile
.u
.HighPart
= 0;
6895 offsetInBigBlockFile
.u
.LowPart
=
6896 blockIndex
* This
->parentStorage
->smallBlockSize
;
6898 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6901 * Read those bytes in the buffer from the small block file.
6902 * The small block has already been identified so it shouldn't fail
6903 * unless the file is corrupt.
6905 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6906 offsetInBigBlockFile
,
6907 bytesToReadInBuffer
,
6909 &bytesReadFromBigBlockFile
);
6914 if (!bytesReadFromBigBlockFile
)
6915 return STG_E_DOCFILECORRUPT
;
6918 * Step to the next big block.
6920 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6922 return STG_E_DOCFILECORRUPT
;
6924 bufferWalker
+= bytesReadFromBigBlockFile
;
6925 size
-= bytesReadFromBigBlockFile
;
6926 *bytesRead
+= bytesReadFromBigBlockFile
;
6927 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6933 /******************************************************************************
6934 * SmallBlockChainStream_WriteAt
6936 * Writes the specified number of bytes to this chain at the specified offset.
6937 * Will fail if not all specified number of bytes have been written.
6939 HRESULT
SmallBlockChainStream_WriteAt(
6940 SmallBlockChainStream
* This
,
6941 ULARGE_INTEGER offset
,
6944 ULONG
* bytesWritten
)
6946 ULARGE_INTEGER offsetInBigBlockFile
;
6947 ULONG blockNoInSequence
=
6948 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6950 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6951 ULONG bytesToWriteInBuffer
;
6953 ULONG bytesWrittenToBigBlockFile
;
6954 const BYTE
* bufferWalker
;
6958 * This should never happen on a small block file.
6960 assert(offset
.u
.HighPart
==0);
6963 * Find the first block in the stream that contains part of the buffer.
6965 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6967 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6969 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6970 return STG_E_DOCFILECORRUPT
;
6971 blockNoInSequence
--;
6975 * Start writing the buffer.
6978 bufferWalker
= buffer
;
6979 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6982 * Calculate how many bytes we can copy to this small block.
6984 bytesToWriteInBuffer
=
6985 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6988 * Calculate the offset of the small block in the small block file.
6990 offsetInBigBlockFile
.u
.HighPart
= 0;
6991 offsetInBigBlockFile
.u
.LowPart
=
6992 blockIndex
* This
->parentStorage
->smallBlockSize
;
6994 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6997 * Write those bytes in the buffer to the small block file.
6999 res
= BlockChainStream_WriteAt(
7000 This
->parentStorage
->smallBlockRootChain
,
7001 offsetInBigBlockFile
,
7002 bytesToWriteInBuffer
,
7004 &bytesWrittenToBigBlockFile
);
7009 * Step to the next big block.
7011 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7014 bufferWalker
+= bytesWrittenToBigBlockFile
;
7015 size
-= bytesWrittenToBigBlockFile
;
7016 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7017 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7020 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7023 /******************************************************************************
7024 * SmallBlockChainStream_Shrink
7026 * Shrinks this chain in the small block depot.
7028 static BOOL
SmallBlockChainStream_Shrink(
7029 SmallBlockChainStream
* This
,
7030 ULARGE_INTEGER newSize
)
7032 ULONG blockIndex
, extraBlock
;
7036 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7038 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7041 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7044 * Go to the new end of chain
7046 while (count
< numBlocks
)
7048 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7055 * If the count is 0, we have a special case, the head of the chain was
7060 DirEntry chainEntry
;
7062 StorageImpl_ReadDirEntry(This
->parentStorage
,
7063 This
->ownerDirEntry
,
7066 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7068 StorageImpl_WriteDirEntry(This
->parentStorage
,
7069 This
->ownerDirEntry
,
7073 * We start freeing the chain at the head block.
7075 extraBlock
= blockIndex
;
7079 /* Get the next block before marking the new end */
7080 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7084 /* Mark the new end of chain */
7085 SmallBlockChainStream_SetNextBlockInChain(
7088 BLOCK_END_OF_CHAIN
);
7092 * Mark the extra blocks as free
7094 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7096 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7099 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7100 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7101 extraBlock
= blockIndex
;
7107 /******************************************************************************
7108 * SmallBlockChainStream_Enlarge
7110 * Grows this chain in the small block depot.
7112 static BOOL
SmallBlockChainStream_Enlarge(
7113 SmallBlockChainStream
* This
,
7114 ULARGE_INTEGER newSize
)
7116 ULONG blockIndex
, currentBlock
;
7118 ULONG oldNumBlocks
= 0;
7120 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7123 * Empty chain. Create the head.
7125 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7127 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7128 SmallBlockChainStream_SetNextBlockInChain(
7131 BLOCK_END_OF_CHAIN
);
7133 if (This
->headOfStreamPlaceHolder
!= NULL
)
7135 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7139 DirEntry chainEntry
;
7141 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7144 chainEntry
.startingBlock
= blockIndex
;
7146 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7151 currentBlock
= blockIndex
;
7154 * Figure out how many blocks are needed to contain this stream
7156 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7158 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7162 * Go to the current end of chain
7164 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7167 currentBlock
= blockIndex
;
7168 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7173 * Add new blocks to the chain
7175 while (oldNumBlocks
< newNumBlocks
)
7177 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7178 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7180 SmallBlockChainStream_SetNextBlockInChain(
7183 BLOCK_END_OF_CHAIN
);
7185 currentBlock
= blockIndex
;
7192 /******************************************************************************
7193 * SmallBlockChainStream_SetSize
7195 * Sets the size of this stream.
7196 * The file will grow if we grow the chain.
7198 * TODO: Free the actual blocks in the file when we shrink the chain.
7199 * Currently, the blocks are still in the file. So the file size
7200 * doesn't shrink even if we shrink streams.
7202 BOOL
SmallBlockChainStream_SetSize(
7203 SmallBlockChainStream
* This
,
7204 ULARGE_INTEGER newSize
)
7206 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7208 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7211 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7213 SmallBlockChainStream_Shrink(This
, newSize
);
7217 SmallBlockChainStream_Enlarge(This
, newSize
);
7223 /******************************************************************************
7224 * SmallBlockChainStream_GetCount
7226 * Returns the number of small blocks that comprises this chain.
7227 * This is not the size of the stream as the last block may not be full!
7230 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7235 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7237 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7241 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7242 blockIndex
, &blockIndex
)))
7249 /******************************************************************************
7250 * SmallBlockChainStream_GetSize
7252 * Returns the size of this chain.
7254 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7256 DirEntry chainEntry
;
7258 if(This
->headOfStreamPlaceHolder
!= NULL
)
7260 ULARGE_INTEGER result
;
7261 result
.u
.HighPart
= 0;
7263 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7264 This
->parentStorage
->smallBlockSize
;
7269 StorageImpl_ReadDirEntry(
7270 This
->parentStorage
,
7271 This
->ownerDirEntry
,
7274 return chainEntry
.size
;
7277 static HRESULT
create_storagefile(
7281 STGOPTIONS
* pStgOptions
,
7285 StorageBaseImpl
* newStorage
= 0;
7286 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7287 HRESULT hr
= STG_E_INVALIDFLAG
;
7291 DWORD fileAttributes
;
7292 WCHAR tempFileName
[MAX_PATH
];
7295 return STG_E_INVALIDPOINTER
;
7297 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7298 return STG_E_INVALIDPARAMETER
;
7300 /* if no share mode given then DENY_NONE is the default */
7301 if (STGM_SHARE_MODE(grfMode
) == 0)
7302 grfMode
|= STGM_SHARE_DENY_NONE
;
7304 if ( FAILED( validateSTGM(grfMode
) ))
7307 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7308 switch(STGM_ACCESS_MODE(grfMode
))
7311 case STGM_READWRITE
:
7317 /* in direct mode, can only use SHARE_EXCLUSIVE */
7318 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7321 /* but in transacted mode, any share mode is valid */
7324 * Generate a unique name.
7328 WCHAR tempPath
[MAX_PATH
];
7329 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7331 memset(tempPath
, 0, sizeof(tempPath
));
7332 memset(tempFileName
, 0, sizeof(tempFileName
));
7334 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7337 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7338 pwcsName
= tempFileName
;
7341 hr
= STG_E_INSUFFICIENTMEMORY
;
7345 creationMode
= TRUNCATE_EXISTING
;
7349 creationMode
= GetCreationModeFromSTGM(grfMode
);
7353 * Interpret the STGM value grfMode
7355 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7356 accessMode
= GetAccessModeFromSTGM(grfMode
);
7358 if (grfMode
& STGM_DELETEONRELEASE
)
7359 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7361 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7363 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7367 FIXME("Storage share mode not implemented.\n");
7372 hFile
= CreateFileW(pwcsName
,
7380 if (hFile
== INVALID_HANDLE_VALUE
)
7382 if(GetLastError() == ERROR_FILE_EXISTS
)
7383 hr
= STG_E_FILEALREADYEXISTS
;
7390 * Allocate and initialize the new IStorage32object.
7392 hr
= Storage_Construct(
7399 pStgOptions
->ulSectorSize
,
7407 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
7408 IStorage_Release(&newStorage
->IStorage_iface
);
7411 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7416 /******************************************************************************
7417 * StgCreateDocfile [OLE32.@]
7418 * Creates a new compound file storage object
7421 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7422 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7423 * reserved [ ?] unused?, usually 0
7424 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7427 * S_OK if the file was successfully created
7428 * some STG_E_ value if error
7430 * if pwcsName is NULL, create file with new unique name
7431 * the function can returns
7432 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7435 HRESULT WINAPI
StgCreateDocfile(
7439 IStorage
**ppstgOpen
)
7441 STGOPTIONS stgoptions
= {1, 0, 512};
7443 TRACE("(%s, %x, %d, %p)\n",
7444 debugstr_w(pwcsName
), grfMode
,
7445 reserved
, ppstgOpen
);
7448 return STG_E_INVALIDPOINTER
;
7450 return STG_E_INVALIDPARAMETER
;
7452 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7455 /******************************************************************************
7456 * StgCreateStorageEx [OLE32.@]
7458 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7460 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7461 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7463 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7465 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7466 return STG_E_INVALIDPARAMETER
;
7469 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7471 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7472 return STG_E_INVALIDPARAMETER
;
7475 if (stgfmt
== STGFMT_FILE
)
7477 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7478 return STG_E_INVALIDPARAMETER
;
7481 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7483 STGOPTIONS defaultOptions
= {1, 0, 512};
7485 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7486 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7490 ERR("Invalid stgfmt argument\n");
7491 return STG_E_INVALIDPARAMETER
;
7494 /******************************************************************************
7495 * StgCreatePropSetStg [OLE32.@]
7497 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7498 IPropertySetStorage
**propset
)
7500 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
7502 return STG_E_INVALIDPARAMETER
;
7504 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
7507 /******************************************************************************
7508 * StgOpenStorageEx [OLE32.@]
7510 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7512 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7513 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7515 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7517 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7518 return STG_E_INVALIDPARAMETER
;
7524 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7525 return STG_E_INVALIDPARAMETER
;
7527 case STGFMT_STORAGE
:
7530 case STGFMT_DOCFILE
:
7531 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7533 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7534 return STG_E_INVALIDPARAMETER
;
7536 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7540 WARN("STGFMT_ANY assuming storage\n");
7544 return STG_E_INVALIDPARAMETER
;
7547 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7551 /******************************************************************************
7552 * StgOpenStorage [OLE32.@]
7554 HRESULT WINAPI
StgOpenStorage(
7555 const OLECHAR
*pwcsName
,
7556 IStorage
*pstgPriority
,
7560 IStorage
**ppstgOpen
)
7562 StorageBaseImpl
* newStorage
= 0;
7568 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7569 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7570 snbExclude
, reserved
, ppstgOpen
);
7574 hr
= STG_E_INVALIDNAME
;
7580 hr
= STG_E_INVALIDPOINTER
;
7586 hr
= STG_E_INVALIDPARAMETER
;
7590 if (grfMode
& STGM_PRIORITY
)
7592 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7593 return STG_E_INVALIDFLAG
;
7594 if (grfMode
& STGM_DELETEONRELEASE
)
7595 return STG_E_INVALIDFUNCTION
;
7596 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7597 return STG_E_INVALIDFLAG
;
7598 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7599 grfMode
|= STGM_SHARE_DENY_NONE
;
7601 /* STGM_PRIORITY stops other IStorage objects on the same file from
7602 * committing until the STGM_PRIORITY IStorage is closed. it also
7603 * stops non-transacted mode StgOpenStorage calls with write access from
7604 * succeeding. obviously, both of these cannot be achieved through just
7605 * file share flags */
7606 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7610 * Validate the sharing mode
7612 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7613 switch(STGM_SHARE_MODE(grfMode
))
7615 case STGM_SHARE_EXCLUSIVE
:
7616 case STGM_SHARE_DENY_WRITE
:
7619 hr
= STG_E_INVALIDFLAG
;
7623 if ( FAILED( validateSTGM(grfMode
) ) ||
7624 (grfMode
&STGM_CREATE
))
7626 hr
= STG_E_INVALIDFLAG
;
7630 /* shared reading requires transacted mode */
7631 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7632 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7633 !(grfMode
&STGM_TRANSACTED
) )
7635 hr
= STG_E_INVALIDFLAG
;
7640 * Interpret the STGM value grfMode
7642 shareMode
= GetShareModeFromSTGM(grfMode
);
7643 accessMode
= GetAccessModeFromSTGM(grfMode
);
7647 hFile
= CreateFileW( pwcsName
,
7652 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7655 if (hFile
==INVALID_HANDLE_VALUE
)
7657 DWORD last_error
= GetLastError();
7663 case ERROR_FILE_NOT_FOUND
:
7664 hr
= STG_E_FILENOTFOUND
;
7667 case ERROR_PATH_NOT_FOUND
:
7668 hr
= STG_E_PATHNOTFOUND
;
7671 case ERROR_ACCESS_DENIED
:
7672 case ERROR_WRITE_PROTECT
:
7673 hr
= STG_E_ACCESSDENIED
;
7676 case ERROR_SHARING_VIOLATION
:
7677 hr
= STG_E_SHAREVIOLATION
;
7688 * Refuse to open the file if it's too small to be a structured storage file
7689 * FIXME: verify the file when reading instead of here
7691 if (GetFileSize(hFile
, NULL
) < 0x100)
7694 hr
= STG_E_FILEALREADYEXISTS
;
7699 * Allocate and initialize the new IStorage32object.
7701 hr
= Storage_Construct(
7714 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7716 if(hr
== STG_E_INVALIDHEADER
)
7717 hr
= STG_E_FILEALREADYEXISTS
;
7721 *ppstgOpen
= &newStorage
->IStorage_iface
;
7724 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7728 /******************************************************************************
7729 * StgCreateDocfileOnILockBytes [OLE32.@]
7731 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7735 IStorage
** ppstgOpen
)
7737 StorageBaseImpl
* newStorage
= 0;
7740 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7741 return STG_E_INVALIDPOINTER
;
7744 * Allocate and initialize the new IStorage object.
7746 hr
= Storage_Construct(
7761 *ppstgOpen
= &newStorage
->IStorage_iface
;
7766 /******************************************************************************
7767 * StgOpenStorageOnILockBytes [OLE32.@]
7769 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7771 IStorage
*pstgPriority
,
7775 IStorage
**ppstgOpen
)
7777 StorageBaseImpl
* newStorage
= 0;
7780 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7781 return STG_E_INVALIDPOINTER
;
7783 if ( FAILED( validateSTGM(grfMode
) ))
7784 return STG_E_INVALIDFLAG
;
7789 * Allocate and initialize the new IStorage object.
7791 hr
= Storage_Construct(
7806 *ppstgOpen
= &newStorage
->IStorage_iface
;
7811 /******************************************************************************
7812 * StgSetTimes [ole32.@]
7813 * StgSetTimes [OLE32.@]
7817 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7818 FILETIME
const *patime
, FILETIME
const *pmtime
)
7820 IStorage
*stg
= NULL
;
7823 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7825 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7829 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7830 IStorage_Release(stg
);
7836 /******************************************************************************
7837 * StgIsStorageILockBytes [OLE32.@]
7839 * Determines if the ILockBytes contains a storage object.
7841 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7843 BYTE sig
[sizeof(STORAGE_magic
)];
7844 ULARGE_INTEGER offset
;
7847 offset
.u
.HighPart
= 0;
7848 offset
.u
.LowPart
= 0;
7850 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7852 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7858 /******************************************************************************
7859 * WriteClassStg [OLE32.@]
7861 * This method will store the specified CLSID in the specified storage object
7863 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7866 return E_INVALIDARG
;
7869 return STG_E_INVALIDPOINTER
;
7871 return IStorage_SetClass(pStg
, rclsid
);
7874 /***********************************************************************
7875 * ReadClassStg (OLE32.@)
7877 * This method reads the CLSID previously written to a storage object with
7878 * the WriteClassStg.
7881 * pstg [I] IStorage pointer
7882 * pclsid [O] Pointer to where the CLSID is written
7886 * Failure: HRESULT code.
7888 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7893 TRACE("(%p, %p)\n", pstg
, pclsid
);
7895 if(!pstg
|| !pclsid
)
7896 return E_INVALIDARG
;
7899 * read a STATSTG structure (contains the clsid) from the storage
7901 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7904 *pclsid
=pstatstg
.clsid
;
7909 /***********************************************************************
7910 * OleLoadFromStream (OLE32.@)
7912 * This function loads an object from stream
7914 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7918 LPPERSISTSTREAM xstm
;
7920 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7922 res
=ReadClassStm(pStm
,&clsid
);
7925 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7928 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7930 IUnknown_Release((IUnknown
*)*ppvObj
);
7933 res
=IPersistStream_Load(xstm
,pStm
);
7934 IPersistStream_Release(xstm
);
7935 /* FIXME: all refcounts ok at this point? I think they should be:
7938 * xstm : 0 (released)
7943 /***********************************************************************
7944 * OleSaveToStream (OLE32.@)
7946 * This function saves an object with the IPersistStream interface on it
7947 * to the specified stream.
7949 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7955 TRACE("(%p,%p)\n",pPStm
,pStm
);
7957 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7959 if (SUCCEEDED(res
)){
7961 res
=WriteClassStm(pStm
,&clsid
);
7965 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7968 TRACE("Finished Save\n");
7972 /****************************************************************************
7973 * This method validate a STGM parameter that can contain the values below
7975 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7976 * The stgm values contained in 0xffff0000 are bitmasks.
7978 * STGM_DIRECT 0x00000000
7979 * STGM_TRANSACTED 0x00010000
7980 * STGM_SIMPLE 0x08000000
7982 * STGM_READ 0x00000000
7983 * STGM_WRITE 0x00000001
7984 * STGM_READWRITE 0x00000002
7986 * STGM_SHARE_DENY_NONE 0x00000040
7987 * STGM_SHARE_DENY_READ 0x00000030
7988 * STGM_SHARE_DENY_WRITE 0x00000020
7989 * STGM_SHARE_EXCLUSIVE 0x00000010
7991 * STGM_PRIORITY 0x00040000
7992 * STGM_DELETEONRELEASE 0x04000000
7994 * STGM_CREATE 0x00001000
7995 * STGM_CONVERT 0x00020000
7996 * STGM_FAILIFTHERE 0x00000000
7998 * STGM_NOSCRATCH 0x00100000
7999 * STGM_NOSNAPSHOT 0x00200000
8001 static HRESULT
validateSTGM(DWORD stgm
)
8003 DWORD access
= STGM_ACCESS_MODE(stgm
);
8004 DWORD share
= STGM_SHARE_MODE(stgm
);
8005 DWORD create
= STGM_CREATE_MODE(stgm
);
8007 if (stgm
&~STGM_KNOWN_FLAGS
)
8009 ERR("unknown flags %08x\n", stgm
);
8017 case STGM_READWRITE
:
8025 case STGM_SHARE_DENY_NONE
:
8026 case STGM_SHARE_DENY_READ
:
8027 case STGM_SHARE_DENY_WRITE
:
8028 case STGM_SHARE_EXCLUSIVE
:
8037 case STGM_FAILIFTHERE
:
8044 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8046 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8050 * STGM_CREATE | STGM_CONVERT
8051 * if both are false, STGM_FAILIFTHERE is set to TRUE
8053 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8057 * STGM_NOSCRATCH requires STGM_TRANSACTED
8059 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8063 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8064 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8066 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8067 (!(stgm
& STGM_TRANSACTED
) ||
8068 share
== STGM_SHARE_EXCLUSIVE
||
8069 share
== STGM_SHARE_DENY_WRITE
) )
8075 /****************************************************************************
8076 * GetShareModeFromSTGM
8078 * This method will return a share mode flag from a STGM value.
8079 * The STGM value is assumed valid.
8081 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8083 switch (STGM_SHARE_MODE(stgm
))
8085 case STGM_SHARE_DENY_NONE
:
8086 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8087 case STGM_SHARE_DENY_READ
:
8088 return FILE_SHARE_WRITE
;
8089 case STGM_SHARE_DENY_WRITE
:
8090 return FILE_SHARE_READ
;
8091 case STGM_SHARE_EXCLUSIVE
:
8094 ERR("Invalid share mode!\n");
8099 /****************************************************************************
8100 * GetAccessModeFromSTGM
8102 * This method will return an access mode flag from a STGM value.
8103 * The STGM value is assumed valid.
8105 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8107 switch (STGM_ACCESS_MODE(stgm
))
8110 return GENERIC_READ
;
8112 case STGM_READWRITE
:
8113 return GENERIC_READ
| GENERIC_WRITE
;
8115 ERR("Invalid access mode!\n");
8120 /****************************************************************************
8121 * GetCreationModeFromSTGM
8123 * This method will return a creation mode flag from a STGM value.
8124 * The STGM value is assumed valid.
8126 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8128 switch(STGM_CREATE_MODE(stgm
))
8131 return CREATE_ALWAYS
;
8133 FIXME("STGM_CONVERT not implemented!\n");
8135 case STGM_FAILIFTHERE
:
8138 ERR("Invalid create mode!\n");
8144 /*************************************************************************
8145 * OLECONVERT_LoadOLE10 [Internal]
8147 * Loads the OLE10 STREAM to memory
8150 * pOleStream [I] The OLESTREAM
8151 * pData [I] Data Structure for the OLESTREAM Data
8155 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8156 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8159 * This function is used by OleConvertOLESTREAMToIStorage only.
8161 * Memory allocated for pData must be freed by the caller
8163 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8166 HRESULT hRes
= S_OK
;
8170 pData
->pData
= NULL
;
8171 pData
->pstrOleObjFileName
= NULL
;
8173 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8176 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8177 if(dwSize
!= sizeof(pData
->dwOleID
))
8179 hRes
= CONVERT10_E_OLESTREAM_GET
;
8181 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8183 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8194 /* Get the TypeID... more info needed for this field */
8195 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8196 if(dwSize
!= sizeof(pData
->dwTypeID
))
8198 hRes
= CONVERT10_E_OLESTREAM_GET
;
8203 if(pData
->dwTypeID
!= 0)
8205 /* Get the length of the OleTypeName */
8206 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8207 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8209 hRes
= CONVERT10_E_OLESTREAM_GET
;
8214 if(pData
->dwOleTypeNameLength
> 0)
8216 /* Get the OleTypeName */
8217 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8218 if(dwSize
!= pData
->dwOleTypeNameLength
)
8220 hRes
= CONVERT10_E_OLESTREAM_GET
;
8226 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8227 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8229 hRes
= CONVERT10_E_OLESTREAM_GET
;
8233 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8234 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8235 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8236 if(pData
->pstrOleObjFileName
)
8238 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8239 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8241 hRes
= CONVERT10_E_OLESTREAM_GET
;
8245 hRes
= CONVERT10_E_OLESTREAM_GET
;
8250 /* Get the Width of the Metafile */
8251 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8252 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8254 hRes
= CONVERT10_E_OLESTREAM_GET
;
8258 /* Get the Height of the Metafile */
8259 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8260 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8262 hRes
= CONVERT10_E_OLESTREAM_GET
;
8268 /* Get the Length of the Data */
8269 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8270 if(dwSize
!= sizeof(pData
->dwDataLength
))
8272 hRes
= CONVERT10_E_OLESTREAM_GET
;
8276 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8278 if(!bStrem1
) /* if it is a second OLE stream data */
8280 pData
->dwDataLength
-= 8;
8281 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8282 if(dwSize
!= sizeof(pData
->strUnknown
))
8284 hRes
= CONVERT10_E_OLESTREAM_GET
;
8290 if(pData
->dwDataLength
> 0)
8292 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8294 /* Get Data (ex. IStorage, Metafile, or BMP) */
8297 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8298 if(dwSize
!= pData
->dwDataLength
)
8300 hRes
= CONVERT10_E_OLESTREAM_GET
;
8305 hRes
= CONVERT10_E_OLESTREAM_GET
;
8314 /*************************************************************************
8315 * OLECONVERT_SaveOLE10 [Internal]
8317 * Saves the OLE10 STREAM From memory
8320 * pData [I] Data Structure for the OLESTREAM Data
8321 * pOleStream [I] The OLESTREAM to save
8325 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8328 * This function is used by OleConvertIStorageToOLESTREAM only.
8331 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8334 HRESULT hRes
= S_OK
;
8338 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8339 if(dwSize
!= sizeof(pData
->dwOleID
))
8341 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8346 /* Set the TypeID */
8347 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8348 if(dwSize
!= sizeof(pData
->dwTypeID
))
8350 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8354 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8356 /* Set the Length of the OleTypeName */
8357 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8358 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8360 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8365 if(pData
->dwOleTypeNameLength
> 0)
8367 /* Set the OleTypeName */
8368 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8369 if(dwSize
!= pData
->dwOleTypeNameLength
)
8371 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8378 /* Set the width of the Metafile */
8379 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8380 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8382 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8388 /* Set the height of the Metafile */
8389 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8390 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8392 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8398 /* Set the length of the Data */
8399 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8400 if(dwSize
!= sizeof(pData
->dwDataLength
))
8402 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8408 if(pData
->dwDataLength
> 0)
8410 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8411 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8412 if(dwSize
!= pData
->dwDataLength
)
8414 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8422 /*************************************************************************
8423 * OLECONVERT_GetOLE20FromOLE10[Internal]
8425 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8426 * opens it, and copies the content to the dest IStorage for
8427 * OleConvertOLESTREAMToIStorage
8431 * pDestStorage [I] The IStorage to copy the data to
8432 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8433 * nBufferLength [I] The size of the buffer
8442 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8446 IStorage
*pTempStorage
;
8447 DWORD dwNumOfBytesWritten
;
8448 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8449 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8451 /* Create a temp File */
8452 GetTempPathW(MAX_PATH
, wstrTempDir
);
8453 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8454 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8456 if(hFile
!= INVALID_HANDLE_VALUE
)
8458 /* Write IStorage Data to File */
8459 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8462 /* Open and copy temp storage to the Dest Storage */
8463 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8466 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8467 IStorage_Release(pTempStorage
);
8469 DeleteFileW(wstrTempFile
);
8474 /*************************************************************************
8475 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8477 * Saves the OLE10 STREAM From memory
8480 * pStorage [I] The Src IStorage to copy
8481 * pData [I] The Dest Memory to write to.
8484 * The size in bytes allocated for pData
8487 * Memory allocated for pData must be freed by the caller
8489 * Used by OleConvertIStorageToOLESTREAM only.
8492 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8496 DWORD nDataLength
= 0;
8497 IStorage
*pTempStorage
;
8498 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8499 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8503 /* Create temp Storage */
8504 GetTempPathW(MAX_PATH
, wstrTempDir
);
8505 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8506 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8510 /* Copy Src Storage to the Temp Storage */
8511 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8512 IStorage_Release(pTempStorage
);
8514 /* Open Temp Storage as a file and copy to memory */
8515 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8516 if(hFile
!= INVALID_HANDLE_VALUE
)
8518 nDataLength
= GetFileSize(hFile
, NULL
);
8519 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8520 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8523 DeleteFileW(wstrTempFile
);
8528 /*************************************************************************
8529 * OLECONVERT_CreateOleStream [Internal]
8531 * Creates the "\001OLE" stream in the IStorage if necessary.
8534 * pStorage [I] Dest storage to create the stream in
8540 * This function is used by OleConvertOLESTREAMToIStorage only.
8542 * This stream is still unknown, MS Word seems to have extra data
8543 * but since the data is stored in the OLESTREAM there should be
8544 * no need to recreate the stream. If the stream is manually
8545 * deleted it will create it with this default data.
8548 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8552 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8553 BYTE pOleStreamHeader
[] =
8555 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8557 0x00, 0x00, 0x00, 0x00
8560 /* Create stream if not present */
8561 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8562 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8566 /* Write default Data */
8567 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8568 IStream_Release(pStream
);
8572 /* write a string to a stream, preceded by its length */
8573 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8580 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8581 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8586 str
= CoTaskMemAlloc( len
);
8587 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8588 r
= IStream_Write( stm
, str
, len
, NULL
);
8589 CoTaskMemFree( str
);
8593 /* read a string preceded by its length from a stream */
8594 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8597 DWORD len
, count
= 0;
8601 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8604 if( count
!= sizeof(len
) )
8605 return E_OUTOFMEMORY
;
8607 TRACE("%d bytes\n",len
);
8609 str
= CoTaskMemAlloc( len
);
8611 return E_OUTOFMEMORY
;
8613 r
= IStream_Read( stm
, str
, len
, &count
);
8618 CoTaskMemFree( str
);
8619 return E_OUTOFMEMORY
;
8622 TRACE("Read string %s\n",debugstr_an(str
,len
));
8624 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8625 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8627 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8628 CoTaskMemFree( str
);
8636 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8637 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8641 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8643 static const BYTE unknown1
[12] =
8644 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8645 0xFF, 0xFF, 0xFF, 0xFF};
8646 static const BYTE unknown2
[16] =
8647 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8648 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8650 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8651 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8652 debugstr_w(szProgIDName
));
8654 /* Create a CompObj stream */
8655 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8656 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8660 /* Write CompObj Structure to stream */
8661 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8663 if( SUCCEEDED( r
) )
8664 r
= WriteClassStm( pstm
, clsid
);
8666 if( SUCCEEDED( r
) )
8667 r
= STREAM_WriteString( pstm
, lpszUserType
);
8668 if( SUCCEEDED( r
) )
8669 r
= STREAM_WriteString( pstm
, szClipName
);
8670 if( SUCCEEDED( r
) )
8671 r
= STREAM_WriteString( pstm
, szProgIDName
);
8672 if( SUCCEEDED( r
) )
8673 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8675 IStream_Release( pstm
);
8680 /***********************************************************************
8681 * WriteFmtUserTypeStg (OLE32.@)
8683 HRESULT WINAPI
WriteFmtUserTypeStg(
8684 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8687 WCHAR szwClipName
[0x40];
8688 CLSID clsid
= CLSID_NULL
;
8689 LPWSTR wstrProgID
= NULL
;
8692 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8694 /* get the clipboard format name */
8695 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8698 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8700 /* FIXME: There's room to save a CLSID and its ProgID, but
8701 the CLSID is not looked up in the registry and in all the
8702 tests I wrote it was CLSID_NULL. Where does it come from?
8705 /* get the real program ID. This may fail, but that's fine */
8706 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8708 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8710 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8711 lpszUserType
, szwClipName
, wstrProgID
);
8713 CoTaskMemFree(wstrProgID
);
8719 /******************************************************************************
8720 * ReadFmtUserTypeStg [OLE32.@]
8722 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8726 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8727 unsigned char unknown1
[12];
8728 unsigned char unknown2
[16];
8730 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8733 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8735 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8736 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8739 WARN("Failed to open stream r = %08x\n", r
);
8743 /* read the various parts of the structure */
8744 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8745 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8747 r
= ReadClassStm( stm
, &clsid
);
8751 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8755 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8759 r
= STREAM_ReadString( stm
, &szProgIDName
);
8763 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8764 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8767 /* ok, success... now we just need to store what we found */
8769 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8770 CoTaskMemFree( szOleTypeName
);
8772 if( lplpszUserType
)
8773 *lplpszUserType
= szCLSIDName
;
8774 CoTaskMemFree( szProgIDName
);
8777 IStream_Release( stm
);
8783 /*************************************************************************
8784 * OLECONVERT_CreateCompObjStream [Internal]
8786 * Creates a "\001CompObj" is the destination IStorage if necessary.
8789 * pStorage [I] The dest IStorage to create the CompObj Stream
8791 * strOleTypeName [I] The ProgID
8795 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8798 * This function is used by OleConvertOLESTREAMToIStorage only.
8800 * The stream data is stored in the OLESTREAM and there should be
8801 * no need to recreate the stream. If the stream is manually
8802 * deleted it will attempt to create it by querying the registry.
8806 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8809 HRESULT hStorageRes
, hRes
= S_OK
;
8810 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8811 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8812 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8814 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8815 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8817 /* Initialize the CompObj structure */
8818 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8819 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8820 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8823 /* Create a CompObj stream if it doesn't exist */
8824 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8825 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8826 if(hStorageRes
== S_OK
)
8828 /* copy the OleTypeName to the compobj struct */
8829 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8830 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8832 /* copy the OleTypeName to the compobj struct */
8833 /* Note: in the test made, these were Identical */
8834 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8835 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8838 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8839 bufferW
, OLESTREAM_MAX_STR_LEN
);
8840 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8846 /* Get the CLSID Default Name from the Registry */
8847 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8848 if(hErr
== ERROR_SUCCESS
)
8850 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8851 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8852 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8853 if(hErr
== ERROR_SUCCESS
)
8855 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8861 /* Write CompObj Structure to stream */
8862 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8864 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8866 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8867 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8869 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8871 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8872 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8874 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8876 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8877 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8879 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8881 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8882 IStream_Release(pStream
);
8888 /*************************************************************************
8889 * OLECONVERT_CreateOlePresStream[Internal]
8891 * Creates the "\002OlePres000" Stream with the Metafile data
8894 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8895 * dwExtentX [I] Width of the Metafile
8896 * dwExtentY [I] Height of the Metafile
8897 * pData [I] Metafile data
8898 * dwDataLength [I] Size of the Metafile data
8902 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8905 * This function is used by OleConvertOLESTREAMToIStorage only.
8908 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8912 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8913 BYTE pOlePresStreamHeader
[] =
8915 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8916 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8917 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8918 0x00, 0x00, 0x00, 0x00
8921 BYTE pOlePresStreamHeaderEmpty
[] =
8923 0x00, 0x00, 0x00, 0x00,
8924 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8925 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8926 0x00, 0x00, 0x00, 0x00
8929 /* Create the OlePres000 Stream */
8930 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8931 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8936 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8938 memset(&OlePres
, 0, sizeof(OlePres
));
8939 /* Do we have any metafile data to save */
8940 if(dwDataLength
> 0)
8942 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8943 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8947 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8948 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8950 /* Set width and height of the metafile */
8951 OlePres
.dwExtentX
= dwExtentX
;
8952 OlePres
.dwExtentY
= -dwExtentY
;
8954 /* Set Data and Length */
8955 if(dwDataLength
> sizeof(METAFILEPICT16
))
8957 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8958 OlePres
.pData
= &(pData
[8]);
8960 /* Save OlePres000 Data to Stream */
8961 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8962 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8963 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8964 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8965 if(OlePres
.dwSize
> 0)
8967 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8969 IStream_Release(pStream
);
8973 /*************************************************************************
8974 * OLECONVERT_CreateOle10NativeStream [Internal]
8976 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8979 * pStorage [I] Dest storage to create the stream in
8980 * pData [I] Ole10 Native Data (ex. bmp)
8981 * dwDataLength [I] Size of the Ole10 Native Data
8987 * This function is used by OleConvertOLESTREAMToIStorage only.
8989 * Might need to verify the data and return appropriate error message
8992 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8996 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8998 /* Create the Ole10Native Stream */
8999 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9000 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9004 /* Write info to stream */
9005 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9006 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9007 IStream_Release(pStream
);
9012 /*************************************************************************
9013 * OLECONVERT_GetOLE10ProgID [Internal]
9015 * Finds the ProgID (or OleTypeID) from the IStorage
9018 * pStorage [I] The Src IStorage to get the ProgID
9019 * strProgID [I] the ProgID string to get
9020 * dwSize [I] the size of the string
9024 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9027 * This function is used by OleConvertIStorageToOLESTREAM only.
9031 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9035 LARGE_INTEGER iSeekPos
;
9036 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9037 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9039 /* Open the CompObj Stream */
9040 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9041 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9045 /*Get the OleType from the CompObj Stream */
9046 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9047 iSeekPos
.u
.HighPart
= 0;
9049 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9050 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9051 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9052 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9053 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9054 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9055 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9057 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9060 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9062 IStream_Release(pStream
);
9067 LPOLESTR wstrProgID
;
9069 /* Get the OleType from the registry */
9070 REFCLSID clsid
= &(stat
.clsid
);
9071 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9072 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9075 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9082 /*************************************************************************
9083 * OLECONVERT_GetOle10PresData [Internal]
9085 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9088 * pStorage [I] Src IStroage
9089 * pOleStream [I] Dest OleStream Mem Struct
9095 * This function is used by OleConvertIStorageToOLESTREAM only.
9097 * Memory allocated for pData must be freed by the caller
9101 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9106 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9108 /* Initialize Default data for OLESTREAM */
9109 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9110 pOleStreamData
[0].dwTypeID
= 2;
9111 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9112 pOleStreamData
[1].dwTypeID
= 0;
9113 pOleStreamData
[0].dwMetaFileWidth
= 0;
9114 pOleStreamData
[0].dwMetaFileHeight
= 0;
9115 pOleStreamData
[0].pData
= NULL
;
9116 pOleStreamData
[1].pData
= NULL
;
9118 /* Open Ole10Native Stream */
9119 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9120 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9124 /* Read Size and Data */
9125 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9126 if(pOleStreamData
->dwDataLength
> 0)
9128 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9129 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9131 IStream_Release(pStream
);
9137 /*************************************************************************
9138 * OLECONVERT_GetOle20PresData[Internal]
9140 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9143 * pStorage [I] Src IStroage
9144 * pOleStreamData [I] Dest OleStream Mem Struct
9150 * This function is used by OleConvertIStorageToOLESTREAM only.
9152 * Memory allocated for pData must be freed by the caller
9154 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9158 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9159 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9161 /* Initialize Default data for OLESTREAM */
9162 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9163 pOleStreamData
[0].dwTypeID
= 2;
9164 pOleStreamData
[0].dwMetaFileWidth
= 0;
9165 pOleStreamData
[0].dwMetaFileHeight
= 0;
9166 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9167 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9168 pOleStreamData
[1].dwTypeID
= 0;
9169 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9170 pOleStreamData
[1].strOleTypeName
[0] = 0;
9171 pOleStreamData
[1].dwMetaFileWidth
= 0;
9172 pOleStreamData
[1].dwMetaFileHeight
= 0;
9173 pOleStreamData
[1].pData
= NULL
;
9174 pOleStreamData
[1].dwDataLength
= 0;
9177 /* Open OlePress000 stream */
9178 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9179 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9182 LARGE_INTEGER iSeekPos
;
9183 METAFILEPICT16 MetaFilePict
;
9184 static const char strMetafilePictName
[] = "METAFILEPICT";
9186 /* Set the TypeID for a Metafile */
9187 pOleStreamData
[1].dwTypeID
= 5;
9189 /* Set the OleTypeName to Metafile */
9190 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9191 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9193 iSeekPos
.u
.HighPart
= 0;
9194 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9196 /* Get Presentation Data */
9197 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9198 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9199 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9200 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9202 /*Set width and Height */
9203 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9204 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9205 if(olePress
.dwSize
> 0)
9208 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9210 /* Set MetaFilePict struct */
9211 MetaFilePict
.mm
= 8;
9212 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9213 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9214 MetaFilePict
.hMF
= 0;
9216 /* Get Metafile Data */
9217 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9218 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9219 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9221 IStream_Release(pStream
);
9225 /*************************************************************************
9226 * OleConvertOLESTREAMToIStorage [OLE32.@]
9231 * DVTARGETDEVICE parameter is not handled
9232 * Still unsure of some mem fields for OLE 10 Stream
9233 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9234 * and "\001OLE" streams
9237 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9238 LPOLESTREAM pOleStream
,
9240 const DVTARGETDEVICE
* ptd
)
9244 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9246 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9248 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9252 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9255 if(pstg
== NULL
|| pOleStream
== NULL
)
9257 hRes
= E_INVALIDARG
;
9262 /* Load the OLESTREAM to Memory */
9263 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9268 /* Load the OLESTREAM to Memory (part 2)*/
9269 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9275 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9277 /* Do we have the IStorage Data in the OLESTREAM */
9278 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9280 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9281 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9285 /* It must be an original OLE 1.0 source */
9286 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9291 /* It must be an original OLE 1.0 source */
9292 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9295 /* Create CompObj Stream if necessary */
9296 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9299 /*Create the Ole Stream if necessary */
9300 OLECONVERT_CreateOleStream(pstg
);
9305 /* Free allocated memory */
9306 for(i
=0; i
< 2; i
++)
9308 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9309 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9310 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9315 /*************************************************************************
9316 * OleConvertIStorageToOLESTREAM [OLE32.@]
9323 * Still unsure of some mem fields for OLE 10 Stream
9324 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9325 * and "\001OLE" streams.
9328 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9330 LPOLESTREAM pOleStream
)
9333 HRESULT hRes
= S_OK
;
9335 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9336 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9338 TRACE("%p %p\n", pstg
, pOleStream
);
9340 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9342 if(pstg
== NULL
|| pOleStream
== NULL
)
9344 hRes
= E_INVALIDARG
;
9348 /* Get the ProgID */
9349 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9350 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9354 /* Was it originally Ole10 */
9355 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9358 IStream_Release(pStream
);
9359 /* Get Presentation Data for Ole10Native */
9360 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9364 /* Get Presentation Data (OLE20) */
9365 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9368 /* Save OLESTREAM */
9369 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9372 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9377 /* Free allocated memory */
9378 for(i
=0; i
< 2; i
++)
9380 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9386 enum stream_1ole_flags
{
9387 OleStream_LinkedObject
= 0x00000001,
9388 OleStream_Convert
= 0x00000100
9391 /***********************************************************************
9392 * GetConvertStg (OLE32.@)
9394 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
9396 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9397 static const DWORD version_magic
= 0x02000001;
9405 if (!stg
) return E_INVALIDARG
;
9407 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9408 if (FAILED(hr
)) return hr
;
9411 hr
= IStream_Read(stream
, header
, sizeof(header
), &len
);
9412 IStream_Release(stream
);
9413 if (FAILED(hr
)) return hr
;
9415 if (header
[0] != version_magic
)
9417 ERR("got wrong version magic for \1Ole stream, 0x%08x\n", header
[0]);
9421 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
9424 /******************************************************************************
9425 * StgIsStorageFile [OLE32.@]
9426 * Verify if the file contains a storage object
9432 * S_OK if file has magic bytes as a storage object
9433 * S_FALSE if file is not storage
9436 StgIsStorageFile(LPCOLESTR fn
)
9442 TRACE("%s\n", debugstr_w(fn
));
9443 hf
= CreateFileW(fn
, GENERIC_READ
,
9444 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9445 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9447 if (hf
== INVALID_HANDLE_VALUE
)
9448 return STG_E_FILENOTFOUND
;
9450 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9452 WARN(" unable to read file\n");
9459 if (bytes_read
!= 8) {
9460 TRACE(" too short\n");
9464 if (!memcmp(magic
,STORAGE_magic
,8)) {
9469 TRACE(" -> Invalid header.\n");
9473 /***********************************************************************
9474 * WriteClassStm (OLE32.@)
9476 * Writes a CLSID to a stream.
9479 * pStm [I] Stream to write to.
9480 * rclsid [I] CLSID to write.
9484 * Failure: HRESULT code.
9486 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9488 TRACE("(%p,%p)\n",pStm
,rclsid
);
9490 if (!pStm
|| !rclsid
)
9491 return E_INVALIDARG
;
9493 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9496 /***********************************************************************
9497 * ReadClassStm (OLE32.@)
9499 * Reads a CLSID from a stream.
9502 * pStm [I] Stream to read from.
9503 * rclsid [O] CLSID to read.
9507 * Failure: HRESULT code.
9509 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9514 TRACE("(%p,%p)\n",pStm
,pclsid
);
9516 if (!pStm
|| !pclsid
)
9517 return E_INVALIDARG
;
9519 /* clear the output args */
9520 *pclsid
= CLSID_NULL
;
9522 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9527 if (nbByte
!= sizeof(CLSID
))
9528 return STG_E_READFAULT
;