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.
34 #include "storage32.h"
36 #include <wine/wingdi16.h>
38 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
40 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
41 #define OLESTREAM_ID 0x501
42 #define OLESTREAM_MAX_STR_LEN 255
45 * These are signatures to detect the type of Document file.
47 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
48 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
50 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
52 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
55 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
57 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
60 /****************************************************************************
61 * Storage32InternalImpl definitions.
63 * Definition of the implementation structure for the IStorage32 interface.
64 * This one implements the IStorage32 interface for storage that are
65 * inside another storage.
67 struct StorageInternalImpl
69 struct StorageBaseImpl base
;
72 * Entry in the parent's stream tracking list
74 struct list ParentListEntry
;
76 StorageBaseImpl
*parentStorage
;
78 typedef struct StorageInternalImpl StorageInternalImpl
;
80 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
81 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
85 DWORD openFlags
, DirRef storageDirEntry
);
86 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
87 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
88 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
89 static HRESULT
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
, ULONG
*read
);
90 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
91 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
92 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
93 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
95 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
96 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
97 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
98 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
99 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
101 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
102 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
103 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
105 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
106 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
107 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
108 ULONG blockIndex
, ULONG offset
, DWORD value
);
109 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
110 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
112 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
113 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
115 typedef struct TransactedDirEntry
117 /* If applicable, a reference to the original DirEntry in the transacted
118 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
119 DirRef transactedParentEntry
;
121 /* True if this entry is being used. */
124 /* True if data is up to date. */
127 /* True if this entry has been modified. */
130 /* True if this entry's stream has been modified. */
133 /* True if this entry has been deleted in the transacted storage, but the
134 * delete has not yet been committed. */
137 /* If this entry's stream has been modified, a reference to where the stream
138 * is stored in the snapshot file. */
141 /* This directory entry's data, including any changes that have been made. */
144 /* A reference to the parent of this node. This is only valid while we are
145 * committing changes. */
148 /* A reference to a newly-created entry in the transacted parent. This is
149 * always equal to transactedParentEntry except when committing changes. */
150 DirRef newTransactedParentEntry
;
151 } TransactedDirEntry
;
153 /****************************************************************************
154 * Transacted storage object.
156 typedef struct TransactedSnapshotImpl
158 struct StorageBaseImpl base
;
161 * Modified streams are temporarily saved to the scratch file.
163 StorageBaseImpl
*scratch
;
165 /* The directory structure is kept here, so that we can track how these
166 * entries relate to those in the parent storage. */
167 TransactedDirEntry
*entries
;
169 ULONG firstFreeEntry
;
172 * Changes are committed to the transacted parent.
174 StorageBaseImpl
*transactedParent
;
175 } TransactedSnapshotImpl
;
177 /* Generic function to create a transacted wrapper for a direct storage object. */
178 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
180 /* OLESTREAM memory structure to use for Get and Put Routines */
181 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
186 DWORD dwOleTypeNameLength
;
187 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
188 CHAR
*pstrOleObjFileName
;
189 DWORD dwOleObjFileNameLength
;
190 DWORD dwMetaFileWidth
;
191 DWORD dwMetaFileHeight
;
192 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
195 }OLECONVERT_OLESTREAM_DATA
;
197 /* CompObj Stream structure */
198 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
203 DWORD dwCLSIDNameLength
;
204 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
205 DWORD dwOleTypeNameLength
;
206 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
207 DWORD dwProgIDNameLength
;
208 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
210 }OLECONVERT_ISTORAGE_COMPOBJ
;
213 /* Ole Presentation Stream structure */
214 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 }OLECONVERT_ISTORAGE_OLEPRES
;
226 /***********************************************************************
227 * Forward declaration of internal functions used by the method DestroyElement
229 static HRESULT
deleteStorageContents(
230 StorageBaseImpl
*parentStorage
,
231 DirRef indexToDelete
,
232 DirEntry entryDataToDelete
);
234 static HRESULT
deleteStreamContents(
235 StorageBaseImpl
*parentStorage
,
236 DirRef indexToDelete
,
237 DirEntry entryDataToDelete
);
239 static HRESULT
removeFromTree(
240 StorageBaseImpl
*This
,
241 DirRef parentStorageIndex
,
242 DirRef deletedIndex
);
244 /***********************************************************************
245 * Declaration of the functions used to manipulate DirEntry
248 static HRESULT
insertIntoTree(
249 StorageBaseImpl
*This
,
250 DirRef parentStorageIndex
,
251 DirRef newEntryIndex
);
253 static LONG
entryNameCmp(
254 const OLECHAR
*name1
,
255 const OLECHAR
*name2
);
257 static DirRef
findElement(
258 StorageBaseImpl
*storage
,
263 static HRESULT
findTreeParent(
264 StorageBaseImpl
*storage
,
266 const OLECHAR
*childName
,
267 DirEntry
*parentData
,
271 /***********************************************************************
272 * Declaration of miscellaneous functions...
274 static HRESULT
validateSTGM(DWORD stgmValue
);
276 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
277 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
278 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
280 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
283 /****************************************************************************
284 * IEnumSTATSTGImpl definitions.
286 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
287 * This class allows iterating through the content of a storage and to find
288 * specific items inside it.
290 struct IEnumSTATSTGImpl
292 IEnumSTATSTG IEnumSTATSTG_iface
;
294 LONG ref
; /* Reference count */
295 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
296 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
298 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
301 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
303 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
307 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
308 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
310 /************************************************************************
314 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
316 return (index
+1) * This
->bigBlockSize
;
319 /************************************************************************
320 ** Storage32BaseImpl implementation
322 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
323 ULARGE_INTEGER offset
,
328 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
331 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
332 ULARGE_INTEGER offset
,
337 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
340 /************************************************************************
341 * Storage32BaseImpl_QueryInterface (IUnknown)
343 * This method implements the common QueryInterface for all IStorage32
344 * implementations contained in this file.
346 * See Windows documentation for more details on IUnknown methods.
348 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
353 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
360 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
361 IsEqualGUID(&IID_IStorage
, riid
))
363 *ppvObject
= &This
->IStorage_iface
;
365 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
367 *ppvObject
= &This
->IPropertySetStorage_iface
;
369 /* locking interface is reported for writer only */
370 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
372 *ppvObject
= &This
->IDirectWriterLock_iface
;
375 return E_NOINTERFACE
;
377 IStorage_AddRef(iface
);
382 /************************************************************************
383 * Storage32BaseImpl_AddRef (IUnknown)
385 * This method implements the common AddRef for all IStorage32
386 * implementations contained in this file.
388 * See Windows documentation for more details on IUnknown methods.
390 static ULONG WINAPI
StorageBaseImpl_AddRef(
393 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
394 ULONG ref
= InterlockedIncrement(&This
->ref
);
396 TRACE("(%p) AddRef to %d\n", This
, ref
);
401 /************************************************************************
402 * Storage32BaseImpl_Release (IUnknown)
404 * This method implements the common Release for all IStorage32
405 * implementations contained in this file.
407 * See Windows documentation for more details on IUnknown methods.
409 static ULONG WINAPI
StorageBaseImpl_Release(
412 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
414 ULONG ref
= InterlockedDecrement(&This
->ref
);
416 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
421 * Since we are using a system of base-classes, we want to call the
422 * destructor of the appropriate derived class. To do this, we are
423 * using virtual functions to implement the destructor.
425 StorageBaseImpl_Destroy(This
);
431 /************************************************************************
432 * Storage32BaseImpl_OpenStream (IStorage)
434 * This method will open the specified stream object from the current storage.
436 * See Windows documentation for more details on IStorage methods.
438 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
440 const OLECHAR
* pwcsName
, /* [string][in] */
441 void* reserved1
, /* [unique][in] */
442 DWORD grfMode
, /* [in] */
443 DWORD reserved2
, /* [in] */
444 IStream
** ppstm
) /* [out] */
446 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
447 StgStreamImpl
* newStream
;
448 DirEntry currentEntry
;
449 DirRef streamEntryRef
;
450 HRESULT res
= STG_E_UNKNOWN
;
452 TRACE("(%p, %s, %p, %x, %d, %p)\n",
453 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
455 if ( (pwcsName
==NULL
) || (ppstm
==0) )
463 if ( FAILED( validateSTGM(grfMode
) ) ||
464 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
466 res
= STG_E_INVALIDFLAG
;
473 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
475 res
= STG_E_INVALIDFUNCTION
;
481 res
= STG_E_REVERTED
;
486 * Check that we're compatible with the parent's storage mode, but
487 * only if we are not in transacted mode
489 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
490 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
492 res
= STG_E_INVALIDFLAG
;
498 * Search for the element with the given name
500 streamEntryRef
= findElement(
502 This
->storageDirEntry
,
507 * If it was found, construct the stream object and return a pointer to it.
509 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
510 (currentEntry
.stgType
==STGTY_STREAM
) )
512 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
514 /* A single stream cannot be opened a second time. */
515 res
= STG_E_ACCESSDENIED
;
519 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
523 newStream
->grfMode
= grfMode
;
524 *ppstm
= &newStream
->IStream_iface
;
526 IStream_AddRef(*ppstm
);
536 res
= STG_E_FILENOTFOUND
;
540 TRACE("<-- IStream %p\n", *ppstm
);
541 TRACE("<-- %08x\n", res
);
545 /************************************************************************
546 * Storage32BaseImpl_OpenStorage (IStorage)
548 * This method will open a new storage object from the current storage.
550 * See Windows documentation for more details on IStorage methods.
552 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
554 const OLECHAR
* pwcsName
, /* [string][unique][in] */
555 IStorage
* pstgPriority
, /* [unique][in] */
556 DWORD grfMode
, /* [in] */
557 SNB snbExclude
, /* [unique][in] */
558 DWORD reserved
, /* [in] */
559 IStorage
** ppstg
) /* [out] */
561 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
562 StorageInternalImpl
* newStorage
;
563 StorageBaseImpl
* newTransactedStorage
;
564 DirEntry currentEntry
;
565 DirRef storageEntryRef
;
566 HRESULT res
= STG_E_UNKNOWN
;
568 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
569 iface
, debugstr_w(pwcsName
), pstgPriority
,
570 grfMode
, snbExclude
, reserved
, ppstg
);
572 if ((pwcsName
==NULL
) || (ppstg
==0) )
578 if (This
->openFlags
& STGM_SIMPLE
)
580 res
= STG_E_INVALIDFUNCTION
;
585 if (snbExclude
!= NULL
)
587 res
= STG_E_INVALIDPARAMETER
;
591 if ( FAILED( validateSTGM(grfMode
) ))
593 res
= STG_E_INVALIDFLAG
;
600 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
601 (grfMode
& STGM_DELETEONRELEASE
) ||
602 (grfMode
& STGM_PRIORITY
) )
604 res
= STG_E_INVALIDFUNCTION
;
609 return STG_E_REVERTED
;
612 * Check that we're compatible with the parent's storage mode,
613 * but only if we are not transacted
615 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
616 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
618 res
= STG_E_ACCESSDENIED
;
625 storageEntryRef
= findElement(
627 This
->storageDirEntry
,
631 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
632 (currentEntry
.stgType
==STGTY_STORAGE
) )
634 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
636 /* A single storage cannot be opened a second time. */
637 res
= STG_E_ACCESSDENIED
;
641 newStorage
= StorageInternalImpl_Construct(
648 if (grfMode
& STGM_TRANSACTED
)
650 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
654 HeapFree(GetProcessHeap(), 0, newStorage
);
658 *ppstg
= &newTransactedStorage
->IStorage_iface
;
662 *ppstg
= &newStorage
->base
.IStorage_iface
;
665 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
671 res
= STG_E_INSUFFICIENTMEMORY
;
675 res
= STG_E_FILENOTFOUND
;
678 TRACE("<-- %08x\n", res
);
682 /************************************************************************
683 * Storage32BaseImpl_EnumElements (IStorage)
685 * This method will create an enumerator object that can be used to
686 * retrieve information about all the elements in the storage object.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
692 DWORD reserved1
, /* [in] */
693 void* reserved2
, /* [size_is][unique][in] */
694 DWORD reserved3
, /* [in] */
695 IEnumSTATSTG
** ppenum
) /* [out] */
697 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
698 IEnumSTATSTGImpl
* newEnum
;
700 TRACE("(%p, %d, %p, %d, %p)\n",
701 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
707 return STG_E_REVERTED
;
709 newEnum
= IEnumSTATSTGImpl_Construct(
711 This
->storageDirEntry
);
715 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
719 return E_OUTOFMEMORY
;
722 /************************************************************************
723 * Storage32BaseImpl_Stat (IStorage)
725 * This method will retrieve information about this storage object.
727 * See Windows documentation for more details on IStorage methods.
729 static HRESULT WINAPI
StorageBaseImpl_Stat(
731 STATSTG
* pstatstg
, /* [out] */
732 DWORD grfStatFlag
) /* [in] */
734 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
735 DirEntry currentEntry
;
736 HRESULT res
= STG_E_UNKNOWN
;
738 TRACE("(%p, %p, %x)\n",
739 iface
, pstatstg
, grfStatFlag
);
749 res
= STG_E_REVERTED
;
753 res
= StorageBaseImpl_ReadDirEntry(
755 This
->storageDirEntry
,
760 StorageUtl_CopyDirEntryToSTATSTG(
766 pstatstg
->grfMode
= This
->openFlags
;
767 pstatstg
->grfStateBits
= This
->stateBits
;
773 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
);
775 TRACE("<-- %08x\n", res
);
779 /************************************************************************
780 * Storage32BaseImpl_RenameElement (IStorage)
782 * This method will rename the specified element.
784 * See Windows documentation for more details on IStorage methods.
786 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
788 const OLECHAR
* pwcsOldName
, /* [in] */
789 const OLECHAR
* pwcsNewName
) /* [in] */
791 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
792 DirEntry currentEntry
;
793 DirRef currentEntryRef
;
795 TRACE("(%p, %s, %s)\n",
796 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
799 return STG_E_REVERTED
;
801 currentEntryRef
= findElement(This
,
802 This
->storageDirEntry
,
806 if (currentEntryRef
!= DIRENTRY_NULL
)
809 * There is already an element with the new name
811 return STG_E_FILEALREADYEXISTS
;
815 * Search for the old element name
817 currentEntryRef
= findElement(This
,
818 This
->storageDirEntry
,
822 if (currentEntryRef
!= DIRENTRY_NULL
)
824 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
825 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
827 WARN("Element is already open; cannot rename.\n");
828 return STG_E_ACCESSDENIED
;
831 /* Remove the element from its current position in the tree */
832 removeFromTree(This
, This
->storageDirEntry
,
835 /* Change the name of the element */
836 strcpyW(currentEntry
.name
, pwcsNewName
);
838 /* Delete any sibling links */
839 currentEntry
.leftChild
= DIRENTRY_NULL
;
840 currentEntry
.rightChild
= DIRENTRY_NULL
;
842 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
845 /* Insert the element in a new position in the tree */
846 insertIntoTree(This
, This
->storageDirEntry
,
852 * There is no element with the old name
854 return STG_E_FILENOTFOUND
;
857 return StorageBaseImpl_Flush(This
);
860 /************************************************************************
861 * Storage32BaseImpl_CreateStream (IStorage)
863 * This method will create a stream object within this storage
865 * See Windows documentation for more details on IStorage methods.
867 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
869 const OLECHAR
* pwcsName
, /* [string][in] */
870 DWORD grfMode
, /* [in] */
871 DWORD reserved1
, /* [in] */
872 DWORD reserved2
, /* [in] */
873 IStream
** ppstm
) /* [out] */
875 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
876 StgStreamImpl
* newStream
;
877 DirEntry currentEntry
, newStreamEntry
;
878 DirRef currentEntryRef
, newStreamEntryRef
;
881 TRACE("(%p, %s, %x, %d, %d, %p)\n",
882 iface
, debugstr_w(pwcsName
), grfMode
,
883 reserved1
, reserved2
, ppstm
);
886 return STG_E_INVALIDPOINTER
;
889 return STG_E_INVALIDNAME
;
891 if (reserved1
|| reserved2
)
892 return STG_E_INVALIDPARAMETER
;
894 if ( FAILED( validateSTGM(grfMode
) ))
895 return STG_E_INVALIDFLAG
;
897 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
898 return STG_E_INVALIDFLAG
;
901 return STG_E_REVERTED
;
906 if ((grfMode
& STGM_DELETEONRELEASE
) ||
907 (grfMode
& STGM_TRANSACTED
))
908 return STG_E_INVALIDFUNCTION
;
911 * Don't worry about permissions in transacted mode, as we can always write
912 * changes; we just can't always commit them.
914 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
915 /* Can't create a stream on read-only storage */
916 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
917 return STG_E_ACCESSDENIED
;
919 /* Can't create a stream with greater access than the parent. */
920 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
921 return STG_E_ACCESSDENIED
;
924 if(This
->openFlags
& STGM_SIMPLE
)
925 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
929 currentEntryRef
= findElement(This
,
930 This
->storageDirEntry
,
934 if (currentEntryRef
!= DIRENTRY_NULL
)
937 * An element with this name already exists
939 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
941 IStorage_DestroyElement(iface
, pwcsName
);
944 return STG_E_FILEALREADYEXISTS
;
948 * memset the empty entry
950 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
952 newStreamEntry
.sizeOfNameString
=
953 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
955 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
956 return STG_E_INVALIDNAME
;
958 strcpyW(newStreamEntry
.name
, pwcsName
);
960 newStreamEntry
.stgType
= STGTY_STREAM
;
961 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
962 newStreamEntry
.size
.u
.LowPart
= 0;
963 newStreamEntry
.size
.u
.HighPart
= 0;
965 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
966 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
967 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
969 /* call CoFileTime to get the current time
974 /* newStreamEntry.clsid */
977 * Create an entry with the new data
979 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
984 * Insert the new entry in the parent storage's tree.
988 This
->storageDirEntry
,
992 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
997 * Open the stream to return it.
999 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1003 *ppstm
= &newStream
->IStream_iface
;
1004 IStream_AddRef(*ppstm
);
1008 return STG_E_INSUFFICIENTMEMORY
;
1011 return StorageBaseImpl_Flush(This
);
1014 /************************************************************************
1015 * Storage32BaseImpl_SetClass (IStorage)
1017 * This method will write the specified CLSID in the directory entry of this
1020 * See Windows documentation for more details on IStorage methods.
1022 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1024 REFCLSID clsid
) /* [in] */
1026 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1028 DirEntry currentEntry
;
1030 TRACE("(%p, %p)\n", iface
, clsid
);
1033 return STG_E_REVERTED
;
1035 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1036 This
->storageDirEntry
,
1038 if (SUCCEEDED(hRes
))
1040 currentEntry
.clsid
= *clsid
;
1042 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1043 This
->storageDirEntry
,
1047 if (SUCCEEDED(hRes
))
1048 hRes
= StorageBaseImpl_Flush(This
);
1053 /************************************************************************
1054 ** Storage32Impl implementation
1057 /************************************************************************
1058 * Storage32BaseImpl_CreateStorage (IStorage)
1060 * This method will create the storage object within the provided storage.
1062 * See Windows documentation for more details on IStorage methods.
1064 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1066 const OLECHAR
*pwcsName
, /* [string][in] */
1067 DWORD grfMode
, /* [in] */
1068 DWORD reserved1
, /* [in] */
1069 DWORD reserved2
, /* [in] */
1070 IStorage
**ppstg
) /* [out] */
1072 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1074 DirEntry currentEntry
;
1076 DirRef currentEntryRef
;
1080 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1081 iface
, debugstr_w(pwcsName
), grfMode
,
1082 reserved1
, reserved2
, ppstg
);
1085 return STG_E_INVALIDPOINTER
;
1087 if (This
->openFlags
& STGM_SIMPLE
)
1089 return STG_E_INVALIDFUNCTION
;
1093 return STG_E_INVALIDNAME
;
1097 if ( FAILED( validateSTGM(grfMode
) ) ||
1098 (grfMode
& STGM_DELETEONRELEASE
) )
1100 WARN("bad grfMode: 0x%x\n", grfMode
);
1101 return STG_E_INVALIDFLAG
;
1105 return STG_E_REVERTED
;
1108 * Check that we're compatible with the parent's storage mode
1110 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1111 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1113 WARN("access denied\n");
1114 return STG_E_ACCESSDENIED
;
1117 currentEntryRef
= findElement(This
,
1118 This
->storageDirEntry
,
1122 if (currentEntryRef
!= DIRENTRY_NULL
)
1125 * An element with this name already exists
1127 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1128 ((This
->openFlags
& STGM_TRANSACTED
) ||
1129 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1131 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1137 WARN("file already exists\n");
1138 return STG_E_FILEALREADYEXISTS
;
1141 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1142 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1144 WARN("read-only storage\n");
1145 return STG_E_ACCESSDENIED
;
1148 memset(&newEntry
, 0, sizeof(DirEntry
));
1150 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1152 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME
;
1158 strcpyW(newEntry
.name
, pwcsName
);
1160 newEntry
.stgType
= STGTY_STORAGE
;
1161 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1162 newEntry
.size
.u
.LowPart
= 0;
1163 newEntry
.size
.u
.HighPart
= 0;
1165 newEntry
.leftChild
= DIRENTRY_NULL
;
1166 newEntry
.rightChild
= DIRENTRY_NULL
;
1167 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1169 /* call CoFileTime to get the current time
1174 /* newEntry.clsid */
1177 * Create a new directory entry for the storage
1179 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1184 * Insert the new directory entry into the parent storage's tree
1186 hr
= insertIntoTree(
1188 This
->storageDirEntry
,
1192 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1197 * Open it to get a pointer to return.
1199 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1201 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1207 hr
= StorageBaseImpl_Flush(This
);
1213 /***************************************************************************
1217 * Reserve a directory entry in the file and initialize it.
1219 static HRESULT
StorageImpl_CreateDirEntry(
1220 StorageBaseImpl
*base
,
1221 const DirEntry
*newData
,
1224 StorageImpl
*storage
= (StorageImpl
*)base
;
1225 ULONG currentEntryIndex
= 0;
1226 ULONG newEntryIndex
= DIRENTRY_NULL
;
1228 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1229 WORD sizeOfNameString
;
1233 hr
= StorageImpl_ReadRawDirEntry(storage
,
1239 StorageUtl_ReadWord(
1241 OFFSET_PS_NAMELENGTH
,
1244 if (sizeOfNameString
== 0)
1247 * The entry exists and is available, we found it.
1249 newEntryIndex
= currentEntryIndex
;
1255 * We exhausted the directory entries, we will create more space below
1257 newEntryIndex
= currentEntryIndex
;
1259 currentEntryIndex
++;
1261 } while (newEntryIndex
== DIRENTRY_NULL
);
1264 * grow the directory stream
1268 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1269 ULARGE_INTEGER newSize
;
1271 ULONG lastEntry
= 0;
1272 ULONG blockCount
= 0;
1275 * obtain the new count of blocks in the directory stream
1277 blockCount
= BlockChainStream_GetCount(
1278 storage
->rootBlockChain
)+1;
1281 * initialize the size used by the directory stream
1283 newSize
.u
.HighPart
= 0;
1284 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1287 * add a block to the directory stream
1289 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1292 * memset the empty entry in order to initialize the unused newly
1295 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1300 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1303 entryIndex
= newEntryIndex
+ 1;
1304 entryIndex
< lastEntry
;
1307 StorageImpl_WriteRawDirEntry(
1313 StorageImpl_SaveFileHeader(storage
);
1316 UpdateRawDirEntry(currentData
, newData
);
1318 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1321 *index
= newEntryIndex
;
1326 /***************************************************************************
1330 * Mark a directory entry in the file as free.
1332 static HRESULT
StorageImpl_DestroyDirEntry(
1333 StorageBaseImpl
*base
,
1336 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1337 StorageImpl
*storage
= (StorageImpl
*)base
;
1339 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1341 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1345 /****************************************************************************
1349 * Case insensitive comparison of DirEntry.name by first considering
1352 * Returns <0 when name1 < name2
1353 * >0 when name1 > name2
1354 * 0 when name1 == name2
1356 static LONG
entryNameCmp(
1357 const OLECHAR
*name1
,
1358 const OLECHAR
*name2
)
1360 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1362 while (diff
== 0 && *name1
!= 0)
1365 * We compare the string themselves only when they are of the same length
1367 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1373 /****************************************************************************
1377 * Add a directory entry to a storage
1379 static HRESULT
insertIntoTree(
1380 StorageBaseImpl
*This
,
1381 DirRef parentStorageIndex
,
1382 DirRef newEntryIndex
)
1384 DirEntry currentEntry
;
1388 * Read the inserted entry
1390 StorageBaseImpl_ReadDirEntry(This
,
1395 * Read the storage entry
1397 StorageBaseImpl_ReadDirEntry(This
,
1401 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1404 * The root storage contains some element, therefore, start the research
1405 * for the appropriate location.
1408 DirRef current
, next
, previous
, currentEntryId
;
1411 * Keep a reference to the root of the storage's element tree
1413 currentEntryId
= currentEntry
.dirRootEntry
;
1418 StorageBaseImpl_ReadDirEntry(This
,
1419 currentEntry
.dirRootEntry
,
1422 previous
= currentEntry
.leftChild
;
1423 next
= currentEntry
.rightChild
;
1424 current
= currentEntryId
;
1428 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1432 if (previous
!= DIRENTRY_NULL
)
1434 StorageBaseImpl_ReadDirEntry(This
,
1441 currentEntry
.leftChild
= newEntryIndex
;
1442 StorageBaseImpl_WriteDirEntry(This
,
1450 if (next
!= DIRENTRY_NULL
)
1452 StorageBaseImpl_ReadDirEntry(This
,
1459 currentEntry
.rightChild
= newEntryIndex
;
1460 StorageBaseImpl_WriteDirEntry(This
,
1469 * Trying to insert an item with the same name in the
1470 * subtree structure.
1472 return STG_E_FILEALREADYEXISTS
;
1475 previous
= currentEntry
.leftChild
;
1476 next
= currentEntry
.rightChild
;
1482 * The storage is empty, make the new entry the root of its element tree
1484 currentEntry
.dirRootEntry
= newEntryIndex
;
1485 StorageBaseImpl_WriteDirEntry(This
,
1493 /****************************************************************************
1497 * Find and read the element of a storage with the given name.
1499 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1500 const OLECHAR
*name
, DirEntry
*data
)
1502 DirRef currentEntry
;
1504 /* Read the storage entry to find the root of the tree. */
1505 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1507 currentEntry
= data
->dirRootEntry
;
1509 while (currentEntry
!= DIRENTRY_NULL
)
1513 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1515 cmp
= entryNameCmp(name
, data
->name
);
1522 currentEntry
= data
->leftChild
;
1525 currentEntry
= data
->rightChild
;
1528 return currentEntry
;
1531 /****************************************************************************
1535 * Find and read the binary tree parent of the element with the given name.
1537 * If there is no such element, find a place where it could be inserted and
1538 * return STG_E_FILENOTFOUND.
1540 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1541 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1547 /* Read the storage entry to find the root of the tree. */
1548 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1550 *parentEntry
= storageEntry
;
1551 *relation
= DIRENTRY_RELATION_DIR
;
1553 childEntry
= parentData
->dirRootEntry
;
1555 while (childEntry
!= DIRENTRY_NULL
)
1559 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1561 cmp
= entryNameCmp(childName
, childData
.name
);
1569 *parentData
= childData
;
1570 *parentEntry
= childEntry
;
1571 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1573 childEntry
= parentData
->leftChild
;
1578 *parentData
= childData
;
1579 *parentEntry
= childEntry
;
1580 *relation
= DIRENTRY_RELATION_NEXT
;
1582 childEntry
= parentData
->rightChild
;
1586 if (childEntry
== DIRENTRY_NULL
)
1587 return STG_E_FILENOTFOUND
;
1593 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1594 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1595 SNB snbExclude
, IStorage
*pstgDest
);
1597 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1598 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1599 SNB snbExclude
, IStorage
*pstgDest
)
1605 IStream
*pstrChild
, *pstrTmp
;
1608 if (srcEntry
== DIRENTRY_NULL
)
1611 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1618 WCHAR
**snb
= snbExclude
;
1620 while ( *snb
!= NULL
&& !skip
)
1622 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1630 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1633 * create a new storage in destination storage
1635 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1636 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1641 * if it already exist, don't create a new one use this one
1643 if (hr
== STG_E_FILEALREADYEXISTS
)
1645 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1646 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1647 NULL
, 0, &pstgTmp
);
1652 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1653 skip_stream
, NULL
, pstgTmp
);
1655 IStorage_Release(pstgTmp
);
1658 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1661 * create a new stream in destination storage. If the stream already
1662 * exist, it will be deleted and a new one will be created.
1664 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1665 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1669 * open child stream storage. This operation must succeed even if the
1670 * stream is already open, so we use internal functions to do it.
1674 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1678 pstrChild
= &streamimpl
->IStream_iface
;
1680 IStream_AddRef(pstrChild
);
1692 * Get the size of the source stream
1694 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1697 * Set the size of the destination stream.
1699 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1704 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1707 IStream_Release( pstrChild
);
1710 IStream_Release( pstrTmp
);
1716 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1717 skip_stream
, snbExclude
, pstgDest
);
1720 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1721 skip_stream
, snbExclude
, pstgDest
);
1726 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1727 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1728 SNB snbExclude
, IStorage
*pstgDest
)
1733 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1736 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1739 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1740 skip_stream
, snbExclude
, pstgDest
);
1745 /*************************************************************************
1748 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1750 DWORD ciidExclude
, /* [in] */
1751 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1752 SNB snbExclude
, /* [unique][in] */
1753 IStorage
* pstgDest
) /* [unique][in] */
1755 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1757 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1760 TRACE("(%p, %d, %p, %p, %p)\n",
1761 iface
, ciidExclude
, rgiidExclude
,
1762 snbExclude
, pstgDest
);
1764 if ( pstgDest
== 0 )
1765 return STG_E_INVALIDPOINTER
;
1767 for(i
= 0; i
< ciidExclude
; ++i
)
1769 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1770 skip_storage
= TRUE
;
1771 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1774 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1779 /* Give up early if it looks like this would be infinitely recursive.
1780 * Oddly enough, this includes some cases that aren't really recursive, like
1781 * copying to a transacted child. */
1782 IStorage
*pstgDestAncestor
= pstgDest
;
1783 IStorage
*pstgDestAncestorChild
= NULL
;
1785 /* Go up the chain from the destination until we find the source storage. */
1786 while (pstgDestAncestor
!= iface
) {
1787 pstgDestAncestorChild
= pstgDest
;
1789 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1791 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1793 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1795 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1797 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1799 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1805 if (pstgDestAncestor
== iface
)
1809 if (pstgDestAncestorChild
&& snbExclude
)
1811 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1813 WCHAR
**snb
= snbExclude
;
1815 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1817 while ( *snb
!= NULL
&& fail
)
1819 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1826 return STG_E_ACCESSDENIED
;
1830 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1831 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1834 /*************************************************************************
1835 * MoveElementTo (IStorage)
1837 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1839 const OLECHAR
*pwcsName
, /* [string][in] */
1840 IStorage
*pstgDest
, /* [unique][in] */
1841 const OLECHAR
*pwcsNewName
,/* [string][in] */
1842 DWORD grfFlags
) /* [in] */
1844 FIXME("(%p %s %p %s %u): stub\n", iface
,
1845 debugstr_w(pwcsName
), pstgDest
,
1846 debugstr_w(pwcsNewName
), grfFlags
);
1850 /*************************************************************************
1853 * Ensures that any changes made to a storage object open in transacted mode
1854 * are reflected in the parent storage
1856 * In a non-transacted mode, this ensures all cached writes are completed.
1858 static HRESULT WINAPI
StorageImpl_Commit(
1860 DWORD grfCommitFlags
)/* [in] */
1862 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1863 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1864 return StorageBaseImpl_Flush(This
);
1867 /*************************************************************************
1870 * Discard all changes that have been made since the last commit operation
1872 static HRESULT WINAPI
StorageImpl_Revert(
1875 TRACE("(%p)\n", iface
);
1879 /*************************************************************************
1880 * DestroyElement (IStorage)
1882 * Strategy: This implementation is built this way for simplicity not for speed.
1883 * I always delete the topmost element of the enumeration and adjust
1884 * the deleted element pointer all the time. This takes longer to
1885 * do but allow to reinvoke DestroyElement whenever we encounter a
1886 * storage object. The optimisation resides in the usage of another
1887 * enumeration strategy that would give all the leaves of a storage
1888 * first. (postfix order)
1890 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1892 const OLECHAR
*pwcsName
)/* [string][in] */
1894 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1897 DirEntry entryToDelete
;
1898 DirRef entryToDeleteRef
;
1901 iface
, debugstr_w(pwcsName
));
1904 return STG_E_INVALIDPOINTER
;
1907 return STG_E_REVERTED
;
1909 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1910 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1911 return STG_E_ACCESSDENIED
;
1913 entryToDeleteRef
= findElement(
1915 This
->storageDirEntry
,
1919 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1921 return STG_E_FILENOTFOUND
;
1924 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1926 hr
= deleteStorageContents(
1931 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1933 hr
= deleteStreamContents(
1943 * Remove the entry from its parent storage
1945 hr
= removeFromTree(
1947 This
->storageDirEntry
,
1951 * Invalidate the entry
1954 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1957 hr
= StorageBaseImpl_Flush(This
);
1963 /******************************************************************************
1964 * Internal stream list handlers
1967 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1969 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1970 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1973 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1975 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1976 list_remove(&(strm
->StrmListEntry
));
1979 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1981 StgStreamImpl
*strm
;
1983 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1985 if (strm
->dirEntry
== streamEntry
)
1994 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1996 StorageInternalImpl
*childstg
;
1998 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2000 if (childstg
->base
.storageDirEntry
== storageEntry
)
2009 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2011 struct list
*cur
, *cur2
;
2012 StgStreamImpl
*strm
=NULL
;
2013 StorageInternalImpl
*childstg
=NULL
;
2015 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2016 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2017 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2018 strm
->parentStorage
= NULL
;
2022 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2023 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2024 StorageBaseImpl_Invalidate( &childstg
->base
);
2027 if (stg
->transactedChild
)
2029 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2031 stg
->transactedChild
= NULL
;
2036 /*********************************************************************
2040 * Delete the contents of a storage entry.
2043 static HRESULT
deleteStorageContents(
2044 StorageBaseImpl
*parentStorage
,
2045 DirRef indexToDelete
,
2046 DirEntry entryDataToDelete
)
2048 IEnumSTATSTG
*elements
= 0;
2049 IStorage
*childStorage
= 0;
2050 STATSTG currentElement
;
2052 HRESULT destroyHr
= S_OK
;
2053 StorageInternalImpl
*stg
, *stg2
;
2055 /* Invalidate any open storage objects. */
2056 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2058 if (stg
->base
.storageDirEntry
== indexToDelete
)
2060 StorageBaseImpl_Invalidate(&stg
->base
);
2065 * Open the storage and enumerate it
2067 hr
= IStorage_OpenStorage(
2068 &parentStorage
->IStorage_iface
,
2069 entryDataToDelete
.name
,
2071 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2082 * Enumerate the elements
2084 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2089 * Obtain the next element
2091 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2094 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2096 CoTaskMemFree(currentElement
.pwcsName
);
2100 * We need to Reset the enumeration every time because we delete elements
2101 * and the enumeration could be invalid
2103 IEnumSTATSTG_Reset(elements
);
2105 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2107 IStorage_Release(childStorage
);
2108 IEnumSTATSTG_Release(elements
);
2113 /*********************************************************************
2117 * Perform the deletion of a stream's data
2120 static HRESULT
deleteStreamContents(
2121 StorageBaseImpl
*parentStorage
,
2122 DirRef indexToDelete
,
2123 DirEntry entryDataToDelete
)
2127 ULARGE_INTEGER size
;
2128 StgStreamImpl
*strm
, *strm2
;
2130 /* Invalidate any open stream objects. */
2131 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2133 if (strm
->dirEntry
== indexToDelete
)
2135 TRACE("Stream deleted %p\n", strm
);
2136 strm
->parentStorage
= NULL
;
2137 list_remove(&strm
->StrmListEntry
);
2141 size
.u
.HighPart
= 0;
2144 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2145 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2155 hr
= IStream_SetSize(pis
, size
);
2163 * Release the stream object.
2165 IStream_Release(pis
);
2170 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2174 case DIRENTRY_RELATION_PREVIOUS
:
2175 entry
->leftChild
= new_target
;
2177 case DIRENTRY_RELATION_NEXT
:
2178 entry
->rightChild
= new_target
;
2180 case DIRENTRY_RELATION_DIR
:
2181 entry
->dirRootEntry
= new_target
;
2188 /*************************************************************************
2192 * This method removes a directory entry from its parent storage tree without
2193 * freeing any resources attached to it.
2195 static HRESULT
removeFromTree(
2196 StorageBaseImpl
*This
,
2197 DirRef parentStorageIndex
,
2198 DirRef deletedIndex
)
2200 DirEntry entryToDelete
;
2201 DirEntry parentEntry
;
2202 DirRef parentEntryRef
;
2203 ULONG typeOfRelation
;
2206 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2212 * Find the element that links to the one we want to delete.
2214 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2215 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2220 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2223 * Replace the deleted entry with its left child
2225 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2227 hr
= StorageBaseImpl_WriteDirEntry(
2236 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2239 * We need to reinsert the right child somewhere. We already know it and
2240 * its children are greater than everything in the left tree, so we
2241 * insert it at the rightmost point in the left tree.
2243 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2244 DirEntry newRightChildParentEntry
;
2248 hr
= StorageBaseImpl_ReadDirEntry(
2250 newRightChildParent
,
2251 &newRightChildParentEntry
);
2257 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2258 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2259 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2261 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2263 hr
= StorageBaseImpl_WriteDirEntry(
2265 newRightChildParent
,
2266 &newRightChildParentEntry
);
2276 * Replace the deleted entry with its right child
2278 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2280 hr
= StorageBaseImpl_WriteDirEntry(
2294 /******************************************************************************
2295 * SetElementTimes (IStorage)
2297 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2299 const OLECHAR
*pwcsName
,/* [string][in] */
2300 const FILETIME
*pctime
, /* [in] */
2301 const FILETIME
*patime
, /* [in] */
2302 const FILETIME
*pmtime
) /* [in] */
2304 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2308 /******************************************************************************
2309 * SetStateBits (IStorage)
2311 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2313 DWORD grfStateBits
,/* [in] */
2314 DWORD grfMask
) /* [in] */
2316 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2319 return STG_E_REVERTED
;
2321 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2325 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2326 DirRef index
, const DirEntry
*data
)
2328 StorageImpl
*This
= (StorageImpl
*)base
;
2329 return StorageImpl_WriteDirEntry(This
, index
, data
);
2332 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2333 DirRef index
, DirEntry
*data
)
2335 StorageImpl
*This
= (StorageImpl
*)base
;
2336 return StorageImpl_ReadDirEntry(This
, index
, data
);
2339 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2343 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2345 if (!This
->blockChainCache
[i
])
2347 return &This
->blockChainCache
[i
];
2351 i
= This
->blockChainToEvict
;
2353 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2354 This
->blockChainCache
[i
] = NULL
;
2356 This
->blockChainToEvict
++;
2357 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2358 This
->blockChainToEvict
= 0;
2360 return &This
->blockChainCache
[i
];
2363 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2366 int i
, free_index
=-1;
2368 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2370 if (!This
->blockChainCache
[i
])
2372 if (free_index
== -1) free_index
= i
;
2374 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2376 return &This
->blockChainCache
[i
];
2380 if (free_index
== -1)
2382 free_index
= This
->blockChainToEvict
;
2384 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2385 This
->blockChainCache
[free_index
] = NULL
;
2387 This
->blockChainToEvict
++;
2388 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2389 This
->blockChainToEvict
= 0;
2392 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2393 return &This
->blockChainCache
[free_index
];
2396 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2400 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2402 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2404 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2405 This
->blockChainCache
[i
] = NULL
;
2411 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2412 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2414 StorageImpl
*This
= (StorageImpl
*)base
;
2419 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2420 if (FAILED(hr
)) return hr
;
2422 if (data
.size
.QuadPart
== 0)
2428 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2430 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2437 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2439 SmallBlockChainStream
*stream
;
2441 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2442 if (!stream
) return E_OUTOFMEMORY
;
2444 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2446 SmallBlockChainStream_Destroy(stream
);
2452 BlockChainStream
*stream
= NULL
;
2454 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2455 if (!stream
) return E_OUTOFMEMORY
;
2457 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2463 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2464 ULARGE_INTEGER newsize
)
2466 StorageImpl
*This
= (StorageImpl
*)base
;
2469 SmallBlockChainStream
*smallblock
=NULL
;
2470 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2472 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2473 if (FAILED(hr
)) return hr
;
2475 /* In simple mode keep the stream size above the small block limit */
2476 if (This
->base
.openFlags
& STGM_SIMPLE
)
2477 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2479 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2482 /* Create a block chain object of the appropriate type */
2483 if (data
.size
.QuadPart
== 0)
2485 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2487 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2488 if (!smallblock
) return E_OUTOFMEMORY
;
2492 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2493 bigblock
= *pbigblock
;
2494 if (!bigblock
) return E_OUTOFMEMORY
;
2497 else if (data
.size
.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 /* Change the block chain type if necessary. */
2510 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2512 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2515 SmallBlockChainStream_Destroy(smallblock
);
2519 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2520 *pbigblock
= bigblock
;
2522 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2524 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2529 /* Set the size of the block chain. */
2532 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2533 SmallBlockChainStream_Destroy(smallblock
);
2537 BlockChainStream_SetSize(bigblock
, newsize
);
2540 /* Set the size in the directory entry. */
2541 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2544 data
.size
= newsize
;
2546 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2551 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2552 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2554 StorageImpl
*This
= (StorageImpl
*)base
;
2557 ULARGE_INTEGER newSize
;
2559 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2560 if (FAILED(hr
)) return hr
;
2562 /* Grow the stream if necessary */
2563 newSize
.QuadPart
= 0;
2564 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2566 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2568 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2572 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2573 if (FAILED(hr
)) return hr
;
2576 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2578 SmallBlockChainStream
*stream
;
2580 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2581 if (!stream
) return E_OUTOFMEMORY
;
2583 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2585 SmallBlockChainStream_Destroy(stream
);
2591 BlockChainStream
*stream
;
2593 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2594 if (!stream
) return E_OUTOFMEMORY
;
2596 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2600 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2603 StorageImpl
*This
= (StorageImpl
*)base
;
2604 DirEntry dst_data
, src_data
;
2607 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2610 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2614 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2615 dst_data
.startingBlock
= src_data
.startingBlock
;
2616 dst_data
.size
= src_data
.size
;
2618 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2624 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2626 StorageImpl
*This
= (StorageImpl
*) iface
;
2630 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2632 *result
= statstg
.pwcsName
;
2637 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
2639 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2640 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
2643 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
2645 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2646 return IStorage_AddRef(&This
->IStorage_iface
);
2649 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
2651 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2652 return IStorage_Release(&This
->IStorage_iface
);
2655 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
2657 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2658 FIXME("(%p)->(%d): stub\n", This
, timeout
);
2662 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
2664 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2665 FIXME("(%p): stub\n", This
);
2669 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
2671 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2672 FIXME("(%p): stub\n", This
);
2676 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
2678 directwriterlock_QueryInterface
,
2679 directwriterlock_AddRef
,
2680 directwriterlock_Release
,
2681 directwriterlock_WaitForWriteAccess
,
2682 directwriterlock_ReleaseWriteAccess
,
2683 directwriterlock_HaveWriteAccess
2687 * Virtual function table for the IStorage32Impl class.
2689 static const IStorageVtbl Storage32Impl_Vtbl
=
2691 StorageBaseImpl_QueryInterface
,
2692 StorageBaseImpl_AddRef
,
2693 StorageBaseImpl_Release
,
2694 StorageBaseImpl_CreateStream
,
2695 StorageBaseImpl_OpenStream
,
2696 StorageBaseImpl_CreateStorage
,
2697 StorageBaseImpl_OpenStorage
,
2698 StorageBaseImpl_CopyTo
,
2699 StorageBaseImpl_MoveElementTo
,
2702 StorageBaseImpl_EnumElements
,
2703 StorageBaseImpl_DestroyElement
,
2704 StorageBaseImpl_RenameElement
,
2705 StorageBaseImpl_SetElementTimes
,
2706 StorageBaseImpl_SetClass
,
2707 StorageBaseImpl_SetStateBits
,
2708 StorageBaseImpl_Stat
2711 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2713 StorageImpl_Destroy
,
2714 StorageImpl_Invalidate
,
2716 StorageImpl_GetFilename
,
2717 StorageImpl_CreateDirEntry
,
2718 StorageImpl_BaseWriteDirEntry
,
2719 StorageImpl_BaseReadDirEntry
,
2720 StorageImpl_DestroyDirEntry
,
2721 StorageImpl_StreamReadAt
,
2722 StorageImpl_StreamWriteAt
,
2723 StorageImpl_StreamSetSize
,
2724 StorageImpl_StreamLink
2727 static HRESULT
StorageImpl_Construct(
2735 StorageImpl
** result
)
2739 DirEntry currentEntry
;
2740 DirRef currentEntryRef
;
2742 if ( FAILED( validateSTGM(openFlags
) ))
2743 return STG_E_INVALIDFLAG
;
2745 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2747 return E_OUTOFMEMORY
;
2749 memset(This
, 0, sizeof(StorageImpl
));
2751 list_init(&This
->base
.strmHead
);
2753 list_init(&This
->base
.storageHead
);
2755 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
2756 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
2757 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
2758 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2759 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2761 This
->base
.create
= create
;
2763 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
2764 This
->base
.lockingrole
= SWMR_Writer
;
2765 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
2766 This
->base
.lockingrole
= SWMR_Reader
;
2768 This
->base
.lockingrole
= SWMR_None
;
2770 This
->base
.reverted
= FALSE
;
2773 * Initialize the big block cache.
2775 This
->bigBlockSize
= sector_size
;
2776 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2778 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2781 This
->lockBytes
= pLkbyt
;
2782 ILockBytes_AddRef(pLkbyt
);
2790 ULARGE_INTEGER size
;
2791 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2793 /* Discard any existing data. */
2795 ILockBytes_SetSize(This
->lockBytes
, size
);
2798 * Initialize all header variables:
2799 * - The big block depot consists of one block and it is at block 0
2800 * - The directory table starts at block 1
2801 * - There is no small block depot
2803 memset( This
->bigBlockDepotStart
,
2805 sizeof(This
->bigBlockDepotStart
));
2807 This
->bigBlockDepotCount
= 1;
2808 This
->bigBlockDepotStart
[0] = 0;
2809 This
->rootStartBlock
= 1;
2810 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2811 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2812 if (sector_size
== 4096)
2813 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2815 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2816 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2817 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2818 This
->extBigBlockDepotCount
= 0;
2820 StorageImpl_SaveFileHeader(This
);
2823 * Add one block for the big block depot and one block for the directory table
2825 size
.u
.HighPart
= 0;
2826 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2827 ILockBytes_SetSize(This
->lockBytes
, size
);
2830 * Initialize the big block depot
2832 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2833 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2834 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2835 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2840 * Load the header for the file.
2842 hr
= StorageImpl_LoadFileHeader(This
);
2851 * There is no block depot cached yet.
2853 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2854 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
2857 * Start searching for free blocks with block 0.
2859 This
->prevFreeBlock
= 0;
2861 This
->firstFreeSmallBlock
= 0;
2863 /* Read the extended big block depot locations. */
2864 if (This
->extBigBlockDepotCount
!= 0)
2866 ULONG current_block
= This
->extBigBlockDepotStart
;
2867 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2870 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2871 if (!This
->extBigBlockDepotLocations
)
2877 This
->extBigBlockDepotLocationsSize
= cache_size
;
2879 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2881 if (current_block
== BLOCK_END_OF_CHAIN
)
2883 WARN("File has too few extended big block depot blocks.\n");
2884 hr
= STG_E_DOCFILECORRUPT
;
2887 This
->extBigBlockDepotLocations
[i
] = current_block
;
2888 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2893 This
->extBigBlockDepotLocations
= NULL
;
2894 This
->extBigBlockDepotLocationsSize
= 0;
2898 * Create the block chain abstractions.
2900 if(!(This
->rootBlockChain
=
2901 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2903 hr
= STG_E_READFAULT
;
2907 if(!(This
->smallBlockDepotChain
=
2908 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2911 hr
= STG_E_READFAULT
;
2916 * Write the root storage entry (memory only)
2920 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2923 * Initialize the directory table
2925 memset(&rootEntry
, 0, sizeof(rootEntry
));
2926 strcpyW(rootEntry
.name
, rootentryW
);
2927 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
2928 rootEntry
.stgType
= STGTY_ROOT
;
2929 rootEntry
.leftChild
= DIRENTRY_NULL
;
2930 rootEntry
.rightChild
= DIRENTRY_NULL
;
2931 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2932 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2933 rootEntry
.size
.u
.HighPart
= 0;
2934 rootEntry
.size
.u
.LowPart
= 0;
2936 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2940 * Find the ID of the root storage.
2942 currentEntryRef
= 0;
2946 hr
= StorageImpl_ReadDirEntry(
2953 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2954 (currentEntry
.stgType
== STGTY_ROOT
) )
2956 This
->base
.storageDirEntry
= currentEntryRef
;
2962 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2966 hr
= STG_E_READFAULT
;
2971 * Create the block chain abstraction for the small block root chain.
2973 if(!(This
->smallBlockRootChain
=
2974 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2976 hr
= STG_E_READFAULT
;
2982 IStorage_Release(&This
->base
.IStorage_iface
);
2987 StorageImpl_Flush(&This
->base
);
2994 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2996 StorageImpl
*This
= (StorageImpl
*) iface
;
2998 StorageBaseImpl_DeleteAll(&This
->base
);
3000 This
->base
.reverted
= TRUE
;
3003 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3005 StorageImpl
*This
= (StorageImpl
*) iface
;
3007 TRACE("(%p)\n", This
);
3009 StorageImpl_Flush(iface
);
3011 StorageImpl_Invalidate(iface
);
3013 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3015 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3016 BlockChainStream_Destroy(This
->rootBlockChain
);
3017 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3019 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3020 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3022 if (This
->lockBytes
)
3023 ILockBytes_Release(This
->lockBytes
);
3024 HeapFree(GetProcessHeap(), 0, This
);
3027 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3029 StorageImpl
*This
= (StorageImpl
*)storage
;
3032 TRACE("(%p)\n", This
);
3034 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3037 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3040 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3042 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3043 if (This
->blockChainCache
[i
])
3044 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3047 hr
= ILockBytes_Flush(This
->lockBytes
);
3052 /******************************************************************************
3053 * Storage32Impl_GetNextFreeBigBlock
3055 * Returns the index of the next free big block.
3056 * If the big block depot is filled, this method will enlarge it.
3059 static ULONG
StorageImpl_GetNextFreeBigBlock(
3062 ULONG depotBlockIndexPos
;
3063 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3064 ULONG depotBlockOffset
;
3065 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3066 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3068 ULONG freeBlock
= BLOCK_UNUSED
;
3070 ULARGE_INTEGER neededSize
;
3073 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3074 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3077 * Scan the entire big block depot until we find a block marked free
3079 while (nextBlockIndex
!= BLOCK_UNUSED
)
3081 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3083 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3086 * Grow the primary depot.
3088 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3090 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3093 * Add a block depot.
3095 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3096 This
->bigBlockDepotCount
++;
3097 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3100 * Flag it as a block depot.
3102 StorageImpl_SetNextBlockInChain(This
,
3106 /* Save new header information.
3108 StorageImpl_SaveFileHeader(This
);
3113 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3115 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3118 * Grow the extended depot.
3120 ULONG extIndex
= BLOCK_UNUSED
;
3121 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3122 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3124 if (extBlockOffset
== 0)
3126 /* We need an extended block.
3128 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3129 This
->extBigBlockDepotCount
++;
3130 depotBlockIndexPos
= extIndex
+ 1;
3133 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3136 * Add a block depot and mark it in the extended block.
3138 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3139 This
->bigBlockDepotCount
++;
3140 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3142 /* Flag the block depot.
3144 StorageImpl_SetNextBlockInChain(This
,
3148 /* If necessary, flag the extended depot block.
3150 if (extIndex
!= BLOCK_UNUSED
)
3151 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3153 /* Save header information.
3155 StorageImpl_SaveFileHeader(This
);
3159 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3163 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3164 ( nextBlockIndex
!= BLOCK_UNUSED
))
3166 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3168 if (nextBlockIndex
== BLOCK_UNUSED
)
3170 freeBlock
= (depotIndex
* blocksPerDepot
) +
3171 (depotBlockOffset
/sizeof(ULONG
));
3174 depotBlockOffset
+= sizeof(ULONG
);
3179 depotBlockOffset
= 0;
3183 * make sure that the block physically exists before using it
3185 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3187 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3189 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3190 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3192 This
->prevFreeBlock
= freeBlock
;
3197 /******************************************************************************
3198 * Storage32Impl_AddBlockDepot
3200 * This will create a depot block, essentially it is a block initialized
3203 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3205 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3208 * Initialize blocks as free
3210 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3211 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3214 /******************************************************************************
3215 * Storage32Impl_GetExtDepotBlock
3217 * Returns the index of the block that corresponds to the specified depot
3218 * index. This method is only for depot indexes equal or greater than
3219 * COUNT_BBDEPOTINHEADER.
3221 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3223 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3224 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3225 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3226 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3227 ULONG blockIndex
= BLOCK_UNUSED
;
3228 ULONG extBlockIndex
;
3229 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3230 int index
, num_blocks
;
3232 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3234 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3235 return BLOCK_UNUSED
;
3237 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3239 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3241 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3243 num_blocks
= This
->bigBlockSize
/ 4;
3245 for (index
= 0; index
< num_blocks
; index
++)
3247 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3248 This
->extBlockDepotCached
[index
] = blockIndex
;
3251 This
->indexExtBlockDepotCached
= extBlockCount
;
3254 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3259 /******************************************************************************
3260 * Storage32Impl_SetExtDepotBlock
3262 * Associates the specified block index to the specified depot index.
3263 * This method is only for depot indexes equal or greater than
3264 * COUNT_BBDEPOTINHEADER.
3266 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3268 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3269 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3270 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3271 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3272 ULONG extBlockIndex
;
3274 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3276 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3278 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3280 if (extBlockIndex
!= BLOCK_UNUSED
)
3282 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3283 extBlockOffset
* sizeof(ULONG
),
3287 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3289 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3293 /******************************************************************************
3294 * Storage32Impl_AddExtBlockDepot
3296 * Creates an extended depot block.
3298 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3300 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3301 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3302 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3303 ULONG index
= BLOCK_UNUSED
;
3304 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3305 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3306 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3308 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3309 blocksPerDepotBlock
;
3311 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3314 * The first extended block.
3316 This
->extBigBlockDepotStart
= index
;
3321 * Find the last existing extended block.
3323 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3326 * Add the new extended block to the chain.
3328 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3333 * Initialize this block.
3335 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3336 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3338 /* Add the block to our cache. */
3339 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3341 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3342 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3344 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3345 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3347 This
->extBigBlockDepotLocations
= new_cache
;
3348 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3350 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3355 /******************************************************************************
3356 * Storage32Impl_FreeBigBlock
3358 * This method will flag the specified block as free in the big block depot.
3360 static void StorageImpl_FreeBigBlock(
3364 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3366 if (blockIndex
< This
->prevFreeBlock
)
3367 This
->prevFreeBlock
= blockIndex
;
3370 /************************************************************************
3371 * Storage32Impl_GetNextBlockInChain
3373 * This method will retrieve the block index of the next big block in
3376 * Params: This - Pointer to the Storage object.
3377 * blockIndex - Index of the block to retrieve the chain
3379 * nextBlockIndex - receives the return value.
3381 * Returns: This method returns the index of the next block in the chain.
3382 * It will return the constants:
3383 * BLOCK_SPECIAL - If the block given was not part of a
3385 * BLOCK_END_OF_CHAIN - If the block given was the last in
3387 * BLOCK_UNUSED - If the block given was not past of a chain
3389 * BLOCK_EXTBBDEPOT - This block is part of the extended
3392 * See Windows documentation for more details on IStorage methods.
3394 static HRESULT
StorageImpl_GetNextBlockInChain(
3397 ULONG
* nextBlockIndex
)
3399 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3400 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3401 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3402 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3404 ULONG depotBlockIndexPos
;
3405 int index
, num_blocks
;
3407 *nextBlockIndex
= BLOCK_SPECIAL
;
3409 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3411 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3412 This
->bigBlockDepotCount
);
3413 return STG_E_READFAULT
;
3417 * Cache the currently accessed depot block.
3419 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3421 This
->indexBlockDepotCached
= depotBlockCount
;
3423 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3425 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3430 * We have to look in the extended depot.
3432 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3435 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3438 return STG_E_READFAULT
;
3440 num_blocks
= This
->bigBlockSize
/ 4;
3442 for (index
= 0; index
< num_blocks
; index
++)
3444 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3445 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3449 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3454 /******************************************************************************
3455 * Storage32Impl_GetNextExtendedBlock
3457 * Given an extended block this method will return the next extended block.
3460 * The last ULONG of an extended block is the block index of the next
3461 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3465 * - The index of the next extended block
3466 * - BLOCK_UNUSED: there is no next extended block.
3467 * - Any other return values denotes failure.
3469 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3471 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3472 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3474 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3477 return nextBlockIndex
;
3480 /******************************************************************************
3481 * Storage32Impl_SetNextBlockInChain
3483 * This method will write the index of the specified block's next block
3484 * in the big block depot.
3486 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3489 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3490 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3491 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3494 static void StorageImpl_SetNextBlockInChain(
3499 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3500 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3501 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3502 ULONG depotBlockIndexPos
;
3504 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3505 assert(blockIndex
!= nextBlock
);
3507 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3509 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3514 * We have to look in the extended depot.
3516 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3519 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3522 * Update the cached block depot, if necessary.
3524 if (depotBlockCount
== This
->indexBlockDepotCached
)
3526 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3530 /******************************************************************************
3531 * Storage32Impl_LoadFileHeader
3533 * This method will read in the file header
3535 static HRESULT
StorageImpl_LoadFileHeader(
3539 BYTE headerBigBlock
[HEADER_SIZE
];
3541 ULARGE_INTEGER offset
;
3546 * Get a pointer to the big block of data containing the header.
3548 offset
.u
.HighPart
= 0;
3549 offset
.u
.LowPart
= 0;
3550 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3551 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3552 hr
= STG_E_FILENOTFOUND
;
3555 * Extract the information from the header.
3560 * Check for the "magic number" signature and return an error if it is not
3563 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3565 return STG_E_OLDFORMAT
;
3568 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3570 return STG_E_INVALIDHEADER
;
3573 StorageUtl_ReadWord(
3575 OFFSET_BIGBLOCKSIZEBITS
,
3576 &This
->bigBlockSizeBits
);
3578 StorageUtl_ReadWord(
3580 OFFSET_SMALLBLOCKSIZEBITS
,
3581 &This
->smallBlockSizeBits
);
3583 StorageUtl_ReadDWord(
3585 OFFSET_BBDEPOTCOUNT
,
3586 &This
->bigBlockDepotCount
);
3588 StorageUtl_ReadDWord(
3590 OFFSET_ROOTSTARTBLOCK
,
3591 &This
->rootStartBlock
);
3593 StorageUtl_ReadDWord(
3595 OFFSET_SMALLBLOCKLIMIT
,
3596 &This
->smallBlockLimit
);
3598 StorageUtl_ReadDWord(
3600 OFFSET_SBDEPOTSTART
,
3601 &This
->smallBlockDepotStart
);
3603 StorageUtl_ReadDWord(
3605 OFFSET_EXTBBDEPOTSTART
,
3606 &This
->extBigBlockDepotStart
);
3608 StorageUtl_ReadDWord(
3610 OFFSET_EXTBBDEPOTCOUNT
,
3611 &This
->extBigBlockDepotCount
);
3613 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3615 StorageUtl_ReadDWord(
3617 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3618 &(This
->bigBlockDepotStart
[index
]));
3622 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3624 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3625 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3628 * Right now, the code is making some assumptions about the size of the
3629 * blocks, just make sure they are what we're expecting.
3631 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3632 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3633 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3635 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3636 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3637 hr
= STG_E_INVALIDHEADER
;
3646 /******************************************************************************
3647 * Storage32Impl_SaveFileHeader
3649 * This method will save to the file the header
3651 static void StorageImpl_SaveFileHeader(
3654 BYTE headerBigBlock
[HEADER_SIZE
];
3657 ULARGE_INTEGER offset
;
3658 DWORD bytes_read
, bytes_written
;
3659 DWORD major_version
, dirsectorcount
;
3662 * Get a pointer to the big block of data containing the header.
3664 offset
.u
.HighPart
= 0;
3665 offset
.u
.LowPart
= 0;
3666 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3667 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3668 hr
= STG_E_FILENOTFOUND
;
3670 if (This
->bigBlockSizeBits
== 0x9)
3672 else if (This
->bigBlockSizeBits
== 0xc)
3676 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3681 * If the block read failed, the file is probably new.
3686 * Initialize for all unknown fields.
3688 memset(headerBigBlock
, 0, HEADER_SIZE
);
3691 * Initialize the magic number.
3693 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3697 * Write the information to the header.
3699 StorageUtl_WriteWord(
3701 OFFSET_MINORVERSION
,
3704 StorageUtl_WriteWord(
3706 OFFSET_MAJORVERSION
,
3709 StorageUtl_WriteWord(
3711 OFFSET_BYTEORDERMARKER
,
3714 StorageUtl_WriteWord(
3716 OFFSET_BIGBLOCKSIZEBITS
,
3717 This
->bigBlockSizeBits
);
3719 StorageUtl_WriteWord(
3721 OFFSET_SMALLBLOCKSIZEBITS
,
3722 This
->smallBlockSizeBits
);
3724 if (major_version
>= 4)
3726 if (This
->rootBlockChain
)
3727 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3729 /* This file is being created, and it will start out with one block. */
3733 /* This field must be 0 in versions older than 4 */
3736 StorageUtl_WriteDWord(
3738 OFFSET_DIRSECTORCOUNT
,
3741 StorageUtl_WriteDWord(
3743 OFFSET_BBDEPOTCOUNT
,
3744 This
->bigBlockDepotCount
);
3746 StorageUtl_WriteDWord(
3748 OFFSET_ROOTSTARTBLOCK
,
3749 This
->rootStartBlock
);
3751 StorageUtl_WriteDWord(
3753 OFFSET_SMALLBLOCKLIMIT
,
3754 This
->smallBlockLimit
);
3756 StorageUtl_WriteDWord(
3758 OFFSET_SBDEPOTSTART
,
3759 This
->smallBlockDepotStart
);
3761 StorageUtl_WriteDWord(
3763 OFFSET_SBDEPOTCOUNT
,
3764 This
->smallBlockDepotChain
?
3765 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3767 StorageUtl_WriteDWord(
3769 OFFSET_EXTBBDEPOTSTART
,
3770 This
->extBigBlockDepotStart
);
3772 StorageUtl_WriteDWord(
3774 OFFSET_EXTBBDEPOTCOUNT
,
3775 This
->extBigBlockDepotCount
);
3777 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3779 StorageUtl_WriteDWord(
3781 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3782 (This
->bigBlockDepotStart
[index
]));
3786 * Write the big block back to the file.
3788 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3791 /******************************************************************************
3792 * StorageImpl_ReadRawDirEntry
3794 * This method will read the raw data from a directory entry in the file.
3796 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3798 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3800 ULARGE_INTEGER offset
;
3804 offset
.u
.HighPart
= 0;
3805 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3807 hr
= BlockChainStream_ReadAt(
3808 This
->rootBlockChain
,
3814 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3815 return STG_E_READFAULT
;
3820 /******************************************************************************
3821 * StorageImpl_WriteRawDirEntry
3823 * This method will write the raw data from a directory entry in the file.
3825 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3827 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3829 ULARGE_INTEGER offset
;
3832 offset
.u
.HighPart
= 0;
3833 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3835 return BlockChainStream_WriteAt(
3836 This
->rootBlockChain
,
3843 /******************************************************************************
3846 * Update raw directory entry data from the fields in newData.
3848 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3850 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3852 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3855 buffer
+ OFFSET_PS_NAME
,
3857 DIRENTRY_NAME_BUFFER_LEN
);
3859 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3861 StorageUtl_WriteWord(
3863 OFFSET_PS_NAMELENGTH
,
3864 newData
->sizeOfNameString
);
3866 StorageUtl_WriteDWord(
3868 OFFSET_PS_LEFTCHILD
,
3869 newData
->leftChild
);
3871 StorageUtl_WriteDWord(
3873 OFFSET_PS_RIGHTCHILD
,
3874 newData
->rightChild
);
3876 StorageUtl_WriteDWord(
3879 newData
->dirRootEntry
);
3881 StorageUtl_WriteGUID(
3886 StorageUtl_WriteDWord(
3889 newData
->ctime
.dwLowDateTime
);
3891 StorageUtl_WriteDWord(
3893 OFFSET_PS_CTIMEHIGH
,
3894 newData
->ctime
.dwHighDateTime
);
3896 StorageUtl_WriteDWord(
3899 newData
->mtime
.dwLowDateTime
);
3901 StorageUtl_WriteDWord(
3903 OFFSET_PS_MTIMEHIGH
,
3904 newData
->ctime
.dwHighDateTime
);
3906 StorageUtl_WriteDWord(
3908 OFFSET_PS_STARTBLOCK
,
3909 newData
->startingBlock
);
3911 StorageUtl_WriteDWord(
3914 newData
->size
.u
.LowPart
);
3917 /******************************************************************************
3918 * Storage32Impl_ReadDirEntry
3920 * This method will read the specified directory entry.
3922 HRESULT
StorageImpl_ReadDirEntry(
3927 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3930 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3932 if (SUCCEEDED(readRes
))
3934 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3937 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3938 DIRENTRY_NAME_BUFFER_LEN
);
3939 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3941 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3943 StorageUtl_ReadWord(
3945 OFFSET_PS_NAMELENGTH
,
3946 &buffer
->sizeOfNameString
);
3948 StorageUtl_ReadDWord(
3950 OFFSET_PS_LEFTCHILD
,
3951 &buffer
->leftChild
);
3953 StorageUtl_ReadDWord(
3955 OFFSET_PS_RIGHTCHILD
,
3956 &buffer
->rightChild
);
3958 StorageUtl_ReadDWord(
3961 &buffer
->dirRootEntry
);
3963 StorageUtl_ReadGUID(
3968 StorageUtl_ReadDWord(
3971 &buffer
->ctime
.dwLowDateTime
);
3973 StorageUtl_ReadDWord(
3975 OFFSET_PS_CTIMEHIGH
,
3976 &buffer
->ctime
.dwHighDateTime
);
3978 StorageUtl_ReadDWord(
3981 &buffer
->mtime
.dwLowDateTime
);
3983 StorageUtl_ReadDWord(
3985 OFFSET_PS_MTIMEHIGH
,
3986 &buffer
->mtime
.dwHighDateTime
);
3988 StorageUtl_ReadDWord(
3990 OFFSET_PS_STARTBLOCK
,
3991 &buffer
->startingBlock
);
3993 StorageUtl_ReadDWord(
3996 &buffer
->size
.u
.LowPart
);
3998 buffer
->size
.u
.HighPart
= 0;
4004 /*********************************************************************
4005 * Write the specified directory entry to the file
4007 HRESULT
StorageImpl_WriteDirEntry(
4010 const DirEntry
* buffer
)
4012 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4014 UpdateRawDirEntry(currentEntry
, buffer
);
4016 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4019 static HRESULT
StorageImpl_ReadBigBlock(
4025 ULARGE_INTEGER ulOffset
;
4029 ulOffset
.u
.HighPart
= 0;
4030 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4032 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4034 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4036 /* File ends during this block; fill the rest with 0's. */
4037 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4040 if (out_read
) *out_read
= read
;
4045 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4051 ULARGE_INTEGER ulOffset
;
4055 ulOffset
.u
.HighPart
= 0;
4056 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4057 ulOffset
.u
.LowPart
+= offset
;
4059 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4060 *value
= lendian32toh(tmp
);
4061 return (read
== sizeof(DWORD
));
4064 static BOOL
StorageImpl_WriteBigBlock(
4069 ULARGE_INTEGER ulOffset
;
4072 ulOffset
.u
.HighPart
= 0;
4073 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4075 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4076 return (wrote
== This
->bigBlockSize
);
4079 static BOOL
StorageImpl_WriteDWordToBigBlock(
4085 ULARGE_INTEGER ulOffset
;
4088 ulOffset
.u
.HighPart
= 0;
4089 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4090 ulOffset
.u
.LowPart
+= offset
;
4092 value
= htole32(value
);
4093 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4094 return (wrote
== sizeof(DWORD
));
4097 /******************************************************************************
4098 * Storage32Impl_SmallBlocksToBigBlocks
4100 * This method will convert a small block chain to a big block chain.
4101 * The small block chain will be destroyed.
4103 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4105 SmallBlockChainStream
** ppsbChain
)
4107 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4108 ULARGE_INTEGER size
, offset
;
4109 ULONG cbRead
, cbWritten
;
4110 ULARGE_INTEGER cbTotalRead
;
4111 DirRef streamEntryRef
;
4112 HRESULT resWrite
= S_OK
;
4114 DirEntry streamEntry
;
4116 BlockChainStream
*bbTempChain
= NULL
;
4117 BlockChainStream
*bigBlockChain
= NULL
;
4120 * Create a temporary big block chain that doesn't have
4121 * an associated directory entry. This temporary chain will be
4122 * used to copy data from small blocks to big blocks.
4124 bbTempChain
= BlockChainStream_Construct(This
,
4127 if(!bbTempChain
) return NULL
;
4129 * Grow the big block chain.
4131 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4132 BlockChainStream_SetSize(bbTempChain
, size
);
4135 * Copy the contents of the small block chain to the big block chain
4136 * by small block size increments.
4138 offset
.u
.LowPart
= 0;
4139 offset
.u
.HighPart
= 0;
4140 cbTotalRead
.QuadPart
= 0;
4142 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4145 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4147 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4150 if (FAILED(resRead
))
4155 cbTotalRead
.QuadPart
+= cbRead
;
4157 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4163 if (FAILED(resWrite
))
4166 offset
.u
.LowPart
+= cbRead
;
4170 resRead
= STG_E_READFAULT
;
4173 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4174 HeapFree(GetProcessHeap(),0,buffer
);
4176 size
.u
.HighPart
= 0;
4179 if (FAILED(resRead
) || FAILED(resWrite
))
4181 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4182 BlockChainStream_SetSize(bbTempChain
, size
);
4183 BlockChainStream_Destroy(bbTempChain
);
4188 * Destroy the small block chain.
4190 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4191 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4192 SmallBlockChainStream_Destroy(*ppsbChain
);
4196 * Change the directory entry. This chain is now a big block chain
4197 * and it doesn't reside in the small blocks chain anymore.
4199 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4201 streamEntry
.startingBlock
= bbHeadOfChain
;
4203 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4206 * Destroy the temporary entryless big block chain.
4207 * Create a new big block chain associated with this entry.
4209 BlockChainStream_Destroy(bbTempChain
);
4210 bigBlockChain
= BlockChainStream_Construct(This
,
4214 return bigBlockChain
;
4217 /******************************************************************************
4218 * Storage32Impl_BigBlocksToSmallBlocks
4220 * This method will convert a big block chain to a small block chain.
4221 * The big block chain will be destroyed on success.
4223 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4225 BlockChainStream
** ppbbChain
,
4226 ULARGE_INTEGER newSize
)
4228 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4229 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4230 DirRef streamEntryRef
;
4231 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4232 DirEntry streamEntry
;
4234 SmallBlockChainStream
* sbTempChain
;
4236 TRACE("%p %p\n", This
, ppbbChain
);
4238 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4244 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4245 size
= BlockChainStream_GetSize(*ppbbChain
);
4246 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4248 offset
.u
.HighPart
= 0;
4249 offset
.u
.LowPart
= 0;
4250 cbTotalRead
.QuadPart
= 0;
4251 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4252 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4254 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4255 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4263 cbTotalRead
.QuadPart
+= cbRead
;
4265 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4266 cbRead
, buffer
, &cbWritten
);
4268 if(FAILED(resWrite
))
4271 offset
.u
.LowPart
+= cbRead
;
4275 resRead
= STG_E_READFAULT
;
4279 HeapFree(GetProcessHeap(), 0, buffer
);
4281 size
.u
.HighPart
= 0;
4284 if(FAILED(resRead
) || FAILED(resWrite
))
4286 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4287 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4288 SmallBlockChainStream_Destroy(sbTempChain
);
4292 /* destroy the original big block chain */
4293 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4294 BlockChainStream_SetSize(*ppbbChain
, size
);
4295 BlockChainStream_Destroy(*ppbbChain
);
4298 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4299 streamEntry
.startingBlock
= sbHeadOfChain
;
4300 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4302 SmallBlockChainStream_Destroy(sbTempChain
);
4303 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4306 static HRESULT
StorageBaseImpl_CopyStream(
4307 StorageBaseImpl
*dst
, DirRef dst_entry
,
4308 StorageBaseImpl
*src
, DirRef src_entry
)
4313 ULARGE_INTEGER bytes_copied
;
4314 ULONG bytestocopy
, bytesread
, byteswritten
;
4316 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4320 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4322 bytes_copied
.QuadPart
= 0;
4323 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4325 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4327 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4329 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4332 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4333 data
, &byteswritten
);
4336 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4337 bytes_copied
.QuadPart
+= byteswritten
;
4345 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4347 DirRef result
=This
->firstFreeEntry
;
4349 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4352 if (result
== This
->entries_size
)
4354 ULONG new_size
= This
->entries_size
* 2;
4355 TransactedDirEntry
*new_entries
;
4357 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4358 if (!new_entries
) return DIRENTRY_NULL
;
4360 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4361 HeapFree(GetProcessHeap(), 0, This
->entries
);
4363 This
->entries
= new_entries
;
4364 This
->entries_size
= new_size
;
4367 This
->entries
[result
].inuse
= TRUE
;
4369 This
->firstFreeEntry
= result
+1;
4374 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4375 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4377 DirRef stubEntryRef
;
4378 TransactedDirEntry
*entry
;
4380 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4382 if (stubEntryRef
!= DIRENTRY_NULL
)
4384 entry
= &This
->entries
[stubEntryRef
];
4386 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4388 entry
->read
= FALSE
;
4391 return stubEntryRef
;
4394 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4395 TransactedSnapshotImpl
*This
, DirRef entry
)
4400 if (!This
->entries
[entry
].read
)
4402 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4403 This
->entries
[entry
].transactedParentEntry
,
4406 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4408 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4410 if (data
.leftChild
== DIRENTRY_NULL
)
4414 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4416 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4418 if (data
.rightChild
== DIRENTRY_NULL
)
4422 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4424 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4426 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4432 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4433 This
->entries
[entry
].read
= TRUE
;
4440 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4441 TransactedSnapshotImpl
*This
, DirRef entry
)
4445 if (!This
->entries
[entry
].stream_dirty
)
4447 DirEntry new_entrydata
;
4449 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4450 new_entrydata
.name
[0] = 'S';
4451 new_entrydata
.sizeOfNameString
= 1;
4452 new_entrydata
.stgType
= STGTY_STREAM
;
4453 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4454 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4455 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4456 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4458 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4459 &This
->entries
[entry
].stream_entry
);
4461 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4463 hr
= StorageBaseImpl_CopyStream(
4464 This
->scratch
, This
->entries
[entry
].stream_entry
,
4465 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4468 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4472 This
->entries
[entry
].stream_dirty
= TRUE
;
4474 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4476 /* Since this entry is modified, and we aren't using its stream data, we
4477 * no longer care about the original entry. */
4479 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4481 if (delete_ref
!= DIRENTRY_NULL
)
4482 This
->entries
[delete_ref
].deleted
= TRUE
;
4484 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4491 /* Find the first entry in a depth-first traversal. */
4492 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4493 TransactedSnapshotImpl
* This
, DirRef parent
)
4495 DirRef cursor
, prev
;
4496 TransactedDirEntry
*entry
;
4499 entry
= &This
->entries
[cursor
];
4502 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4505 cursor
= entry
->data
.leftChild
;
4506 entry
= &This
->entries
[cursor
];
4507 entry
->parent
= prev
;
4509 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4512 cursor
= entry
->data
.rightChild
;
4513 entry
= &This
->entries
[cursor
];
4514 entry
->parent
= prev
;
4516 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4519 cursor
= entry
->data
.dirRootEntry
;
4520 entry
= &This
->entries
[cursor
];
4521 entry
->parent
= prev
;
4530 /* Find the next entry in a depth-first traversal. */
4531 static DirRef
TransactedSnapshotImpl_FindNextChild(
4532 TransactedSnapshotImpl
* This
, DirRef current
)
4535 TransactedDirEntry
*parent_entry
;
4537 parent
= This
->entries
[current
].parent
;
4538 parent_entry
= &This
->entries
[parent
];
4540 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4542 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4544 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4545 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4548 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4550 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4551 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4558 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4559 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4560 TransactedSnapshotImpl
* This
, DirRef entry
)
4562 return entry
!= DIRENTRY_NULL
&&
4563 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4566 /* Destroy the entries created by CopyTree. */
4567 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4568 TransactedSnapshotImpl
* This
, DirRef stop
)
4571 TransactedDirEntry
*entry
;
4572 ULARGE_INTEGER zero
;
4576 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4579 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4581 if (cursor
== DIRENTRY_NULL
)
4584 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4586 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4588 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4590 entry
= &This
->entries
[cursor
];
4592 if (entry
->stream_dirty
)
4593 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4594 entry
->newTransactedParentEntry
, zero
);
4596 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4597 entry
->newTransactedParentEntry
);
4599 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4602 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4606 /* Make a copy of our edited tree that we can use in the parent. */
4607 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4610 TransactedDirEntry
*entry
;
4613 cursor
= This
->base
.storageDirEntry
;
4614 entry
= &This
->entries
[cursor
];
4615 entry
->parent
= DIRENTRY_NULL
;
4616 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4618 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4621 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4623 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4624 entry
= &This
->entries
[cursor
];
4626 while (cursor
!= DIRENTRY_NULL
)
4628 /* Make a copy of this entry in the transacted parent. */
4630 (!entry
->dirty
&& !entry
->stream_dirty
&&
4631 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4632 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4633 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4634 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4639 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4641 newData
.size
.QuadPart
= 0;
4642 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4644 if (newData
.leftChild
!= DIRENTRY_NULL
)
4645 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4647 if (newData
.rightChild
!= DIRENTRY_NULL
)
4648 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4650 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4651 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4653 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4654 &entry
->newTransactedParentEntry
);
4657 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4661 if (entry
->stream_dirty
)
4663 hr
= StorageBaseImpl_CopyStream(
4664 This
->transactedParent
, entry
->newTransactedParentEntry
,
4665 This
->scratch
, entry
->stream_entry
);
4667 else if (entry
->data
.size
.QuadPart
)
4669 hr
= StorageBaseImpl_StreamLink(
4670 This
->transactedParent
, entry
->newTransactedParentEntry
,
4671 entry
->transactedParentEntry
);
4676 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4677 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4682 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4683 entry
= &This
->entries
[cursor
];
4689 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4691 DWORD grfCommitFlags
) /* [in] */
4693 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4694 TransactedDirEntry
*root_entry
;
4695 DirRef i
, dir_root_ref
;
4697 ULARGE_INTEGER zero
;
4702 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4704 /* Cannot commit a read-only transacted storage */
4705 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4706 return STG_E_ACCESSDENIED
;
4708 /* To prevent data loss, we create the new structure in the file before we
4709 * delete the old one, so that in case of errors the old data is intact. We
4710 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4711 * needed in the rare situation where we have just enough free disk space to
4712 * overwrite the existing data. */
4714 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4716 if (!root_entry
->read
)
4719 hr
= TransactedSnapshotImpl_CopyTree(This
);
4720 if (FAILED(hr
)) return hr
;
4722 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4723 dir_root_ref
= DIRENTRY_NULL
;
4725 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4727 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4729 /* Update the storage to use the new data in one step. */
4731 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4732 root_entry
->transactedParentEntry
, &data
);
4736 data
.dirRootEntry
= dir_root_ref
;
4737 data
.clsid
= root_entry
->data
.clsid
;
4738 data
.ctime
= root_entry
->data
.ctime
;
4739 data
.mtime
= root_entry
->data
.mtime
;
4741 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4742 root_entry
->transactedParentEntry
, &data
);
4745 /* Try to flush after updating the root storage, but if the flush fails, keep
4746 * going, on the theory that it'll either succeed later or the subsequent
4747 * writes will fail. */
4748 StorageBaseImpl_Flush(This
->transactedParent
);
4752 /* Destroy the old now-orphaned data. */
4753 for (i
=0; i
<This
->entries_size
; i
++)
4755 TransactedDirEntry
*entry
= &This
->entries
[i
];
4760 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4761 entry
->transactedParentEntry
, zero
);
4762 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4763 entry
->transactedParentEntry
);
4764 memset(entry
, 0, sizeof(TransactedDirEntry
));
4765 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4767 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4769 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4770 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4771 entry
->transactedParentEntry
);
4772 if (entry
->stream_dirty
)
4774 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4775 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4776 entry
->stream_dirty
= FALSE
;
4778 entry
->dirty
= FALSE
;
4779 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4786 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4790 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4795 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4798 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4799 ULARGE_INTEGER zero
;
4802 TRACE("(%p)\n", iface
);
4804 /* Destroy the open objects. */
4805 StorageBaseImpl_DeleteAll(&This
->base
);
4807 /* Clear out the scratch file. */
4809 for (i
=0; i
<This
->entries_size
; i
++)
4811 if (This
->entries
[i
].stream_dirty
)
4813 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4816 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4820 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4822 This
->firstFreeEntry
= 0;
4823 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4828 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4830 if (!This
->reverted
)
4832 TRACE("Storage invalidated (stg=%p)\n", This
);
4834 This
->reverted
= TRUE
;
4836 StorageBaseImpl_DeleteAll(This
);
4840 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4842 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4844 IStorage_Revert(&This
->base
.IStorage_iface
);
4845 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
4846 IStorage_Release(&This
->scratch
->IStorage_iface
);
4847 HeapFree(GetProcessHeap(), 0, This
->entries
);
4848 HeapFree(GetProcessHeap(), 0, This
);
4851 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4853 /* We only need to flush when committing. */
4857 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4859 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4861 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4864 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4865 const DirEntry
*newData
, DirRef
*index
)
4867 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4869 TransactedDirEntry
*new_entry
;
4871 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4872 if (new_ref
== DIRENTRY_NULL
)
4873 return E_OUTOFMEMORY
;
4875 new_entry
= &This
->entries
[new_ref
];
4877 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4878 new_entry
->read
= TRUE
;
4879 new_entry
->dirty
= TRUE
;
4880 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4884 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4889 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4890 DirRef index
, const DirEntry
*data
)
4892 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4895 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4897 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4898 if (FAILED(hr
)) return hr
;
4900 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4902 if (index
!= This
->base
.storageDirEntry
)
4904 This
->entries
[index
].dirty
= TRUE
;
4906 if (data
->size
.QuadPart
== 0 &&
4907 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4909 /* Since this entry is modified, and we aren't using its stream data, we
4910 * no longer care about the original entry. */
4912 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4914 if (delete_ref
!= DIRENTRY_NULL
)
4915 This
->entries
[delete_ref
].deleted
= TRUE
;
4917 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4924 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4925 DirRef index
, DirEntry
*data
)
4927 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4930 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4931 if (FAILED(hr
)) return hr
;
4933 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4935 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4940 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4943 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4945 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4946 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4948 /* If we deleted this entry while it has stream data. We must have left the
4949 * data because some other entry is using it, and we need to leave the
4950 * original entry alone. */
4951 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4952 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4956 This
->entries
[index
].deleted
= TRUE
;
4962 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4963 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4965 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4967 if (This
->entries
[index
].stream_dirty
)
4969 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4970 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4972 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4974 /* This stream doesn't live in the parent, and we haven't allocated storage
4981 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4982 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4986 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4987 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4989 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4992 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4993 if (FAILED(hr
)) return hr
;
4995 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4996 if (FAILED(hr
)) return hr
;
4998 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4999 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5001 if (SUCCEEDED(hr
) && size
!= 0)
5002 This
->entries
[index
].data
.size
.QuadPart
= max(
5003 This
->entries
[index
].data
.size
.QuadPart
,
5004 offset
.QuadPart
+ size
);
5009 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5010 DirRef index
, ULARGE_INTEGER newsize
)
5012 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5015 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5016 if (FAILED(hr
)) return hr
;
5018 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5021 if (newsize
.QuadPart
== 0)
5023 /* Destroy any parent references or entries in the scratch file. */
5024 if (This
->entries
[index
].stream_dirty
)
5026 ULARGE_INTEGER zero
;
5028 StorageBaseImpl_StreamSetSize(This
->scratch
,
5029 This
->entries
[index
].stream_entry
, zero
);
5030 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5031 This
->entries
[index
].stream_entry
);
5032 This
->entries
[index
].stream_dirty
= FALSE
;
5034 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5037 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5039 if (delete_ref
!= DIRENTRY_NULL
)
5040 This
->entries
[delete_ref
].deleted
= TRUE
;
5042 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5047 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5048 if (FAILED(hr
)) return hr
;
5050 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5051 This
->entries
[index
].stream_entry
, newsize
);
5055 This
->entries
[index
].data
.size
= newsize
;
5060 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5061 DirRef dst
, DirRef src
)
5063 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5065 TransactedDirEntry
*dst_entry
, *src_entry
;
5067 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5068 if (FAILED(hr
)) return hr
;
5070 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5071 if (FAILED(hr
)) return hr
;
5073 dst_entry
= &This
->entries
[dst
];
5074 src_entry
= &This
->entries
[src
];
5076 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5077 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5078 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5079 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5080 dst_entry
->data
.size
= src_entry
->data
.size
;
5085 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5087 StorageBaseImpl_QueryInterface
,
5088 StorageBaseImpl_AddRef
,
5089 StorageBaseImpl_Release
,
5090 StorageBaseImpl_CreateStream
,
5091 StorageBaseImpl_OpenStream
,
5092 StorageBaseImpl_CreateStorage
,
5093 StorageBaseImpl_OpenStorage
,
5094 StorageBaseImpl_CopyTo
,
5095 StorageBaseImpl_MoveElementTo
,
5096 TransactedSnapshotImpl_Commit
,
5097 TransactedSnapshotImpl_Revert
,
5098 StorageBaseImpl_EnumElements
,
5099 StorageBaseImpl_DestroyElement
,
5100 StorageBaseImpl_RenameElement
,
5101 StorageBaseImpl_SetElementTimes
,
5102 StorageBaseImpl_SetClass
,
5103 StorageBaseImpl_SetStateBits
,
5104 StorageBaseImpl_Stat
5107 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5109 TransactedSnapshotImpl_Destroy
,
5110 TransactedSnapshotImpl_Invalidate
,
5111 TransactedSnapshotImpl_Flush
,
5112 TransactedSnapshotImpl_GetFilename
,
5113 TransactedSnapshotImpl_CreateDirEntry
,
5114 TransactedSnapshotImpl_WriteDirEntry
,
5115 TransactedSnapshotImpl_ReadDirEntry
,
5116 TransactedSnapshotImpl_DestroyDirEntry
,
5117 TransactedSnapshotImpl_StreamReadAt
,
5118 TransactedSnapshotImpl_StreamWriteAt
,
5119 TransactedSnapshotImpl_StreamSetSize
,
5120 TransactedSnapshotImpl_StreamLink
5123 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5124 TransactedSnapshotImpl
** result
)
5128 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5133 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5135 /* This is OK because the property set storage functions use the IStorage functions. */
5136 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5137 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5139 list_init(&(*result
)->base
.strmHead
);
5141 list_init(&(*result
)->base
.storageHead
);
5143 (*result
)->base
.ref
= 1;
5145 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5147 /* Create a new temporary storage to act as the scratch file. */
5148 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5150 (*result
)->scratch
= impl_from_IStorage(scratch
);
5154 ULONG num_entries
= 20;
5156 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5157 (*result
)->entries_size
= num_entries
;
5158 (*result
)->firstFreeEntry
= 0;
5160 if ((*result
)->entries
)
5162 /* parentStorage already has 1 reference, which we take over here. */
5163 (*result
)->transactedParent
= parentStorage
;
5165 parentStorage
->transactedChild
= &(*result
)->base
;
5167 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5171 IStorage_Release(scratch
);
5177 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5182 return E_OUTOFMEMORY
;
5185 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5186 StorageBaseImpl
** result
)
5190 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5192 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5195 return TransactedSnapshotImpl_Construct(parentStorage
,
5196 (TransactedSnapshotImpl
**)result
);
5199 static HRESULT
Storage_Construct(
5207 StorageBaseImpl
** result
)
5209 StorageImpl
*newStorage
;
5210 StorageBaseImpl
*newTransactedStorage
;
5213 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5214 if (FAILED(hr
)) goto end
;
5216 if (openFlags
& STGM_TRANSACTED
)
5218 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5220 IStorage_Release(&newStorage
->base
.IStorage_iface
);
5222 *result
= newTransactedStorage
;
5225 *result
= &newStorage
->base
;
5231 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5233 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5235 if (!This
->base
.reverted
)
5237 TRACE("Storage invalidated (stg=%p)\n", This
);
5239 This
->base
.reverted
= TRUE
;
5241 This
->parentStorage
= NULL
;
5243 StorageBaseImpl_DeleteAll(&This
->base
);
5245 list_remove(&This
->ParentListEntry
);
5249 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5251 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5253 StorageInternalImpl_Invalidate(&This
->base
);
5255 HeapFree(GetProcessHeap(), 0, This
);
5258 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5260 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5262 return StorageBaseImpl_Flush(This
->parentStorage
);
5265 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5267 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5269 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5272 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5273 const DirEntry
*newData
, DirRef
*index
)
5275 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5277 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5281 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5282 DirRef index
, const DirEntry
*data
)
5284 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5286 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5290 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5291 DirRef index
, DirEntry
*data
)
5293 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5295 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5299 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5302 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5304 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5308 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5309 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5311 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5313 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5314 index
, offset
, size
, buffer
, bytesRead
);
5317 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5318 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5320 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5322 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5323 index
, offset
, size
, buffer
, bytesWritten
);
5326 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5327 DirRef index
, ULARGE_INTEGER newsize
)
5329 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5331 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5335 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5336 DirRef dst
, DirRef src
)
5338 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5340 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5344 /******************************************************************************
5346 ** Storage32InternalImpl_Commit
5349 static HRESULT WINAPI
StorageInternalImpl_Commit(
5351 DWORD grfCommitFlags
) /* [in] */
5353 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5354 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5355 return StorageBaseImpl_Flush(This
);
5358 /******************************************************************************
5360 ** Storage32InternalImpl_Revert
5363 static HRESULT WINAPI
StorageInternalImpl_Revert(
5366 FIXME("(%p): stub\n", iface
);
5370 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5372 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
5373 HeapFree(GetProcessHeap(), 0, This
);
5376 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5377 IEnumSTATSTG
* iface
,
5381 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5384 return E_INVALIDARG
;
5388 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5389 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5392 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5396 return E_NOINTERFACE
;
5399 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5400 IEnumSTATSTG
* iface
)
5402 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5403 return InterlockedIncrement(&This
->ref
);
5406 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5407 IEnumSTATSTG
* iface
)
5409 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5413 newRef
= InterlockedDecrement(&This
->ref
);
5417 IEnumSTATSTGImpl_Destroy(This
);
5423 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5424 IEnumSTATSTGImpl
* This
,
5427 DirRef result
= DIRENTRY_NULL
;
5431 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5433 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5434 This
->parentStorage
->storageDirEntry
, &entry
);
5435 searchNode
= entry
.dirRootEntry
;
5437 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5439 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5443 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5447 searchNode
= entry
.rightChild
;
5451 result
= searchNode
;
5452 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5453 searchNode
= entry
.leftChild
;
5461 if (result
!= DIRENTRY_NULL
)
5462 memcpy(This
->name
, result_name
, sizeof(result_name
));
5468 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5469 IEnumSTATSTG
* iface
,
5472 ULONG
* pceltFetched
)
5474 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5476 DirEntry currentEntry
;
5477 STATSTG
* currentReturnStruct
= rgelt
;
5478 ULONG objectFetched
= 0;
5479 DirRef currentSearchNode
;
5482 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5483 return E_INVALIDARG
;
5485 if (This
->parentStorage
->reverted
)
5486 return STG_E_REVERTED
;
5489 * To avoid the special case, get another pointer to a ULONG value if
5490 * the caller didn't supply one.
5492 if (pceltFetched
==0)
5493 pceltFetched
= &objectFetched
;
5496 * Start the iteration, we will iterate until we hit the end of the
5497 * linked list or until we hit the number of items to iterate through
5501 while ( *pceltFetched
< celt
)
5503 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5505 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5509 * Read the entry from the storage.
5511 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5516 * Copy the information to the return buffer.
5518 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5519 currentReturnStruct
,
5524 * Step to the next item in the iteration
5527 currentReturnStruct
++;
5530 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5537 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5538 IEnumSTATSTG
* iface
,
5541 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5543 ULONG objectFetched
= 0;
5544 DirRef currentSearchNode
;
5547 if (This
->parentStorage
->reverted
)
5548 return STG_E_REVERTED
;
5550 while ( (objectFetched
< celt
) )
5552 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5554 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5560 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5566 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5567 IEnumSTATSTG
* iface
)
5569 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5571 if (This
->parentStorage
->reverted
)
5572 return STG_E_REVERTED
;
5579 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5580 IEnumSTATSTG
* iface
,
5581 IEnumSTATSTG
** ppenum
)
5583 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5584 IEnumSTATSTGImpl
* newClone
;
5586 if (This
->parentStorage
->reverted
)
5587 return STG_E_REVERTED
;
5590 return E_INVALIDARG
;
5592 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5593 This
->storageDirEntry
);
5597 return E_OUTOFMEMORY
;
5601 * The new clone enumeration must point to the same current node as
5604 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5606 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5612 * Virtual function table for the IEnumSTATSTGImpl class.
5614 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5616 IEnumSTATSTGImpl_QueryInterface
,
5617 IEnumSTATSTGImpl_AddRef
,
5618 IEnumSTATSTGImpl_Release
,
5619 IEnumSTATSTGImpl_Next
,
5620 IEnumSTATSTGImpl_Skip
,
5621 IEnumSTATSTGImpl_Reset
,
5622 IEnumSTATSTGImpl_Clone
5625 /******************************************************************************
5626 ** IEnumSTATSTGImpl implementation
5629 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5630 StorageBaseImpl
* parentStorage
,
5631 DirRef storageDirEntry
)
5633 IEnumSTATSTGImpl
* newEnumeration
;
5635 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5639 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5640 newEnumeration
->ref
= 1;
5641 newEnumeration
->name
[0] = 0;
5644 * We want to nail-down the reference to the storage in case the
5645 * enumeration out-lives the storage in the client application.
5647 newEnumeration
->parentStorage
= parentStorage
;
5648 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
5650 newEnumeration
->storageDirEntry
= storageDirEntry
;
5653 return newEnumeration
;
5657 * Virtual function table for the Storage32InternalImpl class.
5659 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5661 StorageBaseImpl_QueryInterface
,
5662 StorageBaseImpl_AddRef
,
5663 StorageBaseImpl_Release
,
5664 StorageBaseImpl_CreateStream
,
5665 StorageBaseImpl_OpenStream
,
5666 StorageBaseImpl_CreateStorage
,
5667 StorageBaseImpl_OpenStorage
,
5668 StorageBaseImpl_CopyTo
,
5669 StorageBaseImpl_MoveElementTo
,
5670 StorageInternalImpl_Commit
,
5671 StorageInternalImpl_Revert
,
5672 StorageBaseImpl_EnumElements
,
5673 StorageBaseImpl_DestroyElement
,
5674 StorageBaseImpl_RenameElement
,
5675 StorageBaseImpl_SetElementTimes
,
5676 StorageBaseImpl_SetClass
,
5677 StorageBaseImpl_SetStateBits
,
5678 StorageBaseImpl_Stat
5681 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5683 StorageInternalImpl_Destroy
,
5684 StorageInternalImpl_Invalidate
,
5685 StorageInternalImpl_Flush
,
5686 StorageInternalImpl_GetFilename
,
5687 StorageInternalImpl_CreateDirEntry
,
5688 StorageInternalImpl_WriteDirEntry
,
5689 StorageInternalImpl_ReadDirEntry
,
5690 StorageInternalImpl_DestroyDirEntry
,
5691 StorageInternalImpl_StreamReadAt
,
5692 StorageInternalImpl_StreamWriteAt
,
5693 StorageInternalImpl_StreamSetSize
,
5694 StorageInternalImpl_StreamLink
5697 /******************************************************************************
5698 ** Storage32InternalImpl implementation
5701 static StorageInternalImpl
* StorageInternalImpl_Construct(
5702 StorageBaseImpl
* parentStorage
,
5704 DirRef storageDirEntry
)
5706 StorageInternalImpl
* newStorage
;
5708 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5712 list_init(&newStorage
->base
.strmHead
);
5714 list_init(&newStorage
->base
.storageHead
);
5717 * Initialize the virtual function table.
5719 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5720 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5721 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5722 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5724 newStorage
->base
.reverted
= FALSE
;
5726 newStorage
->base
.ref
= 1;
5728 newStorage
->parentStorage
= parentStorage
;
5731 * Keep a reference to the directory entry of this storage
5733 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5735 newStorage
->base
.create
= FALSE
;
5743 /******************************************************************************
5744 ** StorageUtl implementation
5747 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5751 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5752 *value
= lendian16toh(tmp
);
5755 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5757 value
= htole16(value
);
5758 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5761 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5765 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5766 *value
= lendian32toh(tmp
);
5769 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5771 value
= htole32(value
);
5772 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5775 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5776 ULARGE_INTEGER
* value
)
5778 #ifdef WORDS_BIGENDIAN
5781 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5782 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5783 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5785 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5789 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5790 const ULARGE_INTEGER
*value
)
5792 #ifdef WORDS_BIGENDIAN
5795 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5796 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5797 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5799 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5803 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5805 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5806 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5807 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5809 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5812 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5814 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5815 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5816 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5818 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5821 void StorageUtl_CopyDirEntryToSTATSTG(
5822 StorageBaseImpl
* storage
,
5823 STATSTG
* destination
,
5824 const DirEntry
* source
,
5828 * The copy of the string occurs only when the flag is not set
5830 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5832 /* Use the filename for the root storage. */
5833 destination
->pwcsName
= 0;
5834 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5836 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5837 (source
->name
[0] == 0) )
5839 destination
->pwcsName
= 0;
5843 destination
->pwcsName
=
5844 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5846 strcpyW(destination
->pwcsName
, source
->name
);
5849 switch (source
->stgType
)
5853 destination
->type
= STGTY_STORAGE
;
5856 destination
->type
= STGTY_STREAM
;
5859 destination
->type
= STGTY_STREAM
;
5863 destination
->cbSize
= source
->size
;
5865 currentReturnStruct->mtime = {0}; TODO
5866 currentReturnStruct->ctime = {0};
5867 currentReturnStruct->atime = {0};
5869 destination
->grfMode
= 0;
5870 destination
->grfLocksSupported
= 0;
5871 destination
->clsid
= source
->clsid
;
5872 destination
->grfStateBits
= 0;
5873 destination
->reserved
= 0;
5876 /******************************************************************************
5877 ** BlockChainStream implementation
5880 /* Read and save the index of all blocks in this stream. */
5881 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5883 ULONG next_sector
, next_offset
;
5885 struct BlockChainRun
*last_run
;
5887 if (This
->indexCacheLen
== 0)
5891 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5895 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5896 next_offset
= last_run
->lastOffset
+1;
5897 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5898 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5900 if (FAILED(hr
)) return hr
;
5903 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5905 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5907 /* Add the current block to the cache. */
5908 if (This
->indexCacheSize
== 0)
5910 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5911 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5912 This
->indexCacheSize
= 16;
5914 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5916 struct BlockChainRun
*new_cache
;
5919 new_size
= This
->indexCacheSize
* 2;
5920 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5921 if (!new_cache
) return E_OUTOFMEMORY
;
5922 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5924 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5925 This
->indexCache
= new_cache
;
5926 This
->indexCacheSize
= new_size
;
5929 This
->indexCacheLen
++;
5930 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5931 last_run
->firstSector
= next_sector
;
5932 last_run
->firstOffset
= next_offset
;
5935 last_run
->lastOffset
= next_offset
;
5937 /* Find the next block. */
5939 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5940 if (FAILED(hr
)) return hr
;
5943 if (This
->indexCacheLen
)
5945 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5946 This
->numBlocks
= last_run
->lastOffset
+1;
5950 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5951 This
->numBlocks
= 0;
5957 /* Locate the nth block in this stream. */
5958 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5960 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5961 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5963 if (offset
>= This
->numBlocks
)
5964 return BLOCK_END_OF_CHAIN
;
5966 while (min_run
< max_run
)
5968 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5969 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5971 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5972 max_run
= run_to_check
-1;
5974 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5976 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5977 min_run
= run_to_check
+1;
5980 /* Block is in this run. */
5981 min_run
= max_run
= run_to_check
;
5984 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5987 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5988 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5990 BlockChainBlock
*result
=NULL
;
5994 if (This
->cachedBlocks
[i
].index
== index
)
5996 *sector
= This
->cachedBlocks
[i
].sector
;
5997 *block
= &This
->cachedBlocks
[i
];
6001 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6002 if (*sector
== BLOCK_END_OF_CHAIN
)
6003 return STG_E_DOCFILECORRUPT
;
6007 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6008 result
= &This
->cachedBlocks
[0];
6009 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6010 result
= &This
->cachedBlocks
[1];
6013 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6014 if (This
->blockToEvict
== 2)
6015 This
->blockToEvict
= 0;
6020 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6021 return STG_E_WRITEFAULT
;
6022 result
->dirty
= FALSE
;
6025 result
->read
= FALSE
;
6026 result
->index
= index
;
6027 result
->sector
= *sector
;
6034 BlockChainStream
* BlockChainStream_Construct(
6035 StorageImpl
* parentStorage
,
6036 ULONG
* headOfStreamPlaceHolder
,
6039 BlockChainStream
* newStream
;
6041 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6043 newStream
->parentStorage
= parentStorage
;
6044 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6045 newStream
->ownerDirEntry
= dirEntry
;
6046 newStream
->indexCache
= NULL
;
6047 newStream
->indexCacheLen
= 0;
6048 newStream
->indexCacheSize
= 0;
6049 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6050 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6051 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6052 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6053 newStream
->blockToEvict
= 0;
6055 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6057 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6058 HeapFree(GetProcessHeap(), 0, newStream
);
6065 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6068 if (!This
) return S_OK
;
6071 if (This
->cachedBlocks
[i
].dirty
)
6073 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6074 This
->cachedBlocks
[i
].dirty
= FALSE
;
6076 return STG_E_WRITEFAULT
;
6082 void BlockChainStream_Destroy(BlockChainStream
* This
)
6086 BlockChainStream_Flush(This
);
6087 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6089 HeapFree(GetProcessHeap(), 0, This
);
6092 /******************************************************************************
6093 * BlockChainStream_GetHeadOfChain
6095 * Returns the head of this stream chain.
6096 * Some special chains don't have directory entries, their heads are kept in
6097 * This->headOfStreamPlaceHolder.
6100 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6102 DirEntry chainEntry
;
6105 if (This
->headOfStreamPlaceHolder
!= 0)
6106 return *(This
->headOfStreamPlaceHolder
);
6108 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6110 hr
= StorageImpl_ReadDirEntry(
6111 This
->parentStorage
,
6112 This
->ownerDirEntry
,
6117 return chainEntry
.startingBlock
;
6121 return BLOCK_END_OF_CHAIN
;
6124 /******************************************************************************
6125 * BlockChainStream_GetCount
6127 * Returns the number of blocks that comprises this chain.
6128 * This is not the size of the stream as the last block may not be full!
6130 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6132 return This
->numBlocks
;
6135 /******************************************************************************
6136 * BlockChainStream_ReadAt
6138 * Reads a specified number of bytes from this chain at the specified offset.
6139 * bytesRead may be NULL.
6140 * Failure will be returned if the specified number of bytes has not been read.
6142 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6143 ULARGE_INTEGER offset
,
6148 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6149 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6150 ULONG bytesToReadInBuffer
;
6153 ULARGE_INTEGER stream_size
;
6155 BlockChainBlock
*cachedBlock
;
6157 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6160 * Find the first block in the stream that contains part of the buffer.
6162 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6166 stream_size
= BlockChainStream_GetSize(This
);
6167 if (stream_size
.QuadPart
> offset
.QuadPart
)
6168 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6173 * Start reading the buffer.
6175 bufferWalker
= buffer
;
6179 ULARGE_INTEGER ulOffset
;
6183 * Calculate how many bytes we can copy from this big block.
6185 bytesToReadInBuffer
=
6186 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6188 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6195 /* Not in cache, and we're going to read past the end of the block. */
6196 ulOffset
.u
.HighPart
= 0;
6197 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6200 StorageImpl_ReadAt(This
->parentStorage
,
6203 bytesToReadInBuffer
,
6208 if (!cachedBlock
->read
)
6211 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
6212 return STG_E_READFAULT
;
6214 cachedBlock
->read
= TRUE
;
6217 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6218 bytesReadAt
= bytesToReadInBuffer
;
6221 blockNoInSequence
++;
6222 bufferWalker
+= bytesReadAt
;
6223 size
-= bytesReadAt
;
6224 *bytesRead
+= bytesReadAt
;
6225 offsetInBlock
= 0; /* There is no offset on the next block */
6227 if (bytesToReadInBuffer
!= bytesReadAt
)
6234 /******************************************************************************
6235 * BlockChainStream_WriteAt
6237 * Writes the specified number of bytes to this chain at the specified offset.
6238 * Will fail if not all specified number of bytes have been written.
6240 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6241 ULARGE_INTEGER offset
,
6244 ULONG
* bytesWritten
)
6246 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6247 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6250 const BYTE
* bufferWalker
;
6252 BlockChainBlock
*cachedBlock
;
6255 bufferWalker
= buffer
;
6259 ULARGE_INTEGER ulOffset
;
6260 DWORD bytesWrittenAt
;
6263 * Calculate how many bytes we can copy to this big block.
6266 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6268 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6270 /* BlockChainStream_SetSize should have already been called to ensure we have
6271 * enough blocks in the chain to write into */
6274 ERR("not enough blocks in chain to write data\n");
6280 /* Not in cache, and we're going to write past the end of the block. */
6281 ulOffset
.u
.HighPart
= 0;
6282 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6285 StorageImpl_WriteAt(This
->parentStorage
,
6293 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6296 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
6297 return STG_E_READFAULT
;
6300 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6301 bytesWrittenAt
= bytesToWrite
;
6302 cachedBlock
->read
= TRUE
;
6303 cachedBlock
->dirty
= TRUE
;
6306 blockNoInSequence
++;
6307 bufferWalker
+= bytesWrittenAt
;
6308 size
-= bytesWrittenAt
;
6309 *bytesWritten
+= bytesWrittenAt
;
6310 offsetInBlock
= 0; /* There is no offset on the next block */
6312 if (bytesWrittenAt
!= bytesToWrite
)
6316 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6319 /******************************************************************************
6320 * BlockChainStream_Shrink
6322 * Shrinks this chain in the big block depot.
6324 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6325 ULARGE_INTEGER newSize
)
6332 * Figure out how many blocks are needed to contain the new size
6334 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6336 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6342 * Go to the new end of chain
6344 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6346 /* Mark the new end of chain */
6347 StorageImpl_SetNextBlockInChain(
6348 This
->parentStorage
,
6350 BLOCK_END_OF_CHAIN
);
6352 This
->tailIndex
= blockIndex
;
6356 if (This
->headOfStreamPlaceHolder
!= 0)
6358 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6362 DirEntry chainEntry
;
6363 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6365 StorageImpl_ReadDirEntry(
6366 This
->parentStorage
,
6367 This
->ownerDirEntry
,
6370 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6372 StorageImpl_WriteDirEntry(
6373 This
->parentStorage
,
6374 This
->ownerDirEntry
,
6378 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6381 This
->numBlocks
= numBlocks
;
6384 * Mark the extra blocks as free
6386 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6388 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6389 StorageImpl_FreeBigBlock(This
->parentStorage
,
6390 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6391 if (last_run
->lastOffset
== last_run
->firstOffset
)
6392 This
->indexCacheLen
--;
6394 last_run
->lastOffset
--;
6398 * Reset the last accessed block cache.
6402 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6404 This
->cachedBlocks
[i
].index
= 0xffffffff;
6405 This
->cachedBlocks
[i
].dirty
= FALSE
;
6412 /******************************************************************************
6413 * BlockChainStream_Enlarge
6415 * Grows this chain in the big block depot.
6417 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6418 ULARGE_INTEGER newSize
)
6420 ULONG blockIndex
, currentBlock
;
6422 ULONG oldNumBlocks
= 0;
6424 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6427 * Empty chain. Create the head.
6429 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6431 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6432 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6434 BLOCK_END_OF_CHAIN
);
6436 if (This
->headOfStreamPlaceHolder
!= 0)
6438 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6442 DirEntry chainEntry
;
6443 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6445 StorageImpl_ReadDirEntry(
6446 This
->parentStorage
,
6447 This
->ownerDirEntry
,
6450 chainEntry
.startingBlock
= blockIndex
;
6452 StorageImpl_WriteDirEntry(
6453 This
->parentStorage
,
6454 This
->ownerDirEntry
,
6458 This
->tailIndex
= blockIndex
;
6459 This
->numBlocks
= 1;
6463 * Figure out how many blocks are needed to contain this stream
6465 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6467 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6471 * Go to the current end of chain
6473 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6475 currentBlock
= blockIndex
;
6477 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6480 currentBlock
= blockIndex
;
6482 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6487 This
->tailIndex
= currentBlock
;
6490 currentBlock
= This
->tailIndex
;
6491 oldNumBlocks
= This
->numBlocks
;
6494 * Add new blocks to the chain
6496 if (oldNumBlocks
< newNumBlocks
)
6498 while (oldNumBlocks
< newNumBlocks
)
6500 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6502 StorageImpl_SetNextBlockInChain(
6503 This
->parentStorage
,
6507 StorageImpl_SetNextBlockInChain(
6508 This
->parentStorage
,
6510 BLOCK_END_OF_CHAIN
);
6512 currentBlock
= blockIndex
;
6516 This
->tailIndex
= blockIndex
;
6517 This
->numBlocks
= newNumBlocks
;
6520 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6526 /******************************************************************************
6527 * BlockChainStream_SetSize
6529 * Sets the size of this stream. The big block depot will be updated.
6530 * The file will grow if we grow the chain.
6532 * TODO: Free the actual blocks in the file when we shrink the chain.
6533 * Currently, the blocks are still in the file. So the file size
6534 * doesn't shrink even if we shrink streams.
6536 BOOL
BlockChainStream_SetSize(
6537 BlockChainStream
* This
,
6538 ULARGE_INTEGER newSize
)
6540 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6542 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6545 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6547 BlockChainStream_Shrink(This
, newSize
);
6551 BlockChainStream_Enlarge(This
, newSize
);
6557 /******************************************************************************
6558 * BlockChainStream_GetSize
6560 * Returns the size of this chain.
6561 * Will return the block count if this chain doesn't have a directory entry.
6563 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6565 DirEntry chainEntry
;
6567 if(This
->headOfStreamPlaceHolder
== NULL
)
6570 * This chain has a directory entry so use the size value from there.
6572 StorageImpl_ReadDirEntry(
6573 This
->parentStorage
,
6574 This
->ownerDirEntry
,
6577 return chainEntry
.size
;
6582 * this chain is a chain that does not have a directory entry, figure out the
6583 * size by making the product number of used blocks times the
6586 ULARGE_INTEGER result
;
6587 result
.u
.HighPart
= 0;
6590 BlockChainStream_GetCount(This
) *
6591 This
->parentStorage
->bigBlockSize
;
6597 /******************************************************************************
6598 ** SmallBlockChainStream implementation
6601 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6602 StorageImpl
* parentStorage
,
6603 ULONG
* headOfStreamPlaceHolder
,
6606 SmallBlockChainStream
* newStream
;
6608 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6610 newStream
->parentStorage
= parentStorage
;
6611 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6612 newStream
->ownerDirEntry
= dirEntry
;
6617 void SmallBlockChainStream_Destroy(
6618 SmallBlockChainStream
* This
)
6620 HeapFree(GetProcessHeap(), 0, This
);
6623 /******************************************************************************
6624 * SmallBlockChainStream_GetHeadOfChain
6626 * Returns the head of this chain of small blocks.
6628 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6629 SmallBlockChainStream
* This
)
6631 DirEntry chainEntry
;
6634 if (This
->headOfStreamPlaceHolder
!= NULL
)
6635 return *(This
->headOfStreamPlaceHolder
);
6637 if (This
->ownerDirEntry
)
6639 hr
= StorageImpl_ReadDirEntry(
6640 This
->parentStorage
,
6641 This
->ownerDirEntry
,
6646 return chainEntry
.startingBlock
;
6651 return BLOCK_END_OF_CHAIN
;
6654 /******************************************************************************
6655 * SmallBlockChainStream_GetNextBlockInChain
6657 * Returns the index of the next small block in this chain.
6660 * - BLOCK_END_OF_CHAIN: end of this chain
6661 * - BLOCK_UNUSED: small block 'blockIndex' is free
6663 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6664 SmallBlockChainStream
* This
,
6666 ULONG
* nextBlockInChain
)
6668 ULARGE_INTEGER offsetOfBlockInDepot
;
6673 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6675 offsetOfBlockInDepot
.u
.HighPart
= 0;
6676 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6679 * Read those bytes in the buffer from the small block file.
6681 res
= BlockChainStream_ReadAt(
6682 This
->parentStorage
->smallBlockDepotChain
,
6683 offsetOfBlockInDepot
,
6688 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6689 res
= STG_E_READFAULT
;
6693 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6700 /******************************************************************************
6701 * SmallBlockChainStream_SetNextBlockInChain
6703 * Writes the index of the next block of the specified block in the small
6705 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6706 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6708 static void SmallBlockChainStream_SetNextBlockInChain(
6709 SmallBlockChainStream
* This
,
6713 ULARGE_INTEGER offsetOfBlockInDepot
;
6717 offsetOfBlockInDepot
.u
.HighPart
= 0;
6718 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6720 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6723 * Read those bytes in the buffer from the small block file.
6725 BlockChainStream_WriteAt(
6726 This
->parentStorage
->smallBlockDepotChain
,
6727 offsetOfBlockInDepot
,
6733 /******************************************************************************
6734 * SmallBlockChainStream_FreeBlock
6736 * Flag small block 'blockIndex' as free in the small block depot.
6738 static void SmallBlockChainStream_FreeBlock(
6739 SmallBlockChainStream
* This
,
6742 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6745 /******************************************************************************
6746 * SmallBlockChainStream_GetNextFreeBlock
6748 * Returns the index of a free small block. The small block depot will be
6749 * enlarged if necessary. The small block chain will also be enlarged if
6752 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6753 SmallBlockChainStream
* This
)
6755 ULARGE_INTEGER offsetOfBlockInDepot
;
6758 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6759 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6761 ULONG smallBlocksPerBigBlock
;
6763 ULONG blocksRequired
;
6764 ULARGE_INTEGER old_size
, size_required
;
6766 offsetOfBlockInDepot
.u
.HighPart
= 0;
6769 * Scan the small block depot for a free block
6771 while (nextBlockIndex
!= BLOCK_UNUSED
)
6773 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6775 res
= BlockChainStream_ReadAt(
6776 This
->parentStorage
->smallBlockDepotChain
,
6777 offsetOfBlockInDepot
,
6783 * If we run out of space for the small block depot, enlarge it
6785 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6787 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6789 if (nextBlockIndex
!= BLOCK_UNUSED
)
6795 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6797 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6798 ULARGE_INTEGER newSize
, offset
;
6801 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6802 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6805 * Initialize all the small blocks to free
6807 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6808 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6809 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6810 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6812 StorageImpl_SaveFileHeader(This
->parentStorage
);
6816 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6818 smallBlocksPerBigBlock
=
6819 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6822 * Verify if we have to allocate big blocks to contain small blocks
6824 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6826 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6828 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6830 if (size_required
.QuadPart
> old_size
.QuadPart
)
6832 BlockChainStream_SetSize(
6833 This
->parentStorage
->smallBlockRootChain
,
6836 StorageImpl_ReadDirEntry(
6837 This
->parentStorage
,
6838 This
->parentStorage
->base
.storageDirEntry
,
6841 rootEntry
.size
= size_required
;
6843 StorageImpl_WriteDirEntry(
6844 This
->parentStorage
,
6845 This
->parentStorage
->base
.storageDirEntry
,
6852 /******************************************************************************
6853 * SmallBlockChainStream_ReadAt
6855 * Reads a specified number of bytes from this chain at the specified offset.
6856 * bytesRead may be NULL.
6857 * Failure will be returned if the specified number of bytes has not been read.
6859 HRESULT
SmallBlockChainStream_ReadAt(
6860 SmallBlockChainStream
* This
,
6861 ULARGE_INTEGER offset
,
6867 ULARGE_INTEGER offsetInBigBlockFile
;
6868 ULONG blockNoInSequence
=
6869 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6871 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6872 ULONG bytesToReadInBuffer
;
6874 ULONG bytesReadFromBigBlockFile
;
6876 ULARGE_INTEGER stream_size
;
6879 * This should never happen on a small block file.
6881 assert(offset
.u
.HighPart
==0);
6885 stream_size
= SmallBlockChainStream_GetSize(This
);
6886 if (stream_size
.QuadPart
> offset
.QuadPart
)
6887 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6892 * Find the first block in the stream that contains part of the buffer.
6894 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6896 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6898 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6901 blockNoInSequence
--;
6905 * Start reading the buffer.
6907 bufferWalker
= buffer
;
6909 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6912 * Calculate how many bytes we can copy from this small block.
6914 bytesToReadInBuffer
=
6915 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6918 * Calculate the offset of the small block in the small block file.
6920 offsetInBigBlockFile
.u
.HighPart
= 0;
6921 offsetInBigBlockFile
.u
.LowPart
=
6922 blockIndex
* This
->parentStorage
->smallBlockSize
;
6924 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6927 * Read those bytes in the buffer from the small block file.
6928 * The small block has already been identified so it shouldn't fail
6929 * unless the file is corrupt.
6931 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6932 offsetInBigBlockFile
,
6933 bytesToReadInBuffer
,
6935 &bytesReadFromBigBlockFile
);
6940 if (!bytesReadFromBigBlockFile
)
6941 return STG_E_DOCFILECORRUPT
;
6944 * Step to the next big block.
6946 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6948 return STG_E_DOCFILECORRUPT
;
6950 bufferWalker
+= bytesReadFromBigBlockFile
;
6951 size
-= bytesReadFromBigBlockFile
;
6952 *bytesRead
+= bytesReadFromBigBlockFile
;
6953 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6959 /******************************************************************************
6960 * SmallBlockChainStream_WriteAt
6962 * Writes the specified number of bytes to this chain at the specified offset.
6963 * Will fail if not all specified number of bytes have been written.
6965 HRESULT
SmallBlockChainStream_WriteAt(
6966 SmallBlockChainStream
* This
,
6967 ULARGE_INTEGER offset
,
6970 ULONG
* bytesWritten
)
6972 ULARGE_INTEGER offsetInBigBlockFile
;
6973 ULONG blockNoInSequence
=
6974 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6976 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6977 ULONG bytesToWriteInBuffer
;
6979 ULONG bytesWrittenToBigBlockFile
;
6980 const BYTE
* bufferWalker
;
6984 * This should never happen on a small block file.
6986 assert(offset
.u
.HighPart
==0);
6989 * Find the first block in the stream that contains part of the buffer.
6991 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6993 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6995 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6996 return STG_E_DOCFILECORRUPT
;
6997 blockNoInSequence
--;
7001 * Start writing the buffer.
7004 bufferWalker
= buffer
;
7005 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7008 * Calculate how many bytes we can copy to this small block.
7010 bytesToWriteInBuffer
=
7011 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7014 * Calculate the offset of the small block in the small block file.
7016 offsetInBigBlockFile
.u
.HighPart
= 0;
7017 offsetInBigBlockFile
.u
.LowPart
=
7018 blockIndex
* This
->parentStorage
->smallBlockSize
;
7020 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
7023 * Write those bytes in the buffer to the small block file.
7025 res
= BlockChainStream_WriteAt(
7026 This
->parentStorage
->smallBlockRootChain
,
7027 offsetInBigBlockFile
,
7028 bytesToWriteInBuffer
,
7030 &bytesWrittenToBigBlockFile
);
7035 * Step to the next big block.
7037 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7040 bufferWalker
+= bytesWrittenToBigBlockFile
;
7041 size
-= bytesWrittenToBigBlockFile
;
7042 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7043 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7046 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7049 /******************************************************************************
7050 * SmallBlockChainStream_Shrink
7052 * Shrinks this chain in the small block depot.
7054 static BOOL
SmallBlockChainStream_Shrink(
7055 SmallBlockChainStream
* This
,
7056 ULARGE_INTEGER newSize
)
7058 ULONG blockIndex
, extraBlock
;
7062 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7064 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7067 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7070 * Go to the new end of chain
7072 while (count
< numBlocks
)
7074 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7081 * If the count is 0, we have a special case, the head of the chain was
7086 DirEntry chainEntry
;
7088 StorageImpl_ReadDirEntry(This
->parentStorage
,
7089 This
->ownerDirEntry
,
7092 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7094 StorageImpl_WriteDirEntry(This
->parentStorage
,
7095 This
->ownerDirEntry
,
7099 * We start freeing the chain at the head block.
7101 extraBlock
= blockIndex
;
7105 /* Get the next block before marking the new end */
7106 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7110 /* Mark the new end of chain */
7111 SmallBlockChainStream_SetNextBlockInChain(
7114 BLOCK_END_OF_CHAIN
);
7118 * Mark the extra blocks as free
7120 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7122 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7125 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7126 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7127 extraBlock
= blockIndex
;
7133 /******************************************************************************
7134 * SmallBlockChainStream_Enlarge
7136 * Grows this chain in the small block depot.
7138 static BOOL
SmallBlockChainStream_Enlarge(
7139 SmallBlockChainStream
* This
,
7140 ULARGE_INTEGER newSize
)
7142 ULONG blockIndex
, currentBlock
;
7144 ULONG oldNumBlocks
= 0;
7146 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7149 * Empty chain. Create the head.
7151 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7153 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7154 SmallBlockChainStream_SetNextBlockInChain(
7157 BLOCK_END_OF_CHAIN
);
7159 if (This
->headOfStreamPlaceHolder
!= NULL
)
7161 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7165 DirEntry chainEntry
;
7167 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7170 chainEntry
.startingBlock
= blockIndex
;
7172 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7177 currentBlock
= blockIndex
;
7180 * Figure out how many blocks are needed to contain this stream
7182 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7184 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7188 * Go to the current end of chain
7190 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7193 currentBlock
= blockIndex
;
7194 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7199 * Add new blocks to the chain
7201 while (oldNumBlocks
< newNumBlocks
)
7203 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7204 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7206 SmallBlockChainStream_SetNextBlockInChain(
7209 BLOCK_END_OF_CHAIN
);
7211 currentBlock
= blockIndex
;
7218 /******************************************************************************
7219 * SmallBlockChainStream_SetSize
7221 * Sets the size of this stream.
7222 * The file will grow if we grow the chain.
7224 * TODO: Free the actual blocks in the file when we shrink the chain.
7225 * Currently, the blocks are still in the file. So the file size
7226 * doesn't shrink even if we shrink streams.
7228 BOOL
SmallBlockChainStream_SetSize(
7229 SmallBlockChainStream
* This
,
7230 ULARGE_INTEGER newSize
)
7232 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7234 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7237 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7239 SmallBlockChainStream_Shrink(This
, newSize
);
7243 SmallBlockChainStream_Enlarge(This
, newSize
);
7249 /******************************************************************************
7250 * SmallBlockChainStream_GetCount
7252 * Returns the number of small blocks that comprises this chain.
7253 * This is not the size of the stream as the last block may not be full!
7256 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7261 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7263 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7267 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7268 blockIndex
, &blockIndex
)))
7275 /******************************************************************************
7276 * SmallBlockChainStream_GetSize
7278 * Returns the size of this chain.
7280 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7282 DirEntry chainEntry
;
7284 if(This
->headOfStreamPlaceHolder
!= NULL
)
7286 ULARGE_INTEGER result
;
7287 result
.u
.HighPart
= 0;
7289 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7290 This
->parentStorage
->smallBlockSize
;
7295 StorageImpl_ReadDirEntry(
7296 This
->parentStorage
,
7297 This
->ownerDirEntry
,
7300 return chainEntry
.size
;
7303 static HRESULT
create_storagefile(
7307 STGOPTIONS
* pStgOptions
,
7311 StorageBaseImpl
* newStorage
= 0;
7312 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7313 HRESULT hr
= STG_E_INVALIDFLAG
;
7317 DWORD fileAttributes
;
7318 WCHAR tempFileName
[MAX_PATH
];
7321 return STG_E_INVALIDPOINTER
;
7323 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7324 return STG_E_INVALIDPARAMETER
;
7326 /* if no share mode given then DENY_NONE is the default */
7327 if (STGM_SHARE_MODE(grfMode
) == 0)
7328 grfMode
|= STGM_SHARE_DENY_NONE
;
7330 if ( FAILED( validateSTGM(grfMode
) ))
7333 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7334 switch(STGM_ACCESS_MODE(grfMode
))
7337 case STGM_READWRITE
:
7343 /* in direct mode, can only use SHARE_EXCLUSIVE */
7344 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7347 /* but in transacted mode, any share mode is valid */
7350 * Generate a unique name.
7354 WCHAR tempPath
[MAX_PATH
];
7355 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7357 memset(tempPath
, 0, sizeof(tempPath
));
7358 memset(tempFileName
, 0, sizeof(tempFileName
));
7360 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7363 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7364 pwcsName
= tempFileName
;
7367 hr
= STG_E_INSUFFICIENTMEMORY
;
7371 creationMode
= TRUNCATE_EXISTING
;
7375 creationMode
= GetCreationModeFromSTGM(grfMode
);
7379 * Interpret the STGM value grfMode
7381 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7382 accessMode
= GetAccessModeFromSTGM(grfMode
);
7384 if (grfMode
& STGM_DELETEONRELEASE
)
7385 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7387 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7389 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7393 FIXME("Storage share mode not implemented.\n");
7398 hFile
= CreateFileW(pwcsName
,
7406 if (hFile
== INVALID_HANDLE_VALUE
)
7408 if(GetLastError() == ERROR_FILE_EXISTS
)
7409 hr
= STG_E_FILEALREADYEXISTS
;
7416 * Allocate and initialize the new IStorage32object.
7418 hr
= Storage_Construct(
7425 pStgOptions
->ulSectorSize
,
7433 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
7434 IStorage_Release(&newStorage
->IStorage_iface
);
7437 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7442 /******************************************************************************
7443 * StgCreateDocfile [OLE32.@]
7444 * Creates a new compound file storage object
7447 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7448 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7449 * reserved [ ?] unused?, usually 0
7450 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7453 * S_OK if the file was successfully created
7454 * some STG_E_ value if error
7456 * if pwcsName is NULL, create file with new unique name
7457 * the function can returns
7458 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7461 HRESULT WINAPI
StgCreateDocfile(
7465 IStorage
**ppstgOpen
)
7467 STGOPTIONS stgoptions
= {1, 0, 512};
7469 TRACE("(%s, %x, %d, %p)\n",
7470 debugstr_w(pwcsName
), grfMode
,
7471 reserved
, ppstgOpen
);
7474 return STG_E_INVALIDPOINTER
;
7476 return STG_E_INVALIDPARAMETER
;
7478 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7481 /******************************************************************************
7482 * StgCreateStorageEx [OLE32.@]
7484 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7486 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7487 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7489 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7491 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7492 return STG_E_INVALIDPARAMETER
;
7495 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7497 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7498 return STG_E_INVALIDPARAMETER
;
7501 if (stgfmt
== STGFMT_FILE
)
7503 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7504 return STG_E_INVALIDPARAMETER
;
7507 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7509 STGOPTIONS defaultOptions
= {1, 0, 512};
7511 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7512 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7516 ERR("Invalid stgfmt argument\n");
7517 return STG_E_INVALIDPARAMETER
;
7520 /******************************************************************************
7521 * StgCreatePropSetStg [OLE32.@]
7523 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7524 IPropertySetStorage
**propset
)
7526 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
7528 return STG_E_INVALIDPARAMETER
;
7530 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
7533 /******************************************************************************
7534 * StgOpenStorageEx [OLE32.@]
7536 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7538 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7539 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7541 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7543 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7544 return STG_E_INVALIDPARAMETER
;
7550 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7551 return STG_E_INVALIDPARAMETER
;
7553 case STGFMT_STORAGE
:
7556 case STGFMT_DOCFILE
:
7557 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7559 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7560 return STG_E_INVALIDPARAMETER
;
7562 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7566 WARN("STGFMT_ANY assuming storage\n");
7570 return STG_E_INVALIDPARAMETER
;
7573 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7577 /******************************************************************************
7578 * StgOpenStorage [OLE32.@]
7580 HRESULT WINAPI
StgOpenStorage(
7581 const OLECHAR
*pwcsName
,
7582 IStorage
*pstgPriority
,
7586 IStorage
**ppstgOpen
)
7588 StorageBaseImpl
* newStorage
= 0;
7593 LPWSTR temp_name
= NULL
;
7595 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7596 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7597 snbExclude
, reserved
, ppstgOpen
);
7601 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
7602 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
7603 if (FAILED(hr
)) goto end
;
7604 pwcsName
= temp_name
;
7605 TRACE("using filename %s\n", debugstr_w(temp_name
));
7610 hr
= STG_E_INVALIDNAME
;
7616 hr
= STG_E_INVALIDPOINTER
;
7622 hr
= STG_E_INVALIDPARAMETER
;
7626 if (grfMode
& STGM_PRIORITY
)
7628 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7629 return STG_E_INVALIDFLAG
;
7630 if (grfMode
& STGM_DELETEONRELEASE
)
7631 return STG_E_INVALIDFUNCTION
;
7632 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7633 return STG_E_INVALIDFLAG
;
7634 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7635 grfMode
|= STGM_SHARE_DENY_NONE
;
7637 /* STGM_PRIORITY stops other IStorage objects on the same file from
7638 * committing until the STGM_PRIORITY IStorage is closed. it also
7639 * stops non-transacted mode StgOpenStorage calls with write access from
7640 * succeeding. obviously, both of these cannot be achieved through just
7641 * file share flags */
7642 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7646 * Validate the sharing mode
7648 if (grfMode
& STGM_DIRECT_SWMR
)
7650 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
7651 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
7653 hr
= STG_E_INVALIDFLAG
;
7657 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7658 switch(STGM_SHARE_MODE(grfMode
))
7660 case STGM_SHARE_EXCLUSIVE
:
7661 case STGM_SHARE_DENY_WRITE
:
7664 hr
= STG_E_INVALIDFLAG
;
7668 if ( FAILED( validateSTGM(grfMode
) ) ||
7669 (grfMode
&STGM_CREATE
))
7671 hr
= STG_E_INVALIDFLAG
;
7675 /* shared reading requires transacted or single writer mode */
7676 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7677 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7678 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
7680 hr
= STG_E_INVALIDFLAG
;
7685 * Interpret the STGM value grfMode
7687 shareMode
= GetShareModeFromSTGM(grfMode
);
7688 accessMode
= GetAccessModeFromSTGM(grfMode
);
7692 hFile
= CreateFileW( pwcsName
,
7697 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7700 if (hFile
==INVALID_HANDLE_VALUE
)
7702 DWORD last_error
= GetLastError();
7708 case ERROR_FILE_NOT_FOUND
:
7709 hr
= STG_E_FILENOTFOUND
;
7712 case ERROR_PATH_NOT_FOUND
:
7713 hr
= STG_E_PATHNOTFOUND
;
7716 case ERROR_ACCESS_DENIED
:
7717 case ERROR_WRITE_PROTECT
:
7718 hr
= STG_E_ACCESSDENIED
;
7721 case ERROR_SHARING_VIOLATION
:
7722 hr
= STG_E_SHAREVIOLATION
;
7733 * Refuse to open the file if it's too small to be a structured storage file
7734 * FIXME: verify the file when reading instead of here
7736 if (GetFileSize(hFile
, NULL
) < 0x100)
7739 hr
= STG_E_FILEALREADYEXISTS
;
7744 * Allocate and initialize the new IStorage32object.
7746 hr
= Storage_Construct(
7759 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7761 if(hr
== STG_E_INVALIDHEADER
)
7762 hr
= STG_E_FILEALREADYEXISTS
;
7766 *ppstgOpen
= &newStorage
->IStorage_iface
;
7769 CoTaskMemFree(temp_name
);
7770 if (pstgPriority
) IStorage_Release(pstgPriority
);
7771 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7775 /******************************************************************************
7776 * StgCreateDocfileOnILockBytes [OLE32.@]
7778 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7782 IStorage
** ppstgOpen
)
7784 StorageBaseImpl
* newStorage
= 0;
7787 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7788 return STG_E_INVALIDPOINTER
;
7791 * Allocate and initialize the new IStorage object.
7793 hr
= Storage_Construct(
7808 *ppstgOpen
= &newStorage
->IStorage_iface
;
7813 /******************************************************************************
7814 * StgOpenStorageOnILockBytes [OLE32.@]
7816 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7818 IStorage
*pstgPriority
,
7822 IStorage
**ppstgOpen
)
7824 StorageBaseImpl
* newStorage
= 0;
7827 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7828 return STG_E_INVALIDPOINTER
;
7830 if ( FAILED( validateSTGM(grfMode
) ))
7831 return STG_E_INVALIDFLAG
;
7836 * Allocate and initialize the new IStorage object.
7838 hr
= Storage_Construct(
7853 *ppstgOpen
= &newStorage
->IStorage_iface
;
7858 /******************************************************************************
7859 * StgSetTimes [ole32.@]
7860 * StgSetTimes [OLE32.@]
7864 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7865 FILETIME
const *patime
, FILETIME
const *pmtime
)
7867 IStorage
*stg
= NULL
;
7870 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7872 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7876 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7877 IStorage_Release(stg
);
7883 /******************************************************************************
7884 * StgIsStorageILockBytes [OLE32.@]
7886 * Determines if the ILockBytes contains a storage object.
7888 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7890 BYTE sig
[sizeof(STORAGE_magic
)];
7891 ULARGE_INTEGER offset
;
7894 offset
.u
.HighPart
= 0;
7895 offset
.u
.LowPart
= 0;
7897 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7899 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7905 /******************************************************************************
7906 * WriteClassStg [OLE32.@]
7908 * This method will store the specified CLSID in the specified storage object
7910 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7913 return E_INVALIDARG
;
7916 return STG_E_INVALIDPOINTER
;
7918 return IStorage_SetClass(pStg
, rclsid
);
7921 /***********************************************************************
7922 * ReadClassStg (OLE32.@)
7924 * This method reads the CLSID previously written to a storage object with
7925 * the WriteClassStg.
7928 * pstg [I] IStorage pointer
7929 * pclsid [O] Pointer to where the CLSID is written
7933 * Failure: HRESULT code.
7935 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7940 TRACE("(%p, %p)\n", pstg
, pclsid
);
7942 if(!pstg
|| !pclsid
)
7943 return E_INVALIDARG
;
7946 * read a STATSTG structure (contains the clsid) from the storage
7948 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7951 *pclsid
=pstatstg
.clsid
;
7956 /***********************************************************************
7957 * OleLoadFromStream (OLE32.@)
7959 * This function loads an object from stream
7961 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7965 LPPERSISTSTREAM xstm
;
7967 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7969 res
=ReadClassStm(pStm
,&clsid
);
7972 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7975 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7977 IUnknown_Release((IUnknown
*)*ppvObj
);
7980 res
=IPersistStream_Load(xstm
,pStm
);
7981 IPersistStream_Release(xstm
);
7982 /* FIXME: all refcounts ok at this point? I think they should be:
7985 * xstm : 0 (released)
7990 /***********************************************************************
7991 * OleSaveToStream (OLE32.@)
7993 * This function saves an object with the IPersistStream interface on it
7994 * to the specified stream.
7996 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8002 TRACE("(%p,%p)\n",pPStm
,pStm
);
8004 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8006 if (SUCCEEDED(res
)){
8008 res
=WriteClassStm(pStm
,&clsid
);
8012 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8015 TRACE("Finished Save\n");
8019 /****************************************************************************
8020 * This method validate a STGM parameter that can contain the values below
8022 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8023 * The stgm values contained in 0xffff0000 are bitmasks.
8025 * STGM_DIRECT 0x00000000
8026 * STGM_TRANSACTED 0x00010000
8027 * STGM_SIMPLE 0x08000000
8029 * STGM_READ 0x00000000
8030 * STGM_WRITE 0x00000001
8031 * STGM_READWRITE 0x00000002
8033 * STGM_SHARE_DENY_NONE 0x00000040
8034 * STGM_SHARE_DENY_READ 0x00000030
8035 * STGM_SHARE_DENY_WRITE 0x00000020
8036 * STGM_SHARE_EXCLUSIVE 0x00000010
8038 * STGM_PRIORITY 0x00040000
8039 * STGM_DELETEONRELEASE 0x04000000
8041 * STGM_CREATE 0x00001000
8042 * STGM_CONVERT 0x00020000
8043 * STGM_FAILIFTHERE 0x00000000
8045 * STGM_NOSCRATCH 0x00100000
8046 * STGM_NOSNAPSHOT 0x00200000
8048 static HRESULT
validateSTGM(DWORD stgm
)
8050 DWORD access
= STGM_ACCESS_MODE(stgm
);
8051 DWORD share
= STGM_SHARE_MODE(stgm
);
8052 DWORD create
= STGM_CREATE_MODE(stgm
);
8054 if (stgm
&~STGM_KNOWN_FLAGS
)
8056 ERR("unknown flags %08x\n", stgm
);
8064 case STGM_READWRITE
:
8072 case STGM_SHARE_DENY_NONE
:
8073 case STGM_SHARE_DENY_READ
:
8074 case STGM_SHARE_DENY_WRITE
:
8075 case STGM_SHARE_EXCLUSIVE
:
8084 case STGM_FAILIFTHERE
:
8091 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8093 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8097 * STGM_CREATE | STGM_CONVERT
8098 * if both are false, STGM_FAILIFTHERE is set to TRUE
8100 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8104 * STGM_NOSCRATCH requires STGM_TRANSACTED
8106 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8110 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8111 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8113 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8114 (!(stgm
& STGM_TRANSACTED
) ||
8115 share
== STGM_SHARE_EXCLUSIVE
||
8116 share
== STGM_SHARE_DENY_WRITE
) )
8122 /****************************************************************************
8123 * GetShareModeFromSTGM
8125 * This method will return a share mode flag from a STGM value.
8126 * The STGM value is assumed valid.
8128 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8130 switch (STGM_SHARE_MODE(stgm
))
8132 case STGM_SHARE_DENY_NONE
:
8133 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8134 case STGM_SHARE_DENY_READ
:
8135 return FILE_SHARE_WRITE
;
8136 case STGM_SHARE_DENY_WRITE
:
8137 return FILE_SHARE_READ
;
8138 case STGM_SHARE_EXCLUSIVE
:
8141 ERR("Invalid share mode!\n");
8146 /****************************************************************************
8147 * GetAccessModeFromSTGM
8149 * This method will return an access mode flag from a STGM value.
8150 * The STGM value is assumed valid.
8152 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8154 switch (STGM_ACCESS_MODE(stgm
))
8157 return GENERIC_READ
;
8159 case STGM_READWRITE
:
8160 return GENERIC_READ
| GENERIC_WRITE
;
8162 ERR("Invalid access mode!\n");
8167 /****************************************************************************
8168 * GetCreationModeFromSTGM
8170 * This method will return a creation mode flag from a STGM value.
8171 * The STGM value is assumed valid.
8173 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8175 switch(STGM_CREATE_MODE(stgm
))
8178 return CREATE_ALWAYS
;
8180 FIXME("STGM_CONVERT not implemented!\n");
8182 case STGM_FAILIFTHERE
:
8185 ERR("Invalid create mode!\n");
8191 /*************************************************************************
8192 * OLECONVERT_LoadOLE10 [Internal]
8194 * Loads the OLE10 STREAM to memory
8197 * pOleStream [I] The OLESTREAM
8198 * pData [I] Data Structure for the OLESTREAM Data
8202 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8203 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8206 * This function is used by OleConvertOLESTREAMToIStorage only.
8208 * Memory allocated for pData must be freed by the caller
8210 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8213 HRESULT hRes
= S_OK
;
8217 pData
->pData
= NULL
;
8218 pData
->pstrOleObjFileName
= NULL
;
8220 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8223 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8224 if(dwSize
!= sizeof(pData
->dwOleID
))
8226 hRes
= CONVERT10_E_OLESTREAM_GET
;
8228 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8230 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8241 /* Get the TypeID... more info needed for this field */
8242 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8243 if(dwSize
!= sizeof(pData
->dwTypeID
))
8245 hRes
= CONVERT10_E_OLESTREAM_GET
;
8250 if(pData
->dwTypeID
!= 0)
8252 /* Get the length of the OleTypeName */
8253 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8254 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8256 hRes
= CONVERT10_E_OLESTREAM_GET
;
8261 if(pData
->dwOleTypeNameLength
> 0)
8263 /* Get the OleTypeName */
8264 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8265 if(dwSize
!= pData
->dwOleTypeNameLength
)
8267 hRes
= CONVERT10_E_OLESTREAM_GET
;
8273 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8274 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8276 hRes
= CONVERT10_E_OLESTREAM_GET
;
8280 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8281 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8282 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8283 if(pData
->pstrOleObjFileName
)
8285 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8286 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8288 hRes
= CONVERT10_E_OLESTREAM_GET
;
8292 hRes
= CONVERT10_E_OLESTREAM_GET
;
8297 /* Get the Width of the Metafile */
8298 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8299 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8301 hRes
= CONVERT10_E_OLESTREAM_GET
;
8305 /* Get the Height of the Metafile */
8306 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8307 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8309 hRes
= CONVERT10_E_OLESTREAM_GET
;
8315 /* Get the Length of the Data */
8316 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8317 if(dwSize
!= sizeof(pData
->dwDataLength
))
8319 hRes
= CONVERT10_E_OLESTREAM_GET
;
8323 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8325 if(!bStrem1
) /* if it is a second OLE stream data */
8327 pData
->dwDataLength
-= 8;
8328 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8329 if(dwSize
!= sizeof(pData
->strUnknown
))
8331 hRes
= CONVERT10_E_OLESTREAM_GET
;
8337 if(pData
->dwDataLength
> 0)
8339 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8341 /* Get Data (ex. IStorage, Metafile, or BMP) */
8344 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8345 if(dwSize
!= pData
->dwDataLength
)
8347 hRes
= CONVERT10_E_OLESTREAM_GET
;
8352 hRes
= CONVERT10_E_OLESTREAM_GET
;
8361 /*************************************************************************
8362 * OLECONVERT_SaveOLE10 [Internal]
8364 * Saves the OLE10 STREAM From memory
8367 * pData [I] Data Structure for the OLESTREAM Data
8368 * pOleStream [I] The OLESTREAM to save
8372 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8375 * This function is used by OleConvertIStorageToOLESTREAM only.
8378 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8381 HRESULT hRes
= S_OK
;
8385 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8386 if(dwSize
!= sizeof(pData
->dwOleID
))
8388 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8393 /* Set the TypeID */
8394 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8395 if(dwSize
!= sizeof(pData
->dwTypeID
))
8397 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8401 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8403 /* Set the Length of the OleTypeName */
8404 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8405 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8407 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8412 if(pData
->dwOleTypeNameLength
> 0)
8414 /* Set the OleTypeName */
8415 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8416 if(dwSize
!= pData
->dwOleTypeNameLength
)
8418 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8425 /* Set the width of the Metafile */
8426 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8427 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8429 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8435 /* Set the height of the Metafile */
8436 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8437 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8439 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8445 /* Set the length of the Data */
8446 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8447 if(dwSize
!= sizeof(pData
->dwDataLength
))
8449 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8455 if(pData
->dwDataLength
> 0)
8457 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8458 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8459 if(dwSize
!= pData
->dwDataLength
)
8461 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8469 /*************************************************************************
8470 * OLECONVERT_GetOLE20FromOLE10[Internal]
8472 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8473 * opens it, and copies the content to the dest IStorage for
8474 * OleConvertOLESTREAMToIStorage
8478 * pDestStorage [I] The IStorage to copy the data to
8479 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8480 * nBufferLength [I] The size of the buffer
8489 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8493 IStorage
*pTempStorage
;
8494 DWORD dwNumOfBytesWritten
;
8495 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8496 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8498 /* Create a temp File */
8499 GetTempPathW(MAX_PATH
, wstrTempDir
);
8500 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8501 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8503 if(hFile
!= INVALID_HANDLE_VALUE
)
8505 /* Write IStorage Data to File */
8506 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8509 /* Open and copy temp storage to the Dest Storage */
8510 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8513 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8514 IStorage_Release(pTempStorage
);
8516 DeleteFileW(wstrTempFile
);
8521 /*************************************************************************
8522 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8524 * Saves the OLE10 STREAM From memory
8527 * pStorage [I] The Src IStorage to copy
8528 * pData [I] The Dest Memory to write to.
8531 * The size in bytes allocated for pData
8534 * Memory allocated for pData must be freed by the caller
8536 * Used by OleConvertIStorageToOLESTREAM only.
8539 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8543 DWORD nDataLength
= 0;
8544 IStorage
*pTempStorage
;
8545 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8546 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8550 /* Create temp Storage */
8551 GetTempPathW(MAX_PATH
, wstrTempDir
);
8552 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8553 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8557 /* Copy Src Storage to the Temp Storage */
8558 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8559 IStorage_Release(pTempStorage
);
8561 /* Open Temp Storage as a file and copy to memory */
8562 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8563 if(hFile
!= INVALID_HANDLE_VALUE
)
8565 nDataLength
= GetFileSize(hFile
, NULL
);
8566 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8567 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8570 DeleteFileW(wstrTempFile
);
8575 /*************************************************************************
8576 * STORAGE_CreateOleStream [Internal]
8578 * Creates the "\001OLE" stream in the IStorage if necessary.
8581 * storage [I] Dest storage to create the stream in
8582 * flags [I] flags to be set for newly created stream
8585 * HRESULT return value
8589 * This stream is still unknown, MS Word seems to have extra data
8590 * but since the data is stored in the OLESTREAM there should be
8591 * no need to recreate the stream. If the stream is manually
8592 * deleted it will create it with this default data.
8595 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
8597 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
8598 static const DWORD version_magic
= 0x02000001;
8602 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
8605 struct empty_1ole_stream
{
8606 DWORD version_magic
;
8608 DWORD update_options
;
8610 DWORD mon_stream_size
;
8612 struct empty_1ole_stream stream_data
;
8614 stream_data
.version_magic
= version_magic
;
8615 stream_data
.flags
= flags
;
8616 stream_data
.update_options
= 0;
8617 stream_data
.reserved
= 0;
8618 stream_data
.mon_stream_size
= 0;
8620 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
8621 IStream_Release(stream
);
8627 /* write a string to a stream, preceded by its length */
8628 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8635 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8636 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8641 str
= CoTaskMemAlloc( len
);
8642 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8643 r
= IStream_Write( stm
, str
, len
, NULL
);
8644 CoTaskMemFree( str
);
8648 /* read a string preceded by its length from a stream */
8649 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8652 DWORD len
, count
= 0;
8656 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8659 if( count
!= sizeof(len
) )
8660 return E_OUTOFMEMORY
;
8662 TRACE("%d bytes\n",len
);
8664 str
= CoTaskMemAlloc( len
);
8666 return E_OUTOFMEMORY
;
8668 r
= IStream_Read( stm
, str
, len
, &count
);
8673 CoTaskMemFree( str
);
8674 return E_OUTOFMEMORY
;
8677 TRACE("Read string %s\n",debugstr_an(str
,len
));
8679 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8680 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8683 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8686 CoTaskMemFree( str
);
8694 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8695 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8699 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8701 static const BYTE unknown1
[12] =
8702 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8703 0xFF, 0xFF, 0xFF, 0xFF};
8704 static const BYTE unknown2
[16] =
8705 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8706 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8708 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8709 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8710 debugstr_w(szProgIDName
));
8712 /* Create a CompObj stream */
8713 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8714 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8718 /* Write CompObj Structure to stream */
8719 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8721 if( SUCCEEDED( r
) )
8722 r
= WriteClassStm( pstm
, clsid
);
8724 if( SUCCEEDED( r
) )
8725 r
= STREAM_WriteString( pstm
, lpszUserType
);
8726 if( SUCCEEDED( r
) )
8727 r
= STREAM_WriteString( pstm
, szClipName
);
8728 if( SUCCEEDED( r
) )
8729 r
= STREAM_WriteString( pstm
, szProgIDName
);
8730 if( SUCCEEDED( r
) )
8731 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8733 IStream_Release( pstm
);
8738 /***********************************************************************
8739 * WriteFmtUserTypeStg (OLE32.@)
8741 HRESULT WINAPI
WriteFmtUserTypeStg(
8742 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8746 WCHAR szwClipName
[0x40];
8748 LPWSTR wstrProgID
= NULL
;
8751 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8753 /* get the clipboard format name */
8756 n
= GetClipboardFormatNameW( cf
, szwClipName
,
8757 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8761 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8763 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
8769 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8771 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8773 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
8774 cf
? szwClipName
: NULL
, wstrProgID
);
8776 CoTaskMemFree(wstrProgID
);
8782 /******************************************************************************
8783 * ReadFmtUserTypeStg [OLE32.@]
8785 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8789 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8790 unsigned char unknown1
[12];
8791 unsigned char unknown2
[16];
8793 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8796 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8798 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8799 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8802 WARN("Failed to open stream r = %08x\n", r
);
8806 /* read the various parts of the structure */
8807 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8808 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8810 r
= ReadClassStm( stm
, &clsid
);
8814 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8818 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8822 r
= STREAM_ReadString( stm
, &szProgIDName
);
8826 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8827 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8830 /* ok, success... now we just need to store what we found */
8832 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8834 if( lplpszUserType
)
8836 *lplpszUserType
= szCLSIDName
;
8841 CoTaskMemFree( szCLSIDName
);
8842 CoTaskMemFree( szOleTypeName
);
8843 CoTaskMemFree( szProgIDName
);
8844 IStream_Release( stm
);
8850 /*************************************************************************
8851 * OLECONVERT_CreateCompObjStream [Internal]
8853 * Creates a "\001CompObj" is the destination IStorage if necessary.
8856 * pStorage [I] The dest IStorage to create the CompObj Stream
8858 * strOleTypeName [I] The ProgID
8862 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8865 * This function is used by OleConvertOLESTREAMToIStorage only.
8867 * The stream data is stored in the OLESTREAM and there should be
8868 * no need to recreate the stream. If the stream is manually
8869 * deleted it will attempt to create it by querying the registry.
8873 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8876 HRESULT hStorageRes
, hRes
= S_OK
;
8877 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8878 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8879 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8881 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8882 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8884 /* Initialize the CompObj structure */
8885 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8886 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8887 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8890 /* Create a CompObj stream if it doesn't exist */
8891 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8892 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8893 if(hStorageRes
== S_OK
)
8895 /* copy the OleTypeName to the compobj struct */
8896 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8897 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8899 /* copy the OleTypeName to the compobj struct */
8900 /* Note: in the test made, these were Identical */
8901 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8902 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8905 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8906 bufferW
, OLESTREAM_MAX_STR_LEN
);
8907 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8913 /* Get the CLSID Default Name from the Registry */
8914 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
8915 if(hErr
== ERROR_SUCCESS
)
8917 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8918 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8919 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8920 if(hErr
== ERROR_SUCCESS
)
8922 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8928 /* Write CompObj Structure to stream */
8929 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8931 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8933 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8934 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8936 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8938 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8939 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8941 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8943 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8944 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8946 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8948 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8949 IStream_Release(pStream
);
8955 /*************************************************************************
8956 * OLECONVERT_CreateOlePresStream[Internal]
8958 * Creates the "\002OlePres000" Stream with the Metafile data
8961 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8962 * dwExtentX [I] Width of the Metafile
8963 * dwExtentY [I] Height of the Metafile
8964 * pData [I] Metafile data
8965 * dwDataLength [I] Size of the Metafile data
8969 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8972 * This function is used by OleConvertOLESTREAMToIStorage only.
8975 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8979 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8980 BYTE pOlePresStreamHeader
[] =
8982 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8983 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8984 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8985 0x00, 0x00, 0x00, 0x00
8988 BYTE pOlePresStreamHeaderEmpty
[] =
8990 0x00, 0x00, 0x00, 0x00,
8991 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8992 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8993 0x00, 0x00, 0x00, 0x00
8996 /* Create the OlePres000 Stream */
8997 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8998 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9003 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9005 memset(&OlePres
, 0, sizeof(OlePres
));
9006 /* Do we have any metafile data to save */
9007 if(dwDataLength
> 0)
9009 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9010 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9014 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9015 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9017 /* Set width and height of the metafile */
9018 OlePres
.dwExtentX
= dwExtentX
;
9019 OlePres
.dwExtentY
= -dwExtentY
;
9021 /* Set Data and Length */
9022 if(dwDataLength
> sizeof(METAFILEPICT16
))
9024 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9025 OlePres
.pData
= &(pData
[8]);
9027 /* Save OlePres000 Data to Stream */
9028 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9029 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9030 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9031 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9032 if(OlePres
.dwSize
> 0)
9034 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9036 IStream_Release(pStream
);
9040 /*************************************************************************
9041 * OLECONVERT_CreateOle10NativeStream [Internal]
9043 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9046 * pStorage [I] Dest storage to create the stream in
9047 * pData [I] Ole10 Native Data (ex. bmp)
9048 * dwDataLength [I] Size of the Ole10 Native Data
9054 * This function is used by OleConvertOLESTREAMToIStorage only.
9056 * Might need to verify the data and return appropriate error message
9059 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9063 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9065 /* Create the Ole10Native Stream */
9066 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9067 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9071 /* Write info to stream */
9072 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9073 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9074 IStream_Release(pStream
);
9079 /*************************************************************************
9080 * OLECONVERT_GetOLE10ProgID [Internal]
9082 * Finds the ProgID (or OleTypeID) from the IStorage
9085 * pStorage [I] The Src IStorage to get the ProgID
9086 * strProgID [I] the ProgID string to get
9087 * dwSize [I] the size of the string
9091 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9094 * This function is used by OleConvertIStorageToOLESTREAM only.
9098 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9102 LARGE_INTEGER iSeekPos
;
9103 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9104 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9106 /* Open the CompObj Stream */
9107 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9108 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9112 /*Get the OleType from the CompObj Stream */
9113 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9114 iSeekPos
.u
.HighPart
= 0;
9116 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9117 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9118 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9119 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9120 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9121 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9122 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9124 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9127 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9129 IStream_Release(pStream
);
9134 LPOLESTR wstrProgID
;
9136 /* Get the OleType from the registry */
9137 REFCLSID clsid
= &(stat
.clsid
);
9138 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9139 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9142 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9143 CoTaskMemFree(wstrProgID
);
9150 /*************************************************************************
9151 * OLECONVERT_GetOle10PresData [Internal]
9153 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9156 * pStorage [I] Src IStroage
9157 * pOleStream [I] Dest OleStream Mem Struct
9163 * This function is used by OleConvertIStorageToOLESTREAM only.
9165 * Memory allocated for pData must be freed by the caller
9169 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9174 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9176 /* Initialize Default data for OLESTREAM */
9177 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9178 pOleStreamData
[0].dwTypeID
= 2;
9179 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9180 pOleStreamData
[1].dwTypeID
= 0;
9181 pOleStreamData
[0].dwMetaFileWidth
= 0;
9182 pOleStreamData
[0].dwMetaFileHeight
= 0;
9183 pOleStreamData
[0].pData
= NULL
;
9184 pOleStreamData
[1].pData
= NULL
;
9186 /* Open Ole10Native Stream */
9187 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9188 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9192 /* Read Size and Data */
9193 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9194 if(pOleStreamData
->dwDataLength
> 0)
9196 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9197 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9199 IStream_Release(pStream
);
9205 /*************************************************************************
9206 * OLECONVERT_GetOle20PresData[Internal]
9208 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9211 * pStorage [I] Src IStroage
9212 * pOleStreamData [I] Dest OleStream Mem Struct
9218 * This function is used by OleConvertIStorageToOLESTREAM only.
9220 * Memory allocated for pData must be freed by the caller
9222 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9226 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9227 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9229 /* Initialize Default data for OLESTREAM */
9230 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9231 pOleStreamData
[0].dwTypeID
= 2;
9232 pOleStreamData
[0].dwMetaFileWidth
= 0;
9233 pOleStreamData
[0].dwMetaFileHeight
= 0;
9234 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9235 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9236 pOleStreamData
[1].dwTypeID
= 0;
9237 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9238 pOleStreamData
[1].strOleTypeName
[0] = 0;
9239 pOleStreamData
[1].dwMetaFileWidth
= 0;
9240 pOleStreamData
[1].dwMetaFileHeight
= 0;
9241 pOleStreamData
[1].pData
= NULL
;
9242 pOleStreamData
[1].dwDataLength
= 0;
9245 /* Open OlePress000 stream */
9246 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9247 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9250 LARGE_INTEGER iSeekPos
;
9251 METAFILEPICT16 MetaFilePict
;
9252 static const char strMetafilePictName
[] = "METAFILEPICT";
9254 /* Set the TypeID for a Metafile */
9255 pOleStreamData
[1].dwTypeID
= 5;
9257 /* Set the OleTypeName to Metafile */
9258 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9259 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9261 iSeekPos
.u
.HighPart
= 0;
9262 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9264 /* Get Presentation Data */
9265 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9266 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9267 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9268 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9270 /*Set width and Height */
9271 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9272 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9273 if(olePress
.dwSize
> 0)
9276 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9278 /* Set MetaFilePict struct */
9279 MetaFilePict
.mm
= 8;
9280 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9281 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9282 MetaFilePict
.hMF
= 0;
9284 /* Get Metafile Data */
9285 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9286 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9287 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9289 IStream_Release(pStream
);
9293 /*************************************************************************
9294 * OleConvertOLESTREAMToIStorage [OLE32.@]
9299 * DVTARGETDEVICE parameter is not handled
9300 * Still unsure of some mem fields for OLE 10 Stream
9301 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9302 * and "\001OLE" streams
9305 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9306 LPOLESTREAM pOleStream
,
9308 const DVTARGETDEVICE
* ptd
)
9312 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9314 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9316 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9320 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9323 if(pstg
== NULL
|| pOleStream
== NULL
)
9325 hRes
= E_INVALIDARG
;
9330 /* Load the OLESTREAM to Memory */
9331 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9336 /* Load the OLESTREAM to Memory (part 2)*/
9337 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9343 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9345 /* Do we have the IStorage Data in the OLESTREAM */
9346 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9348 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9349 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9353 /* It must be an original OLE 1.0 source */
9354 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9359 /* It must be an original OLE 1.0 source */
9360 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9363 /* Create CompObj Stream if necessary */
9364 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9367 /*Create the Ole Stream if necessary */
9368 STORAGE_CreateOleStream(pstg
, 0);
9373 /* Free allocated memory */
9374 for(i
=0; i
< 2; i
++)
9376 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9377 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9378 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9383 /*************************************************************************
9384 * OleConvertIStorageToOLESTREAM [OLE32.@]
9391 * Still unsure of some mem fields for OLE 10 Stream
9392 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9393 * and "\001OLE" streams.
9396 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9398 LPOLESTREAM pOleStream
)
9401 HRESULT hRes
= S_OK
;
9403 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9404 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9406 TRACE("%p %p\n", pstg
, pOleStream
);
9408 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9410 if(pstg
== NULL
|| pOleStream
== NULL
)
9412 hRes
= E_INVALIDARG
;
9416 /* Get the ProgID */
9417 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9418 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9422 /* Was it originally Ole10 */
9423 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9426 IStream_Release(pStream
);
9427 /* Get Presentation Data for Ole10Native */
9428 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9432 /* Get Presentation Data (OLE20) */
9433 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9436 /* Save OLESTREAM */
9437 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9440 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9445 /* Free allocated memory */
9446 for(i
=0; i
< 2; i
++)
9448 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9454 enum stream_1ole_flags
{
9455 OleStream_LinkedObject
= 0x00000001,
9456 OleStream_Convert
= 0x00000004
9459 /***********************************************************************
9460 * GetConvertStg (OLE32.@)
9462 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
9464 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9465 static const DWORD version_magic
= 0x02000001;
9472 if (!stg
) return E_INVALIDARG
;
9474 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9475 if (FAILED(hr
)) return hr
;
9477 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9478 IStream_Release(stream
);
9479 if (FAILED(hr
)) return hr
;
9481 if (header
[0] != version_magic
)
9483 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
9487 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
9490 /***********************************************************************
9491 * SetConvertStg (OLE32.@)
9493 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
9495 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9496 DWORD flags
= convert
? OleStream_Convert
: 0;
9501 TRACE("(%p, %d)\n", storage
, convert
);
9503 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9506 if (hr
!= STG_E_FILENOTFOUND
)
9509 return STORAGE_CreateOleStream(storage
, flags
);
9512 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9515 IStream_Release(stream
);
9519 /* update flag if differs */
9520 if ((header
[1] ^ flags
) & OleStream_Convert
)
9522 LARGE_INTEGER pos
= {{0}};
9524 if (header
[1] & OleStream_Convert
)
9525 flags
= header
[1] & ~OleStream_Convert
;
9527 flags
= header
[1] | OleStream_Convert
;
9529 pos
.QuadPart
= sizeof(DWORD
);
9530 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
9533 IStream_Release(stream
);
9537 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
9540 IStream_Release(stream
);
9544 /******************************************************************************
9545 * StgIsStorageFile [OLE32.@]
9546 * Verify if the file contains a storage object
9552 * S_OK if file has magic bytes as a storage object
9553 * S_FALSE if file is not storage
9556 StgIsStorageFile(LPCOLESTR fn
)
9562 TRACE("%s\n", debugstr_w(fn
));
9563 hf
= CreateFileW(fn
, GENERIC_READ
,
9564 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9565 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9567 if (hf
== INVALID_HANDLE_VALUE
)
9568 return STG_E_FILENOTFOUND
;
9570 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9572 WARN(" unable to read file\n");
9579 if (bytes_read
!= 8) {
9580 TRACE(" too short\n");
9584 if (!memcmp(magic
,STORAGE_magic
,8)) {
9589 TRACE(" -> Invalid header.\n");
9593 /***********************************************************************
9594 * WriteClassStm (OLE32.@)
9596 * Writes a CLSID to a stream.
9599 * pStm [I] Stream to write to.
9600 * rclsid [I] CLSID to write.
9604 * Failure: HRESULT code.
9606 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9608 TRACE("(%p,%p)\n",pStm
,rclsid
);
9610 if (!pStm
|| !rclsid
)
9611 return E_INVALIDARG
;
9613 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9616 /***********************************************************************
9617 * ReadClassStm (OLE32.@)
9619 * Reads a CLSID from a stream.
9622 * pStm [I] Stream to read from.
9623 * rclsid [O] CLSID to read.
9627 * Failure: HRESULT code.
9629 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9634 TRACE("(%p,%p)\n",pStm
,pclsid
);
9636 if (!pStm
|| !pclsid
)
9637 return E_INVALIDARG
;
9639 /* clear the output args */
9640 *pclsid
= CLSID_NULL
;
9642 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9647 if (nbByte
!= sizeof(CLSID
))
9648 return STG_E_READFAULT
;