2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
70 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base
;
85 * Entry in the parent's stream tracking list
87 struct list ParentListEntry
;
89 StorageBaseImpl
*parentStorage
;
91 typedef struct StorageInternalImpl StorageInternalImpl
;
93 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
94 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
96 /* Method definitions for the Storage32InternalImpl class. */
97 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
98 DWORD openFlags
, DirRef storageDirEntry
);
99 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
100 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
101 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
102 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
103 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
104 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
105 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
106 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
108 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
109 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
110 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
111 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
112 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
114 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
115 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
116 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
118 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
119 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
120 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
121 ULONG blockIndex
, ULONG offset
, DWORD value
);
122 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
123 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
125 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
126 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
128 typedef struct TransactedDirEntry
130 /* If applicable, a reference to the original DirEntry in the transacted
131 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
132 DirRef transactedParentEntry
;
134 /* True if this entry is being used. */
137 /* True if data is up to date. */
140 /* True if this entry has been modified. */
143 /* True if this entry's stream has been modified. */
146 /* True if this entry has been deleted in the transacted storage, but the
147 * delete has not yet been committed. */
150 /* If this entry's stream has been modified, a reference to where the stream
151 * is stored in the snapshot file. */
154 /* This directory entry's data, including any changes that have been made. */
157 /* A reference to the parent of this node. This is only valid while we are
158 * committing changes. */
161 /* A reference to a newly-created entry in the transacted parent. This is
162 * always equal to transactedParentEntry except when committing changes. */
163 DirRef newTransactedParentEntry
;
164 } TransactedDirEntry
;
166 /****************************************************************************
167 * Transacted storage object.
169 typedef struct TransactedSnapshotImpl
171 struct StorageBaseImpl base
;
174 * Modified streams are temporarily saved to the scratch file.
176 StorageBaseImpl
*scratch
;
178 /* The directory structure is kept here, so that we can track how these
179 * entries relate to those in the parent storage. */
180 TransactedDirEntry
*entries
;
182 ULONG firstFreeEntry
;
185 * Changes are committed to the transacted parent.
187 StorageBaseImpl
*transactedParent
;
188 } TransactedSnapshotImpl
;
190 /* Generic function to create a transacted wrapper for a direct storage object. */
191 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
193 /* OLESTREAM memory structure to use for Get and Put Routines */
194 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
199 DWORD dwOleTypeNameLength
;
200 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
201 CHAR
*pstrOleObjFileName
;
202 DWORD dwOleObjFileNameLength
;
203 DWORD dwMetaFileWidth
;
204 DWORD dwMetaFileHeight
;
205 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
208 }OLECONVERT_OLESTREAM_DATA
;
210 /* CompObj Stream structure */
211 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
216 DWORD dwCLSIDNameLength
;
217 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
218 DWORD dwOleTypeNameLength
;
219 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
220 DWORD dwProgIDNameLength
;
221 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
223 }OLECONVERT_ISTORAGE_COMPOBJ
;
226 /* Ole Presentation Stream structure */
227 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
235 }OLECONVERT_ISTORAGE_OLEPRES
;
239 /***********************************************************************
240 * Forward declaration of internal functions used by the method DestroyElement
242 static HRESULT
deleteStorageContents(
243 StorageBaseImpl
*parentStorage
,
244 DirRef indexToDelete
,
245 DirEntry entryDataToDelete
);
247 static HRESULT
deleteStreamContents(
248 StorageBaseImpl
*parentStorage
,
249 DirRef indexToDelete
,
250 DirEntry entryDataToDelete
);
252 static HRESULT
removeFromTree(
253 StorageBaseImpl
*This
,
254 DirRef parentStorageIndex
,
255 DirRef deletedIndex
);
257 /***********************************************************************
258 * Declaration of the functions used to manipulate DirEntry
261 static HRESULT
insertIntoTree(
262 StorageBaseImpl
*This
,
263 DirRef parentStorageIndex
,
264 DirRef newEntryIndex
);
266 static LONG
entryNameCmp(
267 const OLECHAR
*name1
,
268 const OLECHAR
*name2
);
270 static DirRef
findElement(
271 StorageBaseImpl
*storage
,
276 static HRESULT
findTreeParent(
277 StorageBaseImpl
*storage
,
279 const OLECHAR
*childName
,
280 DirEntry
*parentData
,
284 /***********************************************************************
285 * Declaration of miscellaneous functions...
287 static HRESULT
validateSTGM(DWORD stgmValue
);
289 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
290 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
291 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
293 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
296 /****************************************************************************
297 * IEnumSTATSTGImpl definitions.
299 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
300 * This class allows iterating through the content of a storage and to find
301 * specific items inside it.
303 struct IEnumSTATSTGImpl
305 IEnumSTATSTG IEnumSTATSTG_iface
;
307 LONG ref
; /* Reference count */
308 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
309 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
311 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
314 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
316 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
320 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
323 /************************************************************************
327 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
329 return (index
+1) * This
->bigBlockSize
;
332 /************************************************************************
333 ** Storage32BaseImpl implementation
335 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
336 ULARGE_INTEGER offset
,
341 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
344 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
345 ULARGE_INTEGER offset
,
350 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
353 /************************************************************************
354 * Storage32BaseImpl_QueryInterface (IUnknown)
356 * This method implements the common QueryInterface for all IStorage32
357 * implementations contained in this file.
359 * See Windows documentation for more details on IUnknown methods.
361 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
366 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
373 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
374 IsEqualGUID(&IID_IStorage
, riid
))
376 *ppvObject
= &This
->IStorage_iface
;
378 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
380 *ppvObject
= &This
->IPropertySetStorage_iface
;
383 return E_NOINTERFACE
;
385 IStorage_AddRef(iface
);
390 /************************************************************************
391 * Storage32BaseImpl_AddRef (IUnknown)
393 * This method implements the common AddRef for all IStorage32
394 * implementations contained in this file.
396 * See Windows documentation for more details on IUnknown methods.
398 static ULONG WINAPI
StorageBaseImpl_AddRef(
401 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
402 ULONG ref
= InterlockedIncrement(&This
->ref
);
404 TRACE("(%p) AddRef to %d\n", This
, ref
);
409 /************************************************************************
410 * Storage32BaseImpl_Release (IUnknown)
412 * This method implements the common Release for all IStorage32
413 * implementations contained in this file.
415 * See Windows documentation for more details on IUnknown methods.
417 static ULONG WINAPI
StorageBaseImpl_Release(
420 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
422 ULONG ref
= InterlockedDecrement(&This
->ref
);
424 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
429 * Since we are using a system of base-classes, we want to call the
430 * destructor of the appropriate derived class. To do this, we are
431 * using virtual functions to implement the destructor.
433 StorageBaseImpl_Destroy(This
);
439 /************************************************************************
440 * Storage32BaseImpl_OpenStream (IStorage)
442 * This method will open the specified stream object from the current storage.
444 * See Windows documentation for more details on IStorage methods.
446 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
448 const OLECHAR
* pwcsName
, /* [string][in] */
449 void* reserved1
, /* [unique][in] */
450 DWORD grfMode
, /* [in] */
451 DWORD reserved2
, /* [in] */
452 IStream
** ppstm
) /* [out] */
454 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
455 StgStreamImpl
* newStream
;
456 DirEntry currentEntry
;
457 DirRef streamEntryRef
;
458 HRESULT res
= STG_E_UNKNOWN
;
460 TRACE("(%p, %s, %p, %x, %d, %p)\n",
461 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
463 if ( (pwcsName
==NULL
) || (ppstm
==0) )
471 if ( FAILED( validateSTGM(grfMode
) ) ||
472 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
474 res
= STG_E_INVALIDFLAG
;
481 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
483 res
= STG_E_INVALIDFUNCTION
;
489 res
= STG_E_REVERTED
;
494 * Check that we're compatible with the parent's storage mode, but
495 * only if we are not in transacted mode
497 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
498 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
500 res
= STG_E_INVALIDFLAG
;
506 * Search for the element with the given name
508 streamEntryRef
= findElement(
510 This
->storageDirEntry
,
515 * If it was found, construct the stream object and return a pointer to it.
517 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
518 (currentEntry
.stgType
==STGTY_STREAM
) )
520 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
522 /* A single stream cannot be opened a second time. */
523 res
= STG_E_ACCESSDENIED
;
527 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
531 newStream
->grfMode
= grfMode
;
532 *ppstm
= &newStream
->IStream_iface
;
534 IStream_AddRef(*ppstm
);
544 res
= STG_E_FILENOTFOUND
;
548 TRACE("<-- IStream %p\n", *ppstm
);
549 TRACE("<-- %08x\n", res
);
553 /************************************************************************
554 * Storage32BaseImpl_OpenStorage (IStorage)
556 * This method will open a new storage object from the current storage.
558 * See Windows documentation for more details on IStorage methods.
560 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
562 const OLECHAR
* pwcsName
, /* [string][unique][in] */
563 IStorage
* pstgPriority
, /* [unique][in] */
564 DWORD grfMode
, /* [in] */
565 SNB snbExclude
, /* [unique][in] */
566 DWORD reserved
, /* [in] */
567 IStorage
** ppstg
) /* [out] */
569 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
570 StorageInternalImpl
* newStorage
;
571 StorageBaseImpl
* newTransactedStorage
;
572 DirEntry currentEntry
;
573 DirRef storageEntryRef
;
574 HRESULT res
= STG_E_UNKNOWN
;
576 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
577 iface
, debugstr_w(pwcsName
), pstgPriority
,
578 grfMode
, snbExclude
, reserved
, ppstg
);
580 if ((pwcsName
==NULL
) || (ppstg
==0) )
586 if (This
->openFlags
& STGM_SIMPLE
)
588 res
= STG_E_INVALIDFUNCTION
;
593 if (snbExclude
!= NULL
)
595 res
= STG_E_INVALIDPARAMETER
;
599 if ( FAILED( validateSTGM(grfMode
) ))
601 res
= STG_E_INVALIDFLAG
;
608 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
609 (grfMode
& STGM_DELETEONRELEASE
) ||
610 (grfMode
& STGM_PRIORITY
) )
612 res
= STG_E_INVALIDFUNCTION
;
617 return STG_E_REVERTED
;
620 * Check that we're compatible with the parent's storage mode,
621 * but only if we are not transacted
623 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
624 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
626 res
= STG_E_ACCESSDENIED
;
633 storageEntryRef
= findElement(
635 This
->storageDirEntry
,
639 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
640 (currentEntry
.stgType
==STGTY_STORAGE
) )
642 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
644 /* A single storage cannot be opened a second time. */
645 res
= STG_E_ACCESSDENIED
;
649 newStorage
= StorageInternalImpl_Construct(
656 if (grfMode
& STGM_TRANSACTED
)
658 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
662 HeapFree(GetProcessHeap(), 0, newStorage
);
666 *ppstg
= &newTransactedStorage
->IStorage_iface
;
670 *ppstg
= &newStorage
->base
.IStorage_iface
;
673 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
679 res
= STG_E_INSUFFICIENTMEMORY
;
683 res
= STG_E_FILENOTFOUND
;
686 TRACE("<-- %08x\n", res
);
690 /************************************************************************
691 * Storage32BaseImpl_EnumElements (IStorage)
693 * This method will create an enumerator object that can be used to
694 * retrieve information about all the elements in the storage object.
696 * See Windows documentation for more details on IStorage methods.
698 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
700 DWORD reserved1
, /* [in] */
701 void* reserved2
, /* [size_is][unique][in] */
702 DWORD reserved3
, /* [in] */
703 IEnumSTATSTG
** ppenum
) /* [out] */
705 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
706 IEnumSTATSTGImpl
* newEnum
;
708 TRACE("(%p, %d, %p, %d, %p)\n",
709 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
715 return STG_E_REVERTED
;
717 newEnum
= IEnumSTATSTGImpl_Construct(
719 This
->storageDirEntry
);
723 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
725 IEnumSTATSTG_AddRef(*ppenum
);
730 return E_OUTOFMEMORY
;
733 /************************************************************************
734 * Storage32BaseImpl_Stat (IStorage)
736 * This method will retrieve information about this storage object.
738 * See Windows documentation for more details on IStorage methods.
740 static HRESULT WINAPI
StorageBaseImpl_Stat(
742 STATSTG
* pstatstg
, /* [out] */
743 DWORD grfStatFlag
) /* [in] */
745 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
746 DirEntry currentEntry
;
747 HRESULT res
= STG_E_UNKNOWN
;
749 TRACE("(%p, %p, %x)\n",
750 iface
, pstatstg
, grfStatFlag
);
760 res
= STG_E_REVERTED
;
764 res
= StorageBaseImpl_ReadDirEntry(
766 This
->storageDirEntry
,
771 StorageUtl_CopyDirEntryToSTATSTG(
777 pstatstg
->grfMode
= This
->openFlags
;
778 pstatstg
->grfStateBits
= This
->stateBits
;
784 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
);
786 TRACE("<-- %08x\n", res
);
790 /************************************************************************
791 * Storage32BaseImpl_RenameElement (IStorage)
793 * This method will rename the specified element.
795 * See Windows documentation for more details on IStorage methods.
797 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
799 const OLECHAR
* pwcsOldName
, /* [in] */
800 const OLECHAR
* pwcsNewName
) /* [in] */
802 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
803 DirEntry currentEntry
;
804 DirRef currentEntryRef
;
806 TRACE("(%p, %s, %s)\n",
807 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
810 return STG_E_REVERTED
;
812 currentEntryRef
= findElement(This
,
813 This
->storageDirEntry
,
817 if (currentEntryRef
!= DIRENTRY_NULL
)
820 * There is already an element with the new name
822 return STG_E_FILEALREADYEXISTS
;
826 * Search for the old element name
828 currentEntryRef
= findElement(This
,
829 This
->storageDirEntry
,
833 if (currentEntryRef
!= DIRENTRY_NULL
)
835 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
836 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
838 WARN("Element is already open; cannot rename.\n");
839 return STG_E_ACCESSDENIED
;
842 /* Remove the element from its current position in the tree */
843 removeFromTree(This
, This
->storageDirEntry
,
846 /* Change the name of the element */
847 strcpyW(currentEntry
.name
, pwcsNewName
);
849 /* Delete any sibling links */
850 currentEntry
.leftChild
= DIRENTRY_NULL
;
851 currentEntry
.rightChild
= DIRENTRY_NULL
;
853 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
856 /* Insert the element in a new position in the tree */
857 insertIntoTree(This
, This
->storageDirEntry
,
863 * There is no element with the old name
865 return STG_E_FILENOTFOUND
;
868 return StorageBaseImpl_Flush(This
);
871 /************************************************************************
872 * Storage32BaseImpl_CreateStream (IStorage)
874 * This method will create a stream object within this storage
876 * See Windows documentation for more details on IStorage methods.
878 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
880 const OLECHAR
* pwcsName
, /* [string][in] */
881 DWORD grfMode
, /* [in] */
882 DWORD reserved1
, /* [in] */
883 DWORD reserved2
, /* [in] */
884 IStream
** ppstm
) /* [out] */
886 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
887 StgStreamImpl
* newStream
;
888 DirEntry currentEntry
, newStreamEntry
;
889 DirRef currentEntryRef
, newStreamEntryRef
;
892 TRACE("(%p, %s, %x, %d, %d, %p)\n",
893 iface
, debugstr_w(pwcsName
), grfMode
,
894 reserved1
, reserved2
, ppstm
);
897 return STG_E_INVALIDPOINTER
;
900 return STG_E_INVALIDNAME
;
902 if (reserved1
|| reserved2
)
903 return STG_E_INVALIDPARAMETER
;
905 if ( FAILED( validateSTGM(grfMode
) ))
906 return STG_E_INVALIDFLAG
;
908 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
909 return STG_E_INVALIDFLAG
;
912 return STG_E_REVERTED
;
917 if ((grfMode
& STGM_DELETEONRELEASE
) ||
918 (grfMode
& STGM_TRANSACTED
))
919 return STG_E_INVALIDFUNCTION
;
922 * Don't worry about permissions in transacted mode, as we can always write
923 * changes; we just can't always commit them.
925 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
926 /* Can't create a stream on read-only storage */
927 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
928 return STG_E_ACCESSDENIED
;
930 /* Can't create a stream with greater access than the parent. */
931 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
932 return STG_E_ACCESSDENIED
;
935 if(This
->openFlags
& STGM_SIMPLE
)
936 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
940 currentEntryRef
= findElement(This
,
941 This
->storageDirEntry
,
945 if (currentEntryRef
!= DIRENTRY_NULL
)
948 * An element with this name already exists
950 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
952 IStorage_DestroyElement(iface
, pwcsName
);
955 return STG_E_FILEALREADYEXISTS
;
959 * memset the empty entry
961 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
963 newStreamEntry
.sizeOfNameString
=
964 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
966 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
967 return STG_E_INVALIDNAME
;
969 strcpyW(newStreamEntry
.name
, pwcsName
);
971 newStreamEntry
.stgType
= STGTY_STREAM
;
972 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
973 newStreamEntry
.size
.u
.LowPart
= 0;
974 newStreamEntry
.size
.u
.HighPart
= 0;
976 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
977 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
978 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
980 /* call CoFileTime to get the current time
985 /* newStreamEntry.clsid */
988 * Create an entry with the new data
990 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
995 * Insert the new entry in the parent storage's tree.
999 This
->storageDirEntry
,
1003 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1008 * Open the stream to return it.
1010 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1014 *ppstm
= &newStream
->IStream_iface
;
1015 IStream_AddRef(*ppstm
);
1019 return STG_E_INSUFFICIENTMEMORY
;
1022 return StorageBaseImpl_Flush(This
);
1025 /************************************************************************
1026 * Storage32BaseImpl_SetClass (IStorage)
1028 * This method will write the specified CLSID in the directory entry of this
1031 * See Windows documentation for more details on IStorage methods.
1033 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1035 REFCLSID clsid
) /* [in] */
1037 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1039 DirEntry currentEntry
;
1041 TRACE("(%p, %p)\n", iface
, clsid
);
1044 return STG_E_REVERTED
;
1046 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1047 This
->storageDirEntry
,
1049 if (SUCCEEDED(hRes
))
1051 currentEntry
.clsid
= *clsid
;
1053 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1054 This
->storageDirEntry
,
1058 if (SUCCEEDED(hRes
))
1059 hRes
= StorageBaseImpl_Flush(This
);
1064 /************************************************************************
1065 ** Storage32Impl implementation
1068 /************************************************************************
1069 * Storage32BaseImpl_CreateStorage (IStorage)
1071 * This method will create the storage object within the provided storage.
1073 * See Windows documentation for more details on IStorage methods.
1075 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1077 const OLECHAR
*pwcsName
, /* [string][in] */
1078 DWORD grfMode
, /* [in] */
1079 DWORD reserved1
, /* [in] */
1080 DWORD reserved2
, /* [in] */
1081 IStorage
**ppstg
) /* [out] */
1083 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1085 DirEntry currentEntry
;
1087 DirRef currentEntryRef
;
1091 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1092 iface
, debugstr_w(pwcsName
), grfMode
,
1093 reserved1
, reserved2
, ppstg
);
1096 return STG_E_INVALIDPOINTER
;
1098 if (This
->openFlags
& STGM_SIMPLE
)
1100 return STG_E_INVALIDFUNCTION
;
1104 return STG_E_INVALIDNAME
;
1108 if ( FAILED( validateSTGM(grfMode
) ) ||
1109 (grfMode
& STGM_DELETEONRELEASE
) )
1111 WARN("bad grfMode: 0x%x\n", grfMode
);
1112 return STG_E_INVALIDFLAG
;
1116 return STG_E_REVERTED
;
1119 * Check that we're compatible with the parent's storage mode
1121 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1122 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1124 WARN("access denied\n");
1125 return STG_E_ACCESSDENIED
;
1128 currentEntryRef
= findElement(This
,
1129 This
->storageDirEntry
,
1133 if (currentEntryRef
!= DIRENTRY_NULL
)
1136 * An element with this name already exists
1138 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1139 ((This
->openFlags
& STGM_TRANSACTED
) ||
1140 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1142 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1148 WARN("file already exists\n");
1149 return STG_E_FILEALREADYEXISTS
;
1152 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1153 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1155 WARN("read-only storage\n");
1156 return STG_E_ACCESSDENIED
;
1159 memset(&newEntry
, 0, sizeof(DirEntry
));
1161 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1163 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1165 FIXME("name too long\n");
1166 return STG_E_INVALIDNAME
;
1169 strcpyW(newEntry
.name
, pwcsName
);
1171 newEntry
.stgType
= STGTY_STORAGE
;
1172 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1173 newEntry
.size
.u
.LowPart
= 0;
1174 newEntry
.size
.u
.HighPart
= 0;
1176 newEntry
.leftChild
= DIRENTRY_NULL
;
1177 newEntry
.rightChild
= DIRENTRY_NULL
;
1178 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1180 /* call CoFileTime to get the current time
1185 /* newEntry.clsid */
1188 * Create a new directory entry for the storage
1190 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1195 * Insert the new directory entry into the parent storage's tree
1197 hr
= insertIntoTree(
1199 This
->storageDirEntry
,
1203 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1208 * Open it to get a pointer to return.
1210 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1212 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1218 hr
= StorageBaseImpl_Flush(This
);
1224 /***************************************************************************
1228 * Reserve a directory entry in the file and initialize it.
1230 static HRESULT
StorageImpl_CreateDirEntry(
1231 StorageBaseImpl
*base
,
1232 const DirEntry
*newData
,
1235 StorageImpl
*storage
= (StorageImpl
*)base
;
1236 ULONG currentEntryIndex
= 0;
1237 ULONG newEntryIndex
= DIRENTRY_NULL
;
1239 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1240 WORD sizeOfNameString
;
1244 hr
= StorageImpl_ReadRawDirEntry(storage
,
1250 StorageUtl_ReadWord(
1252 OFFSET_PS_NAMELENGTH
,
1255 if (sizeOfNameString
== 0)
1258 * The entry exists and is available, we found it.
1260 newEntryIndex
= currentEntryIndex
;
1266 * We exhausted the directory entries, we will create more space below
1268 newEntryIndex
= currentEntryIndex
;
1270 currentEntryIndex
++;
1272 } while (newEntryIndex
== DIRENTRY_NULL
);
1275 * grow the directory stream
1279 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1280 ULARGE_INTEGER newSize
;
1282 ULONG lastEntry
= 0;
1283 ULONG blockCount
= 0;
1286 * obtain the new count of blocks in the directory stream
1288 blockCount
= BlockChainStream_GetCount(
1289 storage
->rootBlockChain
)+1;
1292 * initialize the size used by the directory stream
1294 newSize
.u
.HighPart
= 0;
1295 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1298 * add a block to the directory stream
1300 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1303 * memset the empty entry in order to initialize the unused newly
1306 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1311 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1314 entryIndex
= newEntryIndex
+ 1;
1315 entryIndex
< lastEntry
;
1318 StorageImpl_WriteRawDirEntry(
1324 StorageImpl_SaveFileHeader(storage
);
1327 UpdateRawDirEntry(currentData
, newData
);
1329 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1332 *index
= newEntryIndex
;
1337 /***************************************************************************
1341 * Mark a directory entry in the file as free.
1343 static HRESULT
StorageImpl_DestroyDirEntry(
1344 StorageBaseImpl
*base
,
1347 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1348 StorageImpl
*storage
= (StorageImpl
*)base
;
1350 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1352 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1356 /****************************************************************************
1360 * Case insensitive comparison of DirEntry.name by first considering
1363 * Returns <0 when name1 < name2
1364 * >0 when name1 > name2
1365 * 0 when name1 == name2
1367 static LONG
entryNameCmp(
1368 const OLECHAR
*name1
,
1369 const OLECHAR
*name2
)
1371 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1373 while (diff
== 0 && *name1
!= 0)
1376 * We compare the string themselves only when they are of the same length
1378 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1384 /****************************************************************************
1388 * Add a directory entry to a storage
1390 static HRESULT
insertIntoTree(
1391 StorageBaseImpl
*This
,
1392 DirRef parentStorageIndex
,
1393 DirRef newEntryIndex
)
1395 DirEntry currentEntry
;
1399 * Read the inserted entry
1401 StorageBaseImpl_ReadDirEntry(This
,
1406 * Read the storage entry
1408 StorageBaseImpl_ReadDirEntry(This
,
1412 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1415 * The root storage contains some element, therefore, start the research
1416 * for the appropriate location.
1419 DirRef current
, next
, previous
, currentEntryId
;
1422 * Keep a reference to the root of the storage's element tree
1424 currentEntryId
= currentEntry
.dirRootEntry
;
1429 StorageBaseImpl_ReadDirEntry(This
,
1430 currentEntry
.dirRootEntry
,
1433 previous
= currentEntry
.leftChild
;
1434 next
= currentEntry
.rightChild
;
1435 current
= currentEntryId
;
1439 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1443 if (previous
!= DIRENTRY_NULL
)
1445 StorageBaseImpl_ReadDirEntry(This
,
1452 currentEntry
.leftChild
= newEntryIndex
;
1453 StorageBaseImpl_WriteDirEntry(This
,
1461 if (next
!= DIRENTRY_NULL
)
1463 StorageBaseImpl_ReadDirEntry(This
,
1470 currentEntry
.rightChild
= newEntryIndex
;
1471 StorageBaseImpl_WriteDirEntry(This
,
1480 * Trying to insert an item with the same name in the
1481 * subtree structure.
1483 return STG_E_FILEALREADYEXISTS
;
1486 previous
= currentEntry
.leftChild
;
1487 next
= currentEntry
.rightChild
;
1493 * The storage is empty, make the new entry the root of its element tree
1495 currentEntry
.dirRootEntry
= newEntryIndex
;
1496 StorageBaseImpl_WriteDirEntry(This
,
1504 /****************************************************************************
1508 * Find and read the element of a storage with the given name.
1510 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1511 const OLECHAR
*name
, DirEntry
*data
)
1513 DirRef currentEntry
;
1515 /* Read the storage entry to find the root of the tree. */
1516 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1518 currentEntry
= data
->dirRootEntry
;
1520 while (currentEntry
!= DIRENTRY_NULL
)
1524 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1526 cmp
= entryNameCmp(name
, data
->name
);
1533 currentEntry
= data
->leftChild
;
1536 currentEntry
= data
->rightChild
;
1539 return currentEntry
;
1542 /****************************************************************************
1546 * Find and read the binary tree parent of the element with the given name.
1548 * If there is no such element, find a place where it could be inserted and
1549 * return STG_E_FILENOTFOUND.
1551 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1552 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1558 /* Read the storage entry to find the root of the tree. */
1559 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1561 *parentEntry
= storageEntry
;
1562 *relation
= DIRENTRY_RELATION_DIR
;
1564 childEntry
= parentData
->dirRootEntry
;
1566 while (childEntry
!= DIRENTRY_NULL
)
1570 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1572 cmp
= entryNameCmp(childName
, childData
.name
);
1580 *parentData
= childData
;
1581 *parentEntry
= childEntry
;
1582 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1584 childEntry
= parentData
->leftChild
;
1589 *parentData
= childData
;
1590 *parentEntry
= childEntry
;
1591 *relation
= DIRENTRY_RELATION_NEXT
;
1593 childEntry
= parentData
->rightChild
;
1597 if (childEntry
== DIRENTRY_NULL
)
1598 return STG_E_FILENOTFOUND
;
1604 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1605 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1606 SNB snbExclude
, IStorage
*pstgDest
);
1608 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1609 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1610 SNB snbExclude
, IStorage
*pstgDest
)
1616 IStream
*pstrChild
, *pstrTmp
;
1619 if (srcEntry
== DIRENTRY_NULL
)
1622 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1629 WCHAR
**snb
= snbExclude
;
1631 while ( *snb
!= NULL
&& !skip
)
1633 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1641 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1644 * create a new storage in destination storage
1646 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1647 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1652 * if it already exist, don't create a new one use this one
1654 if (hr
== STG_E_FILEALREADYEXISTS
)
1656 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1657 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1658 NULL
, 0, &pstgTmp
);
1663 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1664 skip_stream
, NULL
, pstgTmp
);
1666 IStorage_Release(pstgTmp
);
1669 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1672 * create a new stream in destination storage. If the stream already
1673 * exist, it will be deleted and a new one will be created.
1675 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1676 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1680 * open child stream storage. This operation must succeed even if the
1681 * stream is already open, so we use internal functions to do it.
1685 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1689 pstrChild
= &streamimpl
->IStream_iface
;
1691 IStream_AddRef(pstrChild
);
1703 * Get the size of the source stream
1705 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1708 * Set the size of the destination stream.
1710 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1715 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1718 IStream_Release( pstrChild
);
1721 IStream_Release( pstrTmp
);
1727 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1728 skip_stream
, snbExclude
, pstgDest
);
1731 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1732 skip_stream
, snbExclude
, pstgDest
);
1737 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1738 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1739 SNB snbExclude
, IStorage
*pstgDest
)
1744 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1747 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1750 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1751 skip_stream
, snbExclude
, pstgDest
);
1756 /*************************************************************************
1759 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1761 DWORD ciidExclude
, /* [in] */
1762 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1763 SNB snbExclude
, /* [unique][in] */
1764 IStorage
* pstgDest
) /* [unique][in] */
1766 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1768 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1771 TRACE("(%p, %d, %p, %p, %p)\n",
1772 iface
, ciidExclude
, rgiidExclude
,
1773 snbExclude
, pstgDest
);
1775 if ( pstgDest
== 0 )
1776 return STG_E_INVALIDPOINTER
;
1778 for(i
= 0; i
< ciidExclude
; ++i
)
1780 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1781 skip_storage
= TRUE
;
1782 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1785 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1790 /* Give up early if it looks like this would be infinitely recursive.
1791 * Oddly enough, this includes some cases that aren't really recursive, like
1792 * copying to a transacted child. */
1793 IStorage
*pstgDestAncestor
= pstgDest
;
1794 IStorage
*pstgDestAncestorChild
= NULL
;
1796 /* Go up the chain from the destination until we find the source storage. */
1797 while (pstgDestAncestor
!= iface
) {
1798 pstgDestAncestorChild
= pstgDest
;
1800 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1802 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1804 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1806 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1808 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1810 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1816 if (pstgDestAncestor
== iface
)
1820 if (pstgDestAncestorChild
&& snbExclude
)
1822 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1824 WCHAR
**snb
= snbExclude
;
1826 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1828 while ( *snb
!= NULL
&& fail
)
1830 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1837 return STG_E_ACCESSDENIED
;
1841 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1842 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1845 /*************************************************************************
1846 * MoveElementTo (IStorage)
1848 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1850 const OLECHAR
*pwcsName
, /* [string][in] */
1851 IStorage
*pstgDest
, /* [unique][in] */
1852 const OLECHAR
*pwcsNewName
,/* [string][in] */
1853 DWORD grfFlags
) /* [in] */
1855 FIXME("(%p %s %p %s %u): stub\n", iface
,
1856 debugstr_w(pwcsName
), pstgDest
,
1857 debugstr_w(pwcsNewName
), grfFlags
);
1861 /*************************************************************************
1864 * Ensures that any changes made to a storage object open in transacted mode
1865 * are reflected in the parent storage
1867 * In a non-transacted mode, this ensures all cached writes are completed.
1869 static HRESULT WINAPI
StorageImpl_Commit(
1871 DWORD grfCommitFlags
)/* [in] */
1873 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1874 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1875 return StorageBaseImpl_Flush(This
);
1878 /*************************************************************************
1881 * Discard all changes that have been made since the last commit operation
1883 static HRESULT WINAPI
StorageImpl_Revert(
1886 TRACE("(%p)\n", iface
);
1890 /*************************************************************************
1891 * DestroyElement (IStorage)
1893 * Strategy: This implementation is built this way for simplicity not for speed.
1894 * I always delete the topmost element of the enumeration and adjust
1895 * the deleted element pointer all the time. This takes longer to
1896 * do but allow to reinvoke DestroyElement whenever we encounter a
1897 * storage object. The optimisation resides in the usage of another
1898 * enumeration strategy that would give all the leaves of a storage
1899 * first. (postfix order)
1901 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1903 const OLECHAR
*pwcsName
)/* [string][in] */
1905 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1908 DirEntry entryToDelete
;
1909 DirRef entryToDeleteRef
;
1912 iface
, debugstr_w(pwcsName
));
1915 return STG_E_INVALIDPOINTER
;
1918 return STG_E_REVERTED
;
1920 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1921 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1922 return STG_E_ACCESSDENIED
;
1924 entryToDeleteRef
= findElement(
1926 This
->storageDirEntry
,
1930 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1932 return STG_E_FILENOTFOUND
;
1935 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1937 hr
= deleteStorageContents(
1942 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1944 hr
= deleteStreamContents(
1954 * Remove the entry from its parent storage
1956 hr
= removeFromTree(
1958 This
->storageDirEntry
,
1962 * Invalidate the entry
1965 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1968 hr
= StorageBaseImpl_Flush(This
);
1974 /******************************************************************************
1975 * Internal stream list handlers
1978 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1980 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1981 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1984 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1986 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1987 list_remove(&(strm
->StrmListEntry
));
1990 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1992 StgStreamImpl
*strm
;
1994 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1996 if (strm
->dirEntry
== streamEntry
)
2005 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2007 StorageInternalImpl
*childstg
;
2009 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2011 if (childstg
->base
.storageDirEntry
== storageEntry
)
2020 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2022 struct list
*cur
, *cur2
;
2023 StgStreamImpl
*strm
=NULL
;
2024 StorageInternalImpl
*childstg
=NULL
;
2026 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2027 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2028 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2029 strm
->parentStorage
= NULL
;
2033 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2034 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2035 StorageBaseImpl_Invalidate( &childstg
->base
);
2038 if (stg
->transactedChild
)
2040 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2042 stg
->transactedChild
= NULL
;
2047 /*********************************************************************
2051 * Delete the contents of a storage entry.
2054 static HRESULT
deleteStorageContents(
2055 StorageBaseImpl
*parentStorage
,
2056 DirRef indexToDelete
,
2057 DirEntry entryDataToDelete
)
2059 IEnumSTATSTG
*elements
= 0;
2060 IStorage
*childStorage
= 0;
2061 STATSTG currentElement
;
2063 HRESULT destroyHr
= S_OK
;
2064 StorageInternalImpl
*stg
, *stg2
;
2066 /* Invalidate any open storage objects. */
2067 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2069 if (stg
->base
.storageDirEntry
== indexToDelete
)
2071 StorageBaseImpl_Invalidate(&stg
->base
);
2076 * Open the storage and enumerate it
2078 hr
= IStorage_OpenStorage(
2079 &parentStorage
->IStorage_iface
,
2080 entryDataToDelete
.name
,
2082 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2093 * Enumerate the elements
2095 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2100 * Obtain the next element
2102 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2105 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2107 CoTaskMemFree(currentElement
.pwcsName
);
2111 * We need to Reset the enumeration every time because we delete elements
2112 * and the enumeration could be invalid
2114 IEnumSTATSTG_Reset(elements
);
2116 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2118 IStorage_Release(childStorage
);
2119 IEnumSTATSTG_Release(elements
);
2124 /*********************************************************************
2128 * Perform the deletion of a stream's data
2131 static HRESULT
deleteStreamContents(
2132 StorageBaseImpl
*parentStorage
,
2133 DirRef indexToDelete
,
2134 DirEntry entryDataToDelete
)
2138 ULARGE_INTEGER size
;
2139 StgStreamImpl
*strm
, *strm2
;
2141 /* Invalidate any open stream objects. */
2142 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2144 if (strm
->dirEntry
== indexToDelete
)
2146 TRACE("Stream deleted %p\n", strm
);
2147 strm
->parentStorage
= NULL
;
2148 list_remove(&strm
->StrmListEntry
);
2152 size
.u
.HighPart
= 0;
2155 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2156 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2166 hr
= IStream_SetSize(pis
, size
);
2174 * Release the stream object.
2176 IStream_Release(pis
);
2181 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2185 case DIRENTRY_RELATION_PREVIOUS
:
2186 entry
->leftChild
= new_target
;
2188 case DIRENTRY_RELATION_NEXT
:
2189 entry
->rightChild
= new_target
;
2191 case DIRENTRY_RELATION_DIR
:
2192 entry
->dirRootEntry
= new_target
;
2199 /*************************************************************************
2203 * This method removes a directory entry from its parent storage tree without
2204 * freeing any resources attached to it.
2206 static HRESULT
removeFromTree(
2207 StorageBaseImpl
*This
,
2208 DirRef parentStorageIndex
,
2209 DirRef deletedIndex
)
2211 DirEntry entryToDelete
;
2212 DirEntry parentEntry
;
2213 DirRef parentEntryRef
;
2214 ULONG typeOfRelation
;
2217 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2223 * Find the element that links to the one we want to delete.
2225 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2226 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2231 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2234 * Replace the deleted entry with its left child
2236 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2238 hr
= StorageBaseImpl_WriteDirEntry(
2247 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2250 * We need to reinsert the right child somewhere. We already know it and
2251 * its children are greater than everything in the left tree, so we
2252 * insert it at the rightmost point in the left tree.
2254 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2255 DirEntry newRightChildParentEntry
;
2259 hr
= StorageBaseImpl_ReadDirEntry(
2261 newRightChildParent
,
2262 &newRightChildParentEntry
);
2268 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2269 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2270 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2272 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2274 hr
= StorageBaseImpl_WriteDirEntry(
2276 newRightChildParent
,
2277 &newRightChildParentEntry
);
2287 * Replace the deleted entry with its right child
2289 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2291 hr
= StorageBaseImpl_WriteDirEntry(
2305 /******************************************************************************
2306 * SetElementTimes (IStorage)
2308 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2310 const OLECHAR
*pwcsName
,/* [string][in] */
2311 const FILETIME
*pctime
, /* [in] */
2312 const FILETIME
*patime
, /* [in] */
2313 const FILETIME
*pmtime
) /* [in] */
2315 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2319 /******************************************************************************
2320 * SetStateBits (IStorage)
2322 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2324 DWORD grfStateBits
,/* [in] */
2325 DWORD grfMask
) /* [in] */
2327 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2330 return STG_E_REVERTED
;
2332 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2336 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2337 DirRef index
, const DirEntry
*data
)
2339 StorageImpl
*This
= (StorageImpl
*)base
;
2340 return StorageImpl_WriteDirEntry(This
, index
, data
);
2343 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2344 DirRef index
, DirEntry
*data
)
2346 StorageImpl
*This
= (StorageImpl
*)base
;
2347 return StorageImpl_ReadDirEntry(This
, index
, data
);
2350 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2354 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2356 if (!This
->blockChainCache
[i
])
2358 return &This
->blockChainCache
[i
];
2362 i
= This
->blockChainToEvict
;
2364 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2365 This
->blockChainCache
[i
] = NULL
;
2367 This
->blockChainToEvict
++;
2368 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2369 This
->blockChainToEvict
= 0;
2371 return &This
->blockChainCache
[i
];
2374 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2377 int i
, free_index
=-1;
2379 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2381 if (!This
->blockChainCache
[i
])
2383 if (free_index
== -1) free_index
= i
;
2385 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2387 return &This
->blockChainCache
[i
];
2391 if (free_index
== -1)
2393 free_index
= This
->blockChainToEvict
;
2395 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2396 This
->blockChainCache
[free_index
] = NULL
;
2398 This
->blockChainToEvict
++;
2399 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2400 This
->blockChainToEvict
= 0;
2403 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2404 return &This
->blockChainCache
[free_index
];
2407 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2411 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2413 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2415 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2416 This
->blockChainCache
[i
] = NULL
;
2422 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2423 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2425 StorageImpl
*This
= (StorageImpl
*)base
;
2430 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2431 if (FAILED(hr
)) return hr
;
2433 if (data
.size
.QuadPart
== 0)
2439 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2441 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2448 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2450 SmallBlockChainStream
*stream
;
2452 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2453 if (!stream
) return E_OUTOFMEMORY
;
2455 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2457 SmallBlockChainStream_Destroy(stream
);
2463 BlockChainStream
*stream
= NULL
;
2465 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2466 if (!stream
) return E_OUTOFMEMORY
;
2468 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2474 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2475 ULARGE_INTEGER newsize
)
2477 StorageImpl
*This
= (StorageImpl
*)base
;
2480 SmallBlockChainStream
*smallblock
=NULL
;
2481 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2483 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2484 if (FAILED(hr
)) return hr
;
2486 /* In simple mode keep the stream size above the small block limit */
2487 if (This
->base
.openFlags
& STGM_SIMPLE
)
2488 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2490 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2493 /* Create a block chain object of the appropriate type */
2494 if (data
.size
.QuadPart
== 0)
2496 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2498 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2499 if (!smallblock
) return E_OUTOFMEMORY
;
2503 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2504 bigblock
= *pbigblock
;
2505 if (!bigblock
) return E_OUTOFMEMORY
;
2508 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2510 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2511 if (!smallblock
) return E_OUTOFMEMORY
;
2515 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2516 bigblock
= *pbigblock
;
2517 if (!bigblock
) return E_OUTOFMEMORY
;
2520 /* Change the block chain type if necessary. */
2521 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2523 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2526 SmallBlockChainStream_Destroy(smallblock
);
2530 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2531 *pbigblock
= bigblock
;
2533 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2535 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2540 /* Set the size of the block chain. */
2543 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2544 SmallBlockChainStream_Destroy(smallblock
);
2548 BlockChainStream_SetSize(bigblock
, newsize
);
2551 /* Set the size in the directory entry. */
2552 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2555 data
.size
= newsize
;
2557 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2562 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2563 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2565 StorageImpl
*This
= (StorageImpl
*)base
;
2568 ULARGE_INTEGER newSize
;
2570 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2571 if (FAILED(hr
)) return hr
;
2573 /* Grow the stream if necessary */
2574 newSize
.QuadPart
= 0;
2575 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2577 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2579 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2583 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2584 if (FAILED(hr
)) return hr
;
2587 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2589 SmallBlockChainStream
*stream
;
2591 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2592 if (!stream
) return E_OUTOFMEMORY
;
2594 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2596 SmallBlockChainStream_Destroy(stream
);
2602 BlockChainStream
*stream
;
2604 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2605 if (!stream
) return E_OUTOFMEMORY
;
2607 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2613 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2616 StorageImpl
*This
= (StorageImpl
*)base
;
2617 DirEntry dst_data
, src_data
;
2620 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2623 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2627 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2628 dst_data
.startingBlock
= src_data
.startingBlock
;
2629 dst_data
.size
= src_data
.size
;
2631 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2637 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2639 StorageImpl
*This
= (StorageImpl
*) iface
;
2643 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2645 *result
= statstg
.pwcsName
;
2651 * Virtual function table for the IStorage32Impl class.
2653 static const IStorageVtbl Storage32Impl_Vtbl
=
2655 StorageBaseImpl_QueryInterface
,
2656 StorageBaseImpl_AddRef
,
2657 StorageBaseImpl_Release
,
2658 StorageBaseImpl_CreateStream
,
2659 StorageBaseImpl_OpenStream
,
2660 StorageBaseImpl_CreateStorage
,
2661 StorageBaseImpl_OpenStorage
,
2662 StorageBaseImpl_CopyTo
,
2663 StorageBaseImpl_MoveElementTo
,
2666 StorageBaseImpl_EnumElements
,
2667 StorageBaseImpl_DestroyElement
,
2668 StorageBaseImpl_RenameElement
,
2669 StorageBaseImpl_SetElementTimes
,
2670 StorageBaseImpl_SetClass
,
2671 StorageBaseImpl_SetStateBits
,
2672 StorageBaseImpl_Stat
2675 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2677 StorageImpl_Destroy
,
2678 StorageImpl_Invalidate
,
2680 StorageImpl_GetFilename
,
2681 StorageImpl_CreateDirEntry
,
2682 StorageImpl_BaseWriteDirEntry
,
2683 StorageImpl_BaseReadDirEntry
,
2684 StorageImpl_DestroyDirEntry
,
2685 StorageImpl_StreamReadAt
,
2686 StorageImpl_StreamWriteAt
,
2687 StorageImpl_StreamSetSize
,
2688 StorageImpl_StreamLink
2691 static HRESULT
StorageImpl_Construct(
2699 StorageImpl
** result
)
2703 DirEntry currentEntry
;
2704 DirRef currentEntryRef
;
2706 if ( FAILED( validateSTGM(openFlags
) ))
2707 return STG_E_INVALIDFLAG
;
2709 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2711 return E_OUTOFMEMORY
;
2713 memset(This
, 0, sizeof(StorageImpl
));
2715 list_init(&This
->base
.strmHead
);
2717 list_init(&This
->base
.storageHead
);
2719 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
2720 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
2721 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2722 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2724 This
->base
.create
= create
;
2726 This
->base
.reverted
= 0;
2729 * Initialize the big block cache.
2731 This
->bigBlockSize
= sector_size
;
2732 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2734 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2737 This
->lockBytes
= pLkbyt
;
2738 ILockBytes_AddRef(pLkbyt
);
2746 ULARGE_INTEGER size
;
2747 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2749 /* Discard any existing data. */
2751 ILockBytes_SetSize(This
->lockBytes
, size
);
2754 * Initialize all header variables:
2755 * - The big block depot consists of one block and it is at block 0
2756 * - The directory table starts at block 1
2757 * - There is no small block depot
2759 memset( This
->bigBlockDepotStart
,
2761 sizeof(This
->bigBlockDepotStart
));
2763 This
->bigBlockDepotCount
= 1;
2764 This
->bigBlockDepotStart
[0] = 0;
2765 This
->rootStartBlock
= 1;
2766 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2767 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2768 if (sector_size
== 4096)
2769 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2771 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2772 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2773 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2774 This
->extBigBlockDepotCount
= 0;
2776 StorageImpl_SaveFileHeader(This
);
2779 * Add one block for the big block depot and one block for the directory table
2781 size
.u
.HighPart
= 0;
2782 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2783 ILockBytes_SetSize(This
->lockBytes
, size
);
2786 * Initialize the big block depot
2788 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2789 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2790 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2791 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2796 * Load the header for the file.
2798 hr
= StorageImpl_LoadFileHeader(This
);
2807 * There is no block depot cached yet.
2809 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2810 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
2813 * Start searching for free blocks with block 0.
2815 This
->prevFreeBlock
= 0;
2817 This
->firstFreeSmallBlock
= 0;
2819 /* Read the extended big block depot locations. */
2820 if (This
->extBigBlockDepotCount
!= 0)
2822 ULONG current_block
= This
->extBigBlockDepotStart
;
2823 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2826 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2827 if (!This
->extBigBlockDepotLocations
)
2833 This
->extBigBlockDepotLocationsSize
= cache_size
;
2835 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2837 if (current_block
== BLOCK_END_OF_CHAIN
)
2839 WARN("File has too few extended big block depot blocks.\n");
2840 hr
= STG_E_DOCFILECORRUPT
;
2843 This
->extBigBlockDepotLocations
[i
] = current_block
;
2844 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2849 This
->extBigBlockDepotLocations
= NULL
;
2850 This
->extBigBlockDepotLocationsSize
= 0;
2854 * Create the block chain abstractions.
2856 if(!(This
->rootBlockChain
=
2857 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2859 hr
= STG_E_READFAULT
;
2863 if(!(This
->smallBlockDepotChain
=
2864 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2867 hr
= STG_E_READFAULT
;
2872 * Write the root storage entry (memory only)
2876 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2879 * Initialize the directory table
2881 memset(&rootEntry
, 0, sizeof(rootEntry
));
2882 strcpyW(rootEntry
.name
, rootentryW
);
2883 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
2884 rootEntry
.stgType
= STGTY_ROOT
;
2885 rootEntry
.leftChild
= DIRENTRY_NULL
;
2886 rootEntry
.rightChild
= DIRENTRY_NULL
;
2887 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2888 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2889 rootEntry
.size
.u
.HighPart
= 0;
2890 rootEntry
.size
.u
.LowPart
= 0;
2892 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2896 * Find the ID of the root storage.
2898 currentEntryRef
= 0;
2902 hr
= StorageImpl_ReadDirEntry(
2909 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2910 (currentEntry
.stgType
== STGTY_ROOT
) )
2912 This
->base
.storageDirEntry
= currentEntryRef
;
2918 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2922 hr
= STG_E_READFAULT
;
2927 * Create the block chain abstraction for the small block root chain.
2929 if(!(This
->smallBlockRootChain
=
2930 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2932 hr
= STG_E_READFAULT
;
2938 IStorage_Release(&This
->base
.IStorage_iface
);
2943 StorageImpl_Flush((StorageBaseImpl
*)This
);
2950 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2952 StorageImpl
*This
= (StorageImpl
*) iface
;
2954 StorageBaseImpl_DeleteAll(&This
->base
);
2956 This
->base
.reverted
= 1;
2959 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2961 StorageImpl
*This
= (StorageImpl
*) iface
;
2963 TRACE("(%p)\n", This
);
2965 StorageImpl_Flush(iface
);
2967 StorageImpl_Invalidate(iface
);
2969 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
2971 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2972 BlockChainStream_Destroy(This
->rootBlockChain
);
2973 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2975 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2976 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2978 if (This
->lockBytes
)
2979 ILockBytes_Release(This
->lockBytes
);
2980 HeapFree(GetProcessHeap(), 0, This
);
2983 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
)
2985 StorageImpl
*This
= (StorageImpl
*) iface
;
2988 TRACE("(%p)\n", This
);
2990 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
2993 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
2996 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
2998 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2999 if (This
->blockChainCache
[i
])
3000 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3003 hr
= ILockBytes_Flush(This
->lockBytes
);
3008 /******************************************************************************
3009 * Storage32Impl_GetNextFreeBigBlock
3011 * Returns the index of the next free big block.
3012 * If the big block depot is filled, this method will enlarge it.
3015 static ULONG
StorageImpl_GetNextFreeBigBlock(
3018 ULONG depotBlockIndexPos
;
3019 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3021 ULONG depotBlockOffset
;
3022 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3023 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3025 ULONG freeBlock
= BLOCK_UNUSED
;
3026 ULARGE_INTEGER neededSize
;
3029 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3030 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3033 * Scan the entire big block depot until we find a block marked free
3035 while (nextBlockIndex
!= BLOCK_UNUSED
)
3037 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3039 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3042 * Grow the primary depot.
3044 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3046 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3049 * Add a block depot.
3051 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3052 This
->bigBlockDepotCount
++;
3053 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3056 * Flag it as a block depot.
3058 StorageImpl_SetNextBlockInChain(This
,
3062 /* Save new header information.
3064 StorageImpl_SaveFileHeader(This
);
3069 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3071 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3074 * Grow the extended depot.
3076 ULONG extIndex
= BLOCK_UNUSED
;
3077 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3078 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3080 if (extBlockOffset
== 0)
3082 /* We need an extended block.
3084 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3085 This
->extBigBlockDepotCount
++;
3086 depotBlockIndexPos
= extIndex
+ 1;
3089 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3092 * Add a block depot and mark it in the extended block.
3094 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3095 This
->bigBlockDepotCount
++;
3096 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3098 /* Flag the block depot.
3100 StorageImpl_SetNextBlockInChain(This
,
3104 /* If necessary, flag the extended depot block.
3106 if (extIndex
!= BLOCK_UNUSED
)
3107 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3109 /* Save header information.
3111 StorageImpl_SaveFileHeader(This
);
3115 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3119 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3120 ( nextBlockIndex
!= BLOCK_UNUSED
))
3122 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3124 if (nextBlockIndex
== BLOCK_UNUSED
)
3126 freeBlock
= (depotIndex
* blocksPerDepot
) +
3127 (depotBlockOffset
/sizeof(ULONG
));
3130 depotBlockOffset
+= sizeof(ULONG
);
3135 depotBlockOffset
= 0;
3139 * make sure that the block physically exists before using it
3141 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3143 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3145 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3146 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3148 This
->prevFreeBlock
= freeBlock
;
3153 /******************************************************************************
3154 * Storage32Impl_AddBlockDepot
3156 * This will create a depot block, essentially it is a block initialized
3159 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3161 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3164 * Initialize blocks as free
3166 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3167 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3170 /******************************************************************************
3171 * Storage32Impl_GetExtDepotBlock
3173 * Returns the index of the block that corresponds to the specified depot
3174 * index. This method is only for depot indexes equal or greater than
3175 * COUNT_BBDEPOTINHEADER.
3177 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3179 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3180 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3181 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3182 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3183 ULONG blockIndex
= BLOCK_UNUSED
;
3184 ULONG extBlockIndex
;
3185 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3186 int index
, num_blocks
;
3188 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3190 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3191 return BLOCK_UNUSED
;
3193 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3195 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3197 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
);
3199 num_blocks
= This
->bigBlockSize
/ 4;
3201 for (index
= 0; index
< num_blocks
; index
++)
3203 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3204 This
->extBlockDepotCached
[index
] = blockIndex
;
3207 This
->indexExtBlockDepotCached
= extBlockCount
;
3210 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3215 /******************************************************************************
3216 * Storage32Impl_SetExtDepotBlock
3218 * Associates the specified block index to the specified depot index.
3219 * This method is only for depot indexes equal or greater than
3220 * COUNT_BBDEPOTINHEADER.
3222 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3224 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3225 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3226 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3227 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3228 ULONG extBlockIndex
;
3230 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3232 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3234 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3236 if (extBlockIndex
!= BLOCK_UNUSED
)
3238 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3239 extBlockOffset
* sizeof(ULONG
),
3243 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3245 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3249 /******************************************************************************
3250 * Storage32Impl_AddExtBlockDepot
3252 * Creates an extended depot block.
3254 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3256 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3257 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3258 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3259 ULONG index
= BLOCK_UNUSED
;
3260 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3261 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3262 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3264 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3265 blocksPerDepotBlock
;
3267 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3270 * The first extended block.
3272 This
->extBigBlockDepotStart
= index
;
3277 * Find the last existing extended block.
3279 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3282 * Add the new extended block to the chain.
3284 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3289 * Initialize this block.
3291 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3292 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3294 /* Add the block to our cache. */
3295 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3297 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3298 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3300 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3301 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3303 This
->extBigBlockDepotLocations
= new_cache
;
3304 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3306 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3311 /******************************************************************************
3312 * Storage32Impl_FreeBigBlock
3314 * This method will flag the specified block as free in the big block depot.
3316 static void StorageImpl_FreeBigBlock(
3320 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3322 if (blockIndex
< This
->prevFreeBlock
)
3323 This
->prevFreeBlock
= blockIndex
;
3326 /************************************************************************
3327 * Storage32Impl_GetNextBlockInChain
3329 * This method will retrieve the block index of the next big block in
3332 * Params: This - Pointer to the Storage object.
3333 * blockIndex - Index of the block to retrieve the chain
3335 * nextBlockIndex - receives the return value.
3337 * Returns: This method returns the index of the next block in the chain.
3338 * It will return the constants:
3339 * BLOCK_SPECIAL - If the block given was not part of a
3341 * BLOCK_END_OF_CHAIN - If the block given was the last in
3343 * BLOCK_UNUSED - If the block given was not past of a chain
3345 * BLOCK_EXTBBDEPOT - This block is part of the extended
3348 * See Windows documentation for more details on IStorage methods.
3350 static HRESULT
StorageImpl_GetNextBlockInChain(
3353 ULONG
* nextBlockIndex
)
3355 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3356 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3357 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3358 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3360 ULONG depotBlockIndexPos
;
3361 int index
, num_blocks
;
3363 *nextBlockIndex
= BLOCK_SPECIAL
;
3365 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3367 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3368 This
->bigBlockDepotCount
);
3369 return STG_E_READFAULT
;
3373 * Cache the currently accessed depot block.
3375 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3377 This
->indexBlockDepotCached
= depotBlockCount
;
3379 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3381 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3386 * We have to look in the extended depot.
3388 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3391 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3394 return STG_E_READFAULT
;
3396 num_blocks
= This
->bigBlockSize
/ 4;
3398 for (index
= 0; index
< num_blocks
; index
++)
3400 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3401 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3405 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3410 /******************************************************************************
3411 * Storage32Impl_GetNextExtendedBlock
3413 * Given an extended block this method will return the next extended block.
3416 * The last ULONG of an extended block is the block index of the next
3417 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3421 * - The index of the next extended block
3422 * - BLOCK_UNUSED: there is no next extended block.
3423 * - Any other return values denotes failure.
3425 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3427 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3428 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3430 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3433 return nextBlockIndex
;
3436 /******************************************************************************
3437 * Storage32Impl_SetNextBlockInChain
3439 * This method will write the index of the specified block's next block
3440 * in the big block depot.
3442 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3445 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3446 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3447 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3450 static void StorageImpl_SetNextBlockInChain(
3455 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3456 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3457 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3458 ULONG depotBlockIndexPos
;
3460 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3461 assert(blockIndex
!= nextBlock
);
3463 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3465 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3470 * We have to look in the extended depot.
3472 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3475 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3478 * Update the cached block depot, if necessary.
3480 if (depotBlockCount
== This
->indexBlockDepotCached
)
3482 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3486 /******************************************************************************
3487 * Storage32Impl_LoadFileHeader
3489 * This method will read in the file header
3491 static HRESULT
StorageImpl_LoadFileHeader(
3495 BYTE headerBigBlock
[HEADER_SIZE
];
3497 ULARGE_INTEGER offset
;
3502 * Get a pointer to the big block of data containing the header.
3504 offset
.u
.HighPart
= 0;
3505 offset
.u
.LowPart
= 0;
3506 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3507 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3508 hr
= STG_E_FILENOTFOUND
;
3511 * Extract the information from the header.
3516 * Check for the "magic number" signature and return an error if it is not
3519 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3521 return STG_E_OLDFORMAT
;
3524 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3526 return STG_E_INVALIDHEADER
;
3529 StorageUtl_ReadWord(
3531 OFFSET_BIGBLOCKSIZEBITS
,
3532 &This
->bigBlockSizeBits
);
3534 StorageUtl_ReadWord(
3536 OFFSET_SMALLBLOCKSIZEBITS
,
3537 &This
->smallBlockSizeBits
);
3539 StorageUtl_ReadDWord(
3541 OFFSET_BBDEPOTCOUNT
,
3542 &This
->bigBlockDepotCount
);
3544 StorageUtl_ReadDWord(
3546 OFFSET_ROOTSTARTBLOCK
,
3547 &This
->rootStartBlock
);
3549 StorageUtl_ReadDWord(
3551 OFFSET_SMALLBLOCKLIMIT
,
3552 &This
->smallBlockLimit
);
3554 StorageUtl_ReadDWord(
3556 OFFSET_SBDEPOTSTART
,
3557 &This
->smallBlockDepotStart
);
3559 StorageUtl_ReadDWord(
3561 OFFSET_EXTBBDEPOTSTART
,
3562 &This
->extBigBlockDepotStart
);
3564 StorageUtl_ReadDWord(
3566 OFFSET_EXTBBDEPOTCOUNT
,
3567 &This
->extBigBlockDepotCount
);
3569 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3571 StorageUtl_ReadDWord(
3573 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3574 &(This
->bigBlockDepotStart
[index
]));
3578 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3580 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3581 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3584 * Right now, the code is making some assumptions about the size of the
3585 * blocks, just make sure they are what we're expecting.
3587 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3588 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3589 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3591 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3592 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3593 hr
= STG_E_INVALIDHEADER
;
3602 /******************************************************************************
3603 * Storage32Impl_SaveFileHeader
3605 * This method will save to the file the header
3607 static void StorageImpl_SaveFileHeader(
3610 BYTE headerBigBlock
[HEADER_SIZE
];
3613 ULARGE_INTEGER offset
;
3614 DWORD bytes_read
, bytes_written
;
3615 DWORD major_version
, dirsectorcount
;
3618 * Get a pointer to the big block of data containing the header.
3620 offset
.u
.HighPart
= 0;
3621 offset
.u
.LowPart
= 0;
3622 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3623 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3624 hr
= STG_E_FILENOTFOUND
;
3626 if (This
->bigBlockSizeBits
== 0x9)
3628 else if (This
->bigBlockSizeBits
== 0xc)
3632 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3637 * If the block read failed, the file is probably new.
3642 * Initialize for all unknown fields.
3644 memset(headerBigBlock
, 0, HEADER_SIZE
);
3647 * Initialize the magic number.
3649 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3653 * Write the information to the header.
3655 StorageUtl_WriteWord(
3657 OFFSET_MINORVERSION
,
3660 StorageUtl_WriteWord(
3662 OFFSET_MAJORVERSION
,
3665 StorageUtl_WriteWord(
3667 OFFSET_BYTEORDERMARKER
,
3670 StorageUtl_WriteWord(
3672 OFFSET_BIGBLOCKSIZEBITS
,
3673 This
->bigBlockSizeBits
);
3675 StorageUtl_WriteWord(
3677 OFFSET_SMALLBLOCKSIZEBITS
,
3678 This
->smallBlockSizeBits
);
3680 if (major_version
>= 4)
3682 if (This
->rootBlockChain
)
3683 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3685 /* This file is being created, and it will start out with one block. */
3689 /* This field must be 0 in versions older than 4 */
3692 StorageUtl_WriteDWord(
3694 OFFSET_DIRSECTORCOUNT
,
3697 StorageUtl_WriteDWord(
3699 OFFSET_BBDEPOTCOUNT
,
3700 This
->bigBlockDepotCount
);
3702 StorageUtl_WriteDWord(
3704 OFFSET_ROOTSTARTBLOCK
,
3705 This
->rootStartBlock
);
3707 StorageUtl_WriteDWord(
3709 OFFSET_SMALLBLOCKLIMIT
,
3710 This
->smallBlockLimit
);
3712 StorageUtl_WriteDWord(
3714 OFFSET_SBDEPOTSTART
,
3715 This
->smallBlockDepotStart
);
3717 StorageUtl_WriteDWord(
3719 OFFSET_SBDEPOTCOUNT
,
3720 This
->smallBlockDepotChain
?
3721 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3723 StorageUtl_WriteDWord(
3725 OFFSET_EXTBBDEPOTSTART
,
3726 This
->extBigBlockDepotStart
);
3728 StorageUtl_WriteDWord(
3730 OFFSET_EXTBBDEPOTCOUNT
,
3731 This
->extBigBlockDepotCount
);
3733 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3735 StorageUtl_WriteDWord(
3737 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3738 (This
->bigBlockDepotStart
[index
]));
3742 * Write the big block back to the file.
3744 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3747 /******************************************************************************
3748 * StorageImpl_ReadRawDirEntry
3750 * This method will read the raw data from a directory entry in the file.
3752 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3754 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3756 ULARGE_INTEGER offset
;
3760 offset
.u
.HighPart
= 0;
3761 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3763 hr
= BlockChainStream_ReadAt(
3764 This
->rootBlockChain
,
3770 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3771 return STG_E_READFAULT
;
3776 /******************************************************************************
3777 * StorageImpl_WriteRawDirEntry
3779 * This method will write the raw data from a directory entry in the file.
3781 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3783 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3785 ULARGE_INTEGER offset
;
3789 offset
.u
.HighPart
= 0;
3790 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3792 hr
= BlockChainStream_WriteAt(
3793 This
->rootBlockChain
,
3802 /******************************************************************************
3805 * Update raw directory entry data from the fields in newData.
3807 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3809 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3811 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3814 buffer
+ OFFSET_PS_NAME
,
3816 DIRENTRY_NAME_BUFFER_LEN
);
3818 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3820 StorageUtl_WriteWord(
3822 OFFSET_PS_NAMELENGTH
,
3823 newData
->sizeOfNameString
);
3825 StorageUtl_WriteDWord(
3827 OFFSET_PS_LEFTCHILD
,
3828 newData
->leftChild
);
3830 StorageUtl_WriteDWord(
3832 OFFSET_PS_RIGHTCHILD
,
3833 newData
->rightChild
);
3835 StorageUtl_WriteDWord(
3838 newData
->dirRootEntry
);
3840 StorageUtl_WriteGUID(
3845 StorageUtl_WriteDWord(
3848 newData
->ctime
.dwLowDateTime
);
3850 StorageUtl_WriteDWord(
3852 OFFSET_PS_CTIMEHIGH
,
3853 newData
->ctime
.dwHighDateTime
);
3855 StorageUtl_WriteDWord(
3858 newData
->mtime
.dwLowDateTime
);
3860 StorageUtl_WriteDWord(
3862 OFFSET_PS_MTIMEHIGH
,
3863 newData
->ctime
.dwHighDateTime
);
3865 StorageUtl_WriteDWord(
3867 OFFSET_PS_STARTBLOCK
,
3868 newData
->startingBlock
);
3870 StorageUtl_WriteDWord(
3873 newData
->size
.u
.LowPart
);
3876 /******************************************************************************
3877 * Storage32Impl_ReadDirEntry
3879 * This method will read the specified directory entry.
3881 HRESULT
StorageImpl_ReadDirEntry(
3886 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3889 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3891 if (SUCCEEDED(readRes
))
3893 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3896 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3897 DIRENTRY_NAME_BUFFER_LEN
);
3898 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3900 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3902 StorageUtl_ReadWord(
3904 OFFSET_PS_NAMELENGTH
,
3905 &buffer
->sizeOfNameString
);
3907 StorageUtl_ReadDWord(
3909 OFFSET_PS_LEFTCHILD
,
3910 &buffer
->leftChild
);
3912 StorageUtl_ReadDWord(
3914 OFFSET_PS_RIGHTCHILD
,
3915 &buffer
->rightChild
);
3917 StorageUtl_ReadDWord(
3920 &buffer
->dirRootEntry
);
3922 StorageUtl_ReadGUID(
3927 StorageUtl_ReadDWord(
3930 &buffer
->ctime
.dwLowDateTime
);
3932 StorageUtl_ReadDWord(
3934 OFFSET_PS_CTIMEHIGH
,
3935 &buffer
->ctime
.dwHighDateTime
);
3937 StorageUtl_ReadDWord(
3940 &buffer
->mtime
.dwLowDateTime
);
3942 StorageUtl_ReadDWord(
3944 OFFSET_PS_MTIMEHIGH
,
3945 &buffer
->mtime
.dwHighDateTime
);
3947 StorageUtl_ReadDWord(
3949 OFFSET_PS_STARTBLOCK
,
3950 &buffer
->startingBlock
);
3952 StorageUtl_ReadDWord(
3955 &buffer
->size
.u
.LowPart
);
3957 buffer
->size
.u
.HighPart
= 0;
3963 /*********************************************************************
3964 * Write the specified directory entry to the file
3966 HRESULT
StorageImpl_WriteDirEntry(
3969 const DirEntry
* buffer
)
3971 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3974 UpdateRawDirEntry(currentEntry
, buffer
);
3976 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3980 static BOOL
StorageImpl_ReadBigBlock(
3985 ULARGE_INTEGER ulOffset
;
3988 ulOffset
.u
.HighPart
= 0;
3989 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3991 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3993 if (read
&& read
< This
->bigBlockSize
)
3995 /* File ends during this block; fill the rest with 0's. */
3996 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4002 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4008 ULARGE_INTEGER ulOffset
;
4012 ulOffset
.u
.HighPart
= 0;
4013 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4014 ulOffset
.u
.LowPart
+= offset
;
4016 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4017 *value
= lendian32toh(tmp
);
4018 return (read
== sizeof(DWORD
));
4021 static BOOL
StorageImpl_WriteBigBlock(
4026 ULARGE_INTEGER ulOffset
;
4029 ulOffset
.u
.HighPart
= 0;
4030 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4032 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4033 return (wrote
== This
->bigBlockSize
);
4036 static BOOL
StorageImpl_WriteDWordToBigBlock(
4042 ULARGE_INTEGER ulOffset
;
4045 ulOffset
.u
.HighPart
= 0;
4046 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4047 ulOffset
.u
.LowPart
+= offset
;
4049 value
= htole32(value
);
4050 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4051 return (wrote
== sizeof(DWORD
));
4054 /******************************************************************************
4055 * Storage32Impl_SmallBlocksToBigBlocks
4057 * This method will convert a small block chain to a big block chain.
4058 * The small block chain will be destroyed.
4060 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4062 SmallBlockChainStream
** ppsbChain
)
4064 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4065 ULARGE_INTEGER size
, offset
;
4066 ULONG cbRead
, cbWritten
;
4067 ULARGE_INTEGER cbTotalRead
;
4068 DirRef streamEntryRef
;
4069 HRESULT resWrite
= S_OK
;
4071 DirEntry streamEntry
;
4073 BlockChainStream
*bbTempChain
= NULL
;
4074 BlockChainStream
*bigBlockChain
= NULL
;
4077 * Create a temporary big block chain that doesn't have
4078 * an associated directory entry. This temporary chain will be
4079 * used to copy data from small blocks to big blocks.
4081 bbTempChain
= BlockChainStream_Construct(This
,
4084 if(!bbTempChain
) return NULL
;
4086 * Grow the big block chain.
4088 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4089 BlockChainStream_SetSize(bbTempChain
, size
);
4092 * Copy the contents of the small block chain to the big block chain
4093 * by small block size increments.
4095 offset
.u
.LowPart
= 0;
4096 offset
.u
.HighPart
= 0;
4097 cbTotalRead
.QuadPart
= 0;
4099 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4102 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4104 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4107 if (FAILED(resRead
))
4112 cbTotalRead
.QuadPart
+= cbRead
;
4114 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4120 if (FAILED(resWrite
))
4123 offset
.u
.LowPart
+= cbRead
;
4127 resRead
= STG_E_READFAULT
;
4130 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4131 HeapFree(GetProcessHeap(),0,buffer
);
4133 size
.u
.HighPart
= 0;
4136 if (FAILED(resRead
) || FAILED(resWrite
))
4138 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4139 BlockChainStream_SetSize(bbTempChain
, size
);
4140 BlockChainStream_Destroy(bbTempChain
);
4145 * Destroy the small block chain.
4147 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4148 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4149 SmallBlockChainStream_Destroy(*ppsbChain
);
4153 * Change the directory entry. This chain is now a big block chain
4154 * and it doesn't reside in the small blocks chain anymore.
4156 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4158 streamEntry
.startingBlock
= bbHeadOfChain
;
4160 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4163 * Destroy the temporary entryless big block chain.
4164 * Create a new big block chain associated with this entry.
4166 BlockChainStream_Destroy(bbTempChain
);
4167 bigBlockChain
= BlockChainStream_Construct(This
,
4171 return bigBlockChain
;
4174 /******************************************************************************
4175 * Storage32Impl_BigBlocksToSmallBlocks
4177 * This method will convert a big block chain to a small block chain.
4178 * The big block chain will be destroyed on success.
4180 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4182 BlockChainStream
** ppbbChain
,
4183 ULARGE_INTEGER newSize
)
4185 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4186 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4187 DirRef streamEntryRef
;
4188 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4189 DirEntry streamEntry
;
4191 SmallBlockChainStream
* sbTempChain
;
4193 TRACE("%p %p\n", This
, ppbbChain
);
4195 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4201 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4202 size
= BlockChainStream_GetSize(*ppbbChain
);
4203 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4205 offset
.u
.HighPart
= 0;
4206 offset
.u
.LowPart
= 0;
4207 cbTotalRead
.QuadPart
= 0;
4208 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4209 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4211 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4212 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4220 cbTotalRead
.QuadPart
+= cbRead
;
4222 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4223 cbRead
, buffer
, &cbWritten
);
4225 if(FAILED(resWrite
))
4228 offset
.u
.LowPart
+= cbRead
;
4232 resRead
= STG_E_READFAULT
;
4236 HeapFree(GetProcessHeap(), 0, buffer
);
4238 size
.u
.HighPart
= 0;
4241 if(FAILED(resRead
) || FAILED(resWrite
))
4243 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4244 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4245 SmallBlockChainStream_Destroy(sbTempChain
);
4249 /* destroy the original big block chain */
4250 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4251 BlockChainStream_SetSize(*ppbbChain
, size
);
4252 BlockChainStream_Destroy(*ppbbChain
);
4255 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4256 streamEntry
.startingBlock
= sbHeadOfChain
;
4257 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4259 SmallBlockChainStream_Destroy(sbTempChain
);
4260 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4263 static HRESULT
StorageBaseImpl_CopyStream(
4264 StorageBaseImpl
*dst
, DirRef dst_entry
,
4265 StorageBaseImpl
*src
, DirRef src_entry
)
4270 ULARGE_INTEGER bytes_copied
;
4271 ULONG bytestocopy
, bytesread
, byteswritten
;
4273 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4277 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4279 bytes_copied
.QuadPart
= 0;
4280 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4282 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4284 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4286 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4289 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4290 data
, &byteswritten
);
4293 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4294 bytes_copied
.QuadPart
+= byteswritten
;
4302 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4304 DirRef result
=This
->firstFreeEntry
;
4306 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4309 if (result
== This
->entries_size
)
4311 ULONG new_size
= This
->entries_size
* 2;
4312 TransactedDirEntry
*new_entries
;
4314 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4315 if (!new_entries
) return DIRENTRY_NULL
;
4317 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4318 HeapFree(GetProcessHeap(), 0, This
->entries
);
4320 This
->entries
= new_entries
;
4321 This
->entries_size
= new_size
;
4324 This
->entries
[result
].inuse
= 1;
4326 This
->firstFreeEntry
= result
+1;
4331 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4332 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4334 DirRef stubEntryRef
;
4335 TransactedDirEntry
*entry
;
4337 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4339 if (stubEntryRef
!= DIRENTRY_NULL
)
4341 entry
= &This
->entries
[stubEntryRef
];
4343 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4348 return stubEntryRef
;
4351 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4352 TransactedSnapshotImpl
*This
, DirRef entry
)
4357 if (!This
->entries
[entry
].read
)
4359 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4360 This
->entries
[entry
].transactedParentEntry
,
4363 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4365 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4367 if (data
.leftChild
== DIRENTRY_NULL
)
4371 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4373 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4375 if (data
.rightChild
== DIRENTRY_NULL
)
4379 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4381 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4383 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4389 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4390 This
->entries
[entry
].read
= 1;
4397 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4398 TransactedSnapshotImpl
*This
, DirRef entry
)
4402 if (!This
->entries
[entry
].stream_dirty
)
4404 DirEntry new_entrydata
;
4406 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4407 new_entrydata
.name
[0] = 'S';
4408 new_entrydata
.sizeOfNameString
= 1;
4409 new_entrydata
.stgType
= STGTY_STREAM
;
4410 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4411 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4412 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4413 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4415 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4416 &This
->entries
[entry
].stream_entry
);
4418 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4420 hr
= StorageBaseImpl_CopyStream(
4421 This
->scratch
, This
->entries
[entry
].stream_entry
,
4422 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4425 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4429 This
->entries
[entry
].stream_dirty
= 1;
4431 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4433 /* Since this entry is modified, and we aren't using its stream data, we
4434 * no longer care about the original entry. */
4436 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4438 if (delete_ref
!= DIRENTRY_NULL
)
4439 This
->entries
[delete_ref
].deleted
= 1;
4441 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4448 /* Find the first entry in a depth-first traversal. */
4449 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4450 TransactedSnapshotImpl
* This
, DirRef parent
)
4452 DirRef cursor
, prev
;
4453 TransactedDirEntry
*entry
;
4456 entry
= &This
->entries
[cursor
];
4459 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4462 cursor
= entry
->data
.leftChild
;
4463 entry
= &This
->entries
[cursor
];
4464 entry
->parent
= prev
;
4466 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4469 cursor
= entry
->data
.rightChild
;
4470 entry
= &This
->entries
[cursor
];
4471 entry
->parent
= prev
;
4473 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4476 cursor
= entry
->data
.dirRootEntry
;
4477 entry
= &This
->entries
[cursor
];
4478 entry
->parent
= prev
;
4487 /* Find the next entry in a depth-first traversal. */
4488 static DirRef
TransactedSnapshotImpl_FindNextChild(
4489 TransactedSnapshotImpl
* This
, DirRef current
)
4492 TransactedDirEntry
*parent_entry
;
4494 parent
= This
->entries
[current
].parent
;
4495 parent_entry
= &This
->entries
[parent
];
4497 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4499 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4501 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4502 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4505 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4507 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4508 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4515 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4516 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4517 TransactedSnapshotImpl
* This
, DirRef entry
)
4519 return entry
!= DIRENTRY_NULL
&&
4520 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4523 /* Destroy the entries created by CopyTree. */
4524 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4525 TransactedSnapshotImpl
* This
, DirRef stop
)
4528 TransactedDirEntry
*entry
;
4529 ULARGE_INTEGER zero
;
4533 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4536 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4538 if (cursor
== DIRENTRY_NULL
)
4541 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4543 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4545 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4547 entry
= &This
->entries
[cursor
];
4549 if (entry
->stream_dirty
)
4550 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4551 entry
->newTransactedParentEntry
, zero
);
4553 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4554 entry
->newTransactedParentEntry
);
4556 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4559 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4563 /* Make a copy of our edited tree that we can use in the parent. */
4564 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4567 TransactedDirEntry
*entry
;
4570 cursor
= This
->base
.storageDirEntry
;
4571 entry
= &This
->entries
[cursor
];
4572 entry
->parent
= DIRENTRY_NULL
;
4573 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4575 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4578 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4580 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4581 entry
= &This
->entries
[cursor
];
4583 while (cursor
!= DIRENTRY_NULL
)
4585 /* Make a copy of this entry in the transacted parent. */
4587 (!entry
->dirty
&& !entry
->stream_dirty
&&
4588 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4589 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4590 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4591 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4596 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4598 newData
.size
.QuadPart
= 0;
4599 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4601 if (newData
.leftChild
!= DIRENTRY_NULL
)
4602 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4604 if (newData
.rightChild
!= DIRENTRY_NULL
)
4605 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4607 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4608 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4610 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4611 &entry
->newTransactedParentEntry
);
4614 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4618 if (entry
->stream_dirty
)
4620 hr
= StorageBaseImpl_CopyStream(
4621 This
->transactedParent
, entry
->newTransactedParentEntry
,
4622 This
->scratch
, entry
->stream_entry
);
4624 else if (entry
->data
.size
.QuadPart
)
4626 hr
= StorageBaseImpl_StreamLink(
4627 This
->transactedParent
, entry
->newTransactedParentEntry
,
4628 entry
->transactedParentEntry
);
4633 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4634 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4639 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4640 entry
= &This
->entries
[cursor
];
4646 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4648 DWORD grfCommitFlags
) /* [in] */
4650 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4651 TransactedDirEntry
*root_entry
;
4652 DirRef i
, dir_root_ref
;
4654 ULARGE_INTEGER zero
;
4659 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4661 /* Cannot commit a read-only transacted storage */
4662 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4663 return STG_E_ACCESSDENIED
;
4665 /* To prevent data loss, we create the new structure in the file before we
4666 * delete the old one, so that in case of errors the old data is intact. We
4667 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4668 * needed in the rare situation where we have just enough free disk space to
4669 * overwrite the existing data. */
4671 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4673 if (!root_entry
->read
)
4676 hr
= TransactedSnapshotImpl_CopyTree(This
);
4677 if (FAILED(hr
)) return hr
;
4679 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4680 dir_root_ref
= DIRENTRY_NULL
;
4682 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4684 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4686 /* Update the storage to use the new data in one step. */
4688 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4689 root_entry
->transactedParentEntry
, &data
);
4693 data
.dirRootEntry
= dir_root_ref
;
4694 data
.clsid
= root_entry
->data
.clsid
;
4695 data
.ctime
= root_entry
->data
.ctime
;
4696 data
.mtime
= root_entry
->data
.mtime
;
4698 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4699 root_entry
->transactedParentEntry
, &data
);
4702 /* Try to flush after updating the root storage, but if the flush fails, keep
4703 * going, on the theory that it'll either succeed later or the subsequent
4704 * writes will fail. */
4705 StorageBaseImpl_Flush(This
->transactedParent
);
4709 /* Destroy the old now-orphaned data. */
4710 for (i
=0; i
<This
->entries_size
; i
++)
4712 TransactedDirEntry
*entry
= &This
->entries
[i
];
4717 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4718 entry
->transactedParentEntry
, zero
);
4719 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4720 entry
->transactedParentEntry
);
4721 memset(entry
, 0, sizeof(TransactedDirEntry
));
4722 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4724 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4726 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4727 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4728 entry
->transactedParentEntry
);
4729 if (entry
->stream_dirty
)
4731 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4732 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4733 entry
->stream_dirty
= 0;
4736 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4743 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4747 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4752 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4755 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4756 ULARGE_INTEGER zero
;
4759 TRACE("(%p)\n", iface
);
4761 /* Destroy the open objects. */
4762 StorageBaseImpl_DeleteAll(&This
->base
);
4764 /* Clear out the scratch file. */
4766 for (i
=0; i
<This
->entries_size
; i
++)
4768 if (This
->entries
[i
].stream_dirty
)
4770 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4773 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4777 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4779 This
->firstFreeEntry
= 0;
4780 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4785 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4787 if (!This
->reverted
)
4789 TRACE("Storage invalidated (stg=%p)\n", This
);
4793 StorageBaseImpl_DeleteAll(This
);
4797 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4799 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4801 IStorage_Revert(&This
->base
.IStorage_iface
);
4802 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
4803 IStorage_Release(&This
->scratch
->IStorage_iface
);
4804 HeapFree(GetProcessHeap(), 0, This
->entries
);
4805 HeapFree(GetProcessHeap(), 0, This
);
4808 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4810 /* We only need to flush when committing. */
4814 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4816 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4818 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4821 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4822 const DirEntry
*newData
, DirRef
*index
)
4824 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4826 TransactedDirEntry
*new_entry
;
4828 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4829 if (new_ref
== DIRENTRY_NULL
)
4830 return E_OUTOFMEMORY
;
4832 new_entry
= &This
->entries
[new_ref
];
4834 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4835 new_entry
->read
= 1;
4836 new_entry
->dirty
= 1;
4837 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4841 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4846 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4847 DirRef index
, const DirEntry
*data
)
4849 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4852 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4854 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4855 if (FAILED(hr
)) return hr
;
4857 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4859 if (index
!= This
->base
.storageDirEntry
)
4861 This
->entries
[index
].dirty
= 1;
4863 if (data
->size
.QuadPart
== 0 &&
4864 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4866 /* Since this entry is modified, and we aren't using its stream data, we
4867 * no longer care about the original entry. */
4869 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4871 if (delete_ref
!= DIRENTRY_NULL
)
4872 This
->entries
[delete_ref
].deleted
= 1;
4874 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4881 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4882 DirRef index
, DirEntry
*data
)
4884 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4887 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4888 if (FAILED(hr
)) return hr
;
4890 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4892 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4897 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4900 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4902 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4903 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4905 /* If we deleted this entry while it has stream data. We must have left the
4906 * data because some other entry is using it, and we need to leave the
4907 * original entry alone. */
4908 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4909 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4913 This
->entries
[index
].deleted
= 1;
4919 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4920 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4922 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4924 if (This
->entries
[index
].stream_dirty
)
4926 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4927 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4929 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4931 /* This stream doesn't live in the parent, and we haven't allocated storage
4938 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4939 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4943 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4944 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4946 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4949 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4950 if (FAILED(hr
)) return hr
;
4952 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4953 if (FAILED(hr
)) return hr
;
4955 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4956 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4958 if (SUCCEEDED(hr
) && size
!= 0)
4959 This
->entries
[index
].data
.size
.QuadPart
= max(
4960 This
->entries
[index
].data
.size
.QuadPart
,
4961 offset
.QuadPart
+ size
);
4966 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4967 DirRef index
, ULARGE_INTEGER newsize
)
4969 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4972 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4973 if (FAILED(hr
)) return hr
;
4975 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4978 if (newsize
.QuadPart
== 0)
4980 /* Destroy any parent references or entries in the scratch file. */
4981 if (This
->entries
[index
].stream_dirty
)
4983 ULARGE_INTEGER zero
;
4985 StorageBaseImpl_StreamSetSize(This
->scratch
,
4986 This
->entries
[index
].stream_entry
, zero
);
4987 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4988 This
->entries
[index
].stream_entry
);
4989 This
->entries
[index
].stream_dirty
= 0;
4991 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4994 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4996 if (delete_ref
!= DIRENTRY_NULL
)
4997 This
->entries
[delete_ref
].deleted
= 1;
4999 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5004 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5005 if (FAILED(hr
)) return hr
;
5007 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5008 This
->entries
[index
].stream_entry
, newsize
);
5012 This
->entries
[index
].data
.size
= newsize
;
5017 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5018 DirRef dst
, DirRef src
)
5020 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5022 TransactedDirEntry
*dst_entry
, *src_entry
;
5024 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5025 if (FAILED(hr
)) return hr
;
5027 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5028 if (FAILED(hr
)) return hr
;
5030 dst_entry
= &This
->entries
[dst
];
5031 src_entry
= &This
->entries
[src
];
5033 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5034 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5035 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5036 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5037 dst_entry
->data
.size
= src_entry
->data
.size
;
5042 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5044 StorageBaseImpl_QueryInterface
,
5045 StorageBaseImpl_AddRef
,
5046 StorageBaseImpl_Release
,
5047 StorageBaseImpl_CreateStream
,
5048 StorageBaseImpl_OpenStream
,
5049 StorageBaseImpl_CreateStorage
,
5050 StorageBaseImpl_OpenStorage
,
5051 StorageBaseImpl_CopyTo
,
5052 StorageBaseImpl_MoveElementTo
,
5053 TransactedSnapshotImpl_Commit
,
5054 TransactedSnapshotImpl_Revert
,
5055 StorageBaseImpl_EnumElements
,
5056 StorageBaseImpl_DestroyElement
,
5057 StorageBaseImpl_RenameElement
,
5058 StorageBaseImpl_SetElementTimes
,
5059 StorageBaseImpl_SetClass
,
5060 StorageBaseImpl_SetStateBits
,
5061 StorageBaseImpl_Stat
5064 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5066 TransactedSnapshotImpl_Destroy
,
5067 TransactedSnapshotImpl_Invalidate
,
5068 TransactedSnapshotImpl_Flush
,
5069 TransactedSnapshotImpl_GetFilename
,
5070 TransactedSnapshotImpl_CreateDirEntry
,
5071 TransactedSnapshotImpl_WriteDirEntry
,
5072 TransactedSnapshotImpl_ReadDirEntry
,
5073 TransactedSnapshotImpl_DestroyDirEntry
,
5074 TransactedSnapshotImpl_StreamReadAt
,
5075 TransactedSnapshotImpl_StreamWriteAt
,
5076 TransactedSnapshotImpl_StreamSetSize
,
5077 TransactedSnapshotImpl_StreamLink
5080 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5081 TransactedSnapshotImpl
** result
)
5085 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5090 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5092 /* This is OK because the property set storage functions use the IStorage functions. */
5093 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5094 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5096 list_init(&(*result
)->base
.strmHead
);
5098 list_init(&(*result
)->base
.storageHead
);
5100 (*result
)->base
.ref
= 1;
5102 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5104 /* Create a new temporary storage to act as the scratch file. */
5105 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5107 (*result
)->scratch
= impl_from_IStorage(scratch
);
5111 ULONG num_entries
= 20;
5113 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5114 (*result
)->entries_size
= num_entries
;
5115 (*result
)->firstFreeEntry
= 0;
5117 if ((*result
)->entries
)
5119 /* parentStorage already has 1 reference, which we take over here. */
5120 (*result
)->transactedParent
= parentStorage
;
5122 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5124 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5128 IStorage_Release(scratch
);
5134 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5139 return E_OUTOFMEMORY
;
5142 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5143 StorageBaseImpl
** result
)
5147 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5149 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5152 return TransactedSnapshotImpl_Construct(parentStorage
,
5153 (TransactedSnapshotImpl
**)result
);
5156 static HRESULT
Storage_Construct(
5164 StorageBaseImpl
** result
)
5166 StorageImpl
*newStorage
;
5167 StorageBaseImpl
*newTransactedStorage
;
5170 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5171 if (FAILED(hr
)) goto end
;
5173 if (openFlags
& STGM_TRANSACTED
)
5175 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5177 IStorage_Release(&newStorage
->base
.IStorage_iface
);
5179 *result
= newTransactedStorage
;
5182 *result
= &newStorage
->base
;
5188 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5190 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5192 if (!This
->base
.reverted
)
5194 TRACE("Storage invalidated (stg=%p)\n", This
);
5196 This
->base
.reverted
= 1;
5198 This
->parentStorage
= NULL
;
5200 StorageBaseImpl_DeleteAll(&This
->base
);
5202 list_remove(&This
->ParentListEntry
);
5206 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5208 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5210 StorageInternalImpl_Invalidate(&This
->base
);
5212 HeapFree(GetProcessHeap(), 0, This
);
5215 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5217 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5219 return StorageBaseImpl_Flush(This
->parentStorage
);
5222 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5224 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5226 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5229 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5230 const DirEntry
*newData
, DirRef
*index
)
5232 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5234 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5238 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5239 DirRef index
, const DirEntry
*data
)
5241 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5243 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5247 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5248 DirRef index
, DirEntry
*data
)
5250 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5252 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5256 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5259 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5261 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5265 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5266 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5268 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5270 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5271 index
, offset
, size
, buffer
, bytesRead
);
5274 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5275 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5277 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5279 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5280 index
, offset
, size
, buffer
, bytesWritten
);
5283 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5284 DirRef index
, ULARGE_INTEGER newsize
)
5286 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5288 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5292 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5293 DirRef dst
, DirRef src
)
5295 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5297 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5301 /******************************************************************************
5303 ** Storage32InternalImpl_Commit
5306 static HRESULT WINAPI
StorageInternalImpl_Commit(
5308 DWORD grfCommitFlags
) /* [in] */
5310 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5311 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5312 return StorageBaseImpl_Flush(This
);
5315 /******************************************************************************
5317 ** Storage32InternalImpl_Revert
5320 static HRESULT WINAPI
StorageInternalImpl_Revert(
5323 FIXME("(%p): stub\n", iface
);
5327 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5329 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
5330 HeapFree(GetProcessHeap(), 0, This
);
5333 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5334 IEnumSTATSTG
* iface
,
5338 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5341 return E_INVALIDARG
;
5345 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5346 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5349 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5353 return E_NOINTERFACE
;
5356 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5357 IEnumSTATSTG
* iface
)
5359 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5360 return InterlockedIncrement(&This
->ref
);
5363 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5364 IEnumSTATSTG
* iface
)
5366 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5370 newRef
= InterlockedDecrement(&This
->ref
);
5374 IEnumSTATSTGImpl_Destroy(This
);
5380 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5381 IEnumSTATSTGImpl
* This
,
5384 DirRef result
= DIRENTRY_NULL
;
5388 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5390 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5391 This
->parentStorage
->storageDirEntry
, &entry
);
5392 searchNode
= entry
.dirRootEntry
;
5394 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5396 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5400 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5404 searchNode
= entry
.rightChild
;
5408 result
= searchNode
;
5409 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5410 searchNode
= entry
.leftChild
;
5418 if (result
!= DIRENTRY_NULL
)
5419 memcpy(This
->name
, result_name
, sizeof(result_name
));
5425 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5426 IEnumSTATSTG
* iface
,
5429 ULONG
* pceltFetched
)
5431 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5433 DirEntry currentEntry
;
5434 STATSTG
* currentReturnStruct
= rgelt
;
5435 ULONG objectFetched
= 0;
5436 DirRef currentSearchNode
;
5439 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5440 return E_INVALIDARG
;
5442 if (This
->parentStorage
->reverted
)
5443 return STG_E_REVERTED
;
5446 * To avoid the special case, get another pointer to a ULONG value if
5447 * the caller didn't supply one.
5449 if (pceltFetched
==0)
5450 pceltFetched
= &objectFetched
;
5453 * Start the iteration, we will iterate until we hit the end of the
5454 * linked list or until we hit the number of items to iterate through
5458 while ( *pceltFetched
< celt
)
5460 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5462 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5466 * Read the entry from the storage.
5468 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5473 * Copy the information to the return buffer.
5475 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5476 currentReturnStruct
,
5481 * Step to the next item in the iteration
5484 currentReturnStruct
++;
5487 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5494 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5495 IEnumSTATSTG
* iface
,
5498 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5500 ULONG objectFetched
= 0;
5501 DirRef currentSearchNode
;
5504 if (This
->parentStorage
->reverted
)
5505 return STG_E_REVERTED
;
5507 while ( (objectFetched
< celt
) )
5509 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5511 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5517 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5523 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5524 IEnumSTATSTG
* iface
)
5526 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5528 if (This
->parentStorage
->reverted
)
5529 return STG_E_REVERTED
;
5536 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5537 IEnumSTATSTG
* iface
,
5538 IEnumSTATSTG
** ppenum
)
5540 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5542 IEnumSTATSTGImpl
* newClone
;
5544 if (This
->parentStorage
->reverted
)
5545 return STG_E_REVERTED
;
5548 * Perform a sanity check on the parameters.
5551 return E_INVALIDARG
;
5553 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5554 This
->storageDirEntry
);
5558 * The new clone enumeration must point to the same current node as
5561 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5563 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5566 * Don't forget to nail down a reference to the clone before
5569 IEnumSTATSTGImpl_AddRef(*ppenum
);
5575 * Virtual function table for the IEnumSTATSTGImpl class.
5577 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5579 IEnumSTATSTGImpl_QueryInterface
,
5580 IEnumSTATSTGImpl_AddRef
,
5581 IEnumSTATSTGImpl_Release
,
5582 IEnumSTATSTGImpl_Next
,
5583 IEnumSTATSTGImpl_Skip
,
5584 IEnumSTATSTGImpl_Reset
,
5585 IEnumSTATSTGImpl_Clone
5588 /******************************************************************************
5589 ** IEnumSTATSTGImpl implementation
5592 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5593 StorageBaseImpl
* parentStorage
,
5594 DirRef storageDirEntry
)
5596 IEnumSTATSTGImpl
* newEnumeration
;
5598 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5600 if (newEnumeration
!=0)
5603 * Set-up the virtual function table and reference count.
5605 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5606 newEnumeration
->ref
= 0;
5609 * We want to nail-down the reference to the storage in case the
5610 * enumeration out-lives the storage in the client application.
5612 newEnumeration
->parentStorage
= parentStorage
;
5613 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
5615 newEnumeration
->storageDirEntry
= storageDirEntry
;
5618 * Make sure the current node of the iterator is the first one.
5620 IEnumSTATSTGImpl_Reset(&newEnumeration
->IEnumSTATSTG_iface
);
5623 return newEnumeration
;
5627 * Virtual function table for the Storage32InternalImpl class.
5629 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5631 StorageBaseImpl_QueryInterface
,
5632 StorageBaseImpl_AddRef
,
5633 StorageBaseImpl_Release
,
5634 StorageBaseImpl_CreateStream
,
5635 StorageBaseImpl_OpenStream
,
5636 StorageBaseImpl_CreateStorage
,
5637 StorageBaseImpl_OpenStorage
,
5638 StorageBaseImpl_CopyTo
,
5639 StorageBaseImpl_MoveElementTo
,
5640 StorageInternalImpl_Commit
,
5641 StorageInternalImpl_Revert
,
5642 StorageBaseImpl_EnumElements
,
5643 StorageBaseImpl_DestroyElement
,
5644 StorageBaseImpl_RenameElement
,
5645 StorageBaseImpl_SetElementTimes
,
5646 StorageBaseImpl_SetClass
,
5647 StorageBaseImpl_SetStateBits
,
5648 StorageBaseImpl_Stat
5651 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5653 StorageInternalImpl_Destroy
,
5654 StorageInternalImpl_Invalidate
,
5655 StorageInternalImpl_Flush
,
5656 StorageInternalImpl_GetFilename
,
5657 StorageInternalImpl_CreateDirEntry
,
5658 StorageInternalImpl_WriteDirEntry
,
5659 StorageInternalImpl_ReadDirEntry
,
5660 StorageInternalImpl_DestroyDirEntry
,
5661 StorageInternalImpl_StreamReadAt
,
5662 StorageInternalImpl_StreamWriteAt
,
5663 StorageInternalImpl_StreamSetSize
,
5664 StorageInternalImpl_StreamLink
5667 /******************************************************************************
5668 ** Storage32InternalImpl implementation
5671 static StorageInternalImpl
* StorageInternalImpl_Construct(
5672 StorageBaseImpl
* parentStorage
,
5674 DirRef storageDirEntry
)
5676 StorageInternalImpl
* newStorage
;
5678 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5682 list_init(&newStorage
->base
.strmHead
);
5684 list_init(&newStorage
->base
.storageHead
);
5687 * Initialize the virtual function table.
5689 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5690 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5691 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5692 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5694 newStorage
->base
.reverted
= 0;
5696 newStorage
->base
.ref
= 1;
5698 newStorage
->parentStorage
= parentStorage
;
5701 * Keep a reference to the directory entry of this storage
5703 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5705 newStorage
->base
.create
= 0;
5713 /******************************************************************************
5714 ** StorageUtl implementation
5717 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5721 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5722 *value
= lendian16toh(tmp
);
5725 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5727 value
= htole16(value
);
5728 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5731 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5735 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5736 *value
= lendian32toh(tmp
);
5739 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5741 value
= htole32(value
);
5742 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5745 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5746 ULARGE_INTEGER
* value
)
5748 #ifdef WORDS_BIGENDIAN
5751 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5752 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5753 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5755 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5759 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5760 const ULARGE_INTEGER
*value
)
5762 #ifdef WORDS_BIGENDIAN
5765 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5766 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5767 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5769 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5773 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5775 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5776 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5777 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5779 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5782 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5784 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5785 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5786 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5788 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5791 void StorageUtl_CopyDirEntryToSTATSTG(
5792 StorageBaseImpl
* storage
,
5793 STATSTG
* destination
,
5794 const DirEntry
* source
,
5798 * The copy of the string occurs only when the flag is not set
5800 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5802 /* Use the filename for the root storage. */
5803 destination
->pwcsName
= 0;
5804 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5806 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5807 (source
->name
[0] == 0) )
5809 destination
->pwcsName
= 0;
5813 destination
->pwcsName
=
5814 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5816 strcpyW(destination
->pwcsName
, source
->name
);
5819 switch (source
->stgType
)
5823 destination
->type
= STGTY_STORAGE
;
5826 destination
->type
= STGTY_STREAM
;
5829 destination
->type
= STGTY_STREAM
;
5833 destination
->cbSize
= source
->size
;
5835 currentReturnStruct->mtime = {0}; TODO
5836 currentReturnStruct->ctime = {0};
5837 currentReturnStruct->atime = {0};
5839 destination
->grfMode
= 0;
5840 destination
->grfLocksSupported
= 0;
5841 destination
->clsid
= source
->clsid
;
5842 destination
->grfStateBits
= 0;
5843 destination
->reserved
= 0;
5846 /******************************************************************************
5847 ** BlockChainStream implementation
5850 /* Read and save the index of all blocks in this stream. */
5851 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5853 ULONG next_sector
, next_offset
;
5855 struct BlockChainRun
*last_run
;
5857 if (This
->indexCacheLen
== 0)
5861 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5865 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5866 next_offset
= last_run
->lastOffset
+1;
5867 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5868 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5870 if (FAILED(hr
)) return hr
;
5873 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5875 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5877 /* Add the current block to the cache. */
5878 if (This
->indexCacheSize
== 0)
5880 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5881 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5882 This
->indexCacheSize
= 16;
5884 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5886 struct BlockChainRun
*new_cache
;
5889 new_size
= This
->indexCacheSize
* 2;
5890 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5891 if (!new_cache
) return E_OUTOFMEMORY
;
5892 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5894 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5895 This
->indexCache
= new_cache
;
5896 This
->indexCacheSize
= new_size
;
5899 This
->indexCacheLen
++;
5900 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5901 last_run
->firstSector
= next_sector
;
5902 last_run
->firstOffset
= next_offset
;
5905 last_run
->lastOffset
= next_offset
;
5907 /* Find the next block. */
5909 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5910 if (FAILED(hr
)) return hr
;
5913 if (This
->indexCacheLen
)
5915 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5916 This
->numBlocks
= last_run
->lastOffset
+1;
5920 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5921 This
->numBlocks
= 0;
5927 /* Locate the nth block in this stream. */
5928 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5930 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5931 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5933 if (offset
>= This
->numBlocks
)
5934 return BLOCK_END_OF_CHAIN
;
5936 while (min_run
< max_run
)
5938 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5939 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5941 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5942 max_run
= run_to_check
-1;
5944 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5946 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5947 min_run
= run_to_check
+1;
5950 /* Block is in this run. */
5951 min_run
= max_run
= run_to_check
;
5954 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5957 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5958 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5960 BlockChainBlock
*result
=NULL
;
5964 if (This
->cachedBlocks
[i
].index
== index
)
5966 *sector
= This
->cachedBlocks
[i
].sector
;
5967 *block
= &This
->cachedBlocks
[i
];
5971 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5972 if (*sector
== BLOCK_END_OF_CHAIN
)
5973 return STG_E_DOCFILECORRUPT
;
5977 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5978 result
= &This
->cachedBlocks
[0];
5979 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5980 result
= &This
->cachedBlocks
[1];
5983 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5984 if (This
->blockToEvict
== 2)
5985 This
->blockToEvict
= 0;
5990 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5991 return STG_E_WRITEFAULT
;
5996 result
->index
= index
;
5997 result
->sector
= *sector
;
6004 BlockChainStream
* BlockChainStream_Construct(
6005 StorageImpl
* parentStorage
,
6006 ULONG
* headOfStreamPlaceHolder
,
6009 BlockChainStream
* newStream
;
6011 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6013 newStream
->parentStorage
= parentStorage
;
6014 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6015 newStream
->ownerDirEntry
= dirEntry
;
6016 newStream
->indexCache
= NULL
;
6017 newStream
->indexCacheLen
= 0;
6018 newStream
->indexCacheSize
= 0;
6019 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6020 newStream
->cachedBlocks
[0].dirty
= 0;
6021 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6022 newStream
->cachedBlocks
[1].dirty
= 0;
6023 newStream
->blockToEvict
= 0;
6025 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6027 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6028 HeapFree(GetProcessHeap(), 0, newStream
);
6035 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6038 if (!This
) return S_OK
;
6041 if (This
->cachedBlocks
[i
].dirty
)
6043 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6044 This
->cachedBlocks
[i
].dirty
= 0;
6046 return STG_E_WRITEFAULT
;
6052 void BlockChainStream_Destroy(BlockChainStream
* This
)
6056 BlockChainStream_Flush(This
);
6057 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6059 HeapFree(GetProcessHeap(), 0, This
);
6062 /******************************************************************************
6063 * BlockChainStream_GetHeadOfChain
6065 * Returns the head of this stream chain.
6066 * Some special chains don't have directory entries, their heads are kept in
6067 * This->headOfStreamPlaceHolder.
6070 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6072 DirEntry chainEntry
;
6075 if (This
->headOfStreamPlaceHolder
!= 0)
6076 return *(This
->headOfStreamPlaceHolder
);
6078 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6080 hr
= StorageImpl_ReadDirEntry(
6081 This
->parentStorage
,
6082 This
->ownerDirEntry
,
6087 return chainEntry
.startingBlock
;
6091 return BLOCK_END_OF_CHAIN
;
6094 /******************************************************************************
6095 * BlockChainStream_GetCount
6097 * Returns the number of blocks that comprises this chain.
6098 * This is not the size of the stream as the last block may not be full!
6100 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6102 return This
->numBlocks
;
6105 /******************************************************************************
6106 * BlockChainStream_ReadAt
6108 * Reads a specified number of bytes from this chain at the specified offset.
6109 * bytesRead may be NULL.
6110 * Failure will be returned if the specified number of bytes has not been read.
6112 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6113 ULARGE_INTEGER offset
,
6118 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6119 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6120 ULONG bytesToReadInBuffer
;
6123 ULARGE_INTEGER stream_size
;
6125 BlockChainBlock
*cachedBlock
;
6127 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6130 * Find the first block in the stream that contains part of the buffer.
6132 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6136 stream_size
= BlockChainStream_GetSize(This
);
6137 if (stream_size
.QuadPart
> offset
.QuadPart
)
6138 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6143 * Start reading the buffer.
6145 bufferWalker
= buffer
;
6149 ULARGE_INTEGER ulOffset
;
6153 * Calculate how many bytes we can copy from this big block.
6155 bytesToReadInBuffer
=
6156 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6158 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6165 /* Not in cache, and we're going to read past the end of the block. */
6166 ulOffset
.u
.HighPart
= 0;
6167 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6170 StorageImpl_ReadAt(This
->parentStorage
,
6173 bytesToReadInBuffer
,
6178 if (!cachedBlock
->read
)
6180 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6181 return STG_E_READFAULT
;
6183 cachedBlock
->read
= 1;
6186 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6187 bytesReadAt
= bytesToReadInBuffer
;
6190 blockNoInSequence
++;
6191 bufferWalker
+= bytesReadAt
;
6192 size
-= bytesReadAt
;
6193 *bytesRead
+= bytesReadAt
;
6194 offsetInBlock
= 0; /* There is no offset on the next block */
6196 if (bytesToReadInBuffer
!= bytesReadAt
)
6203 /******************************************************************************
6204 * BlockChainStream_WriteAt
6206 * Writes the specified number of bytes to this chain at the specified offset.
6207 * Will fail if not all specified number of bytes have been written.
6209 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6210 ULARGE_INTEGER offset
,
6213 ULONG
* bytesWritten
)
6215 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6216 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6219 const BYTE
* bufferWalker
;
6221 BlockChainBlock
*cachedBlock
;
6224 bufferWalker
= buffer
;
6228 ULARGE_INTEGER ulOffset
;
6229 DWORD bytesWrittenAt
;
6232 * Calculate how many bytes we can copy to this big block.
6235 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6237 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6239 /* BlockChainStream_SetSize should have already been called to ensure we have
6240 * enough blocks in the chain to write into */
6243 ERR("not enough blocks in chain to write data\n");
6249 /* Not in cache, and we're going to write past the end of the block. */
6250 ulOffset
.u
.HighPart
= 0;
6251 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6254 StorageImpl_WriteAt(This
->parentStorage
,
6262 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6264 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6265 return STG_E_READFAULT
;
6268 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6269 bytesWrittenAt
= bytesToWrite
;
6270 cachedBlock
->read
= 1;
6271 cachedBlock
->dirty
= 1;
6274 blockNoInSequence
++;
6275 bufferWalker
+= bytesWrittenAt
;
6276 size
-= bytesWrittenAt
;
6277 *bytesWritten
+= bytesWrittenAt
;
6278 offsetInBlock
= 0; /* There is no offset on the next block */
6280 if (bytesWrittenAt
!= bytesToWrite
)
6284 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6287 /******************************************************************************
6288 * BlockChainStream_Shrink
6290 * Shrinks this chain in the big block depot.
6292 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6293 ULARGE_INTEGER newSize
)
6300 * Figure out how many blocks are needed to contain the new size
6302 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6304 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6310 * Go to the new end of chain
6312 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6314 /* Mark the new end of chain */
6315 StorageImpl_SetNextBlockInChain(
6316 This
->parentStorage
,
6318 BLOCK_END_OF_CHAIN
);
6320 This
->tailIndex
= blockIndex
;
6324 if (This
->headOfStreamPlaceHolder
!= 0)
6326 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6330 DirEntry chainEntry
;
6331 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6333 StorageImpl_ReadDirEntry(
6334 This
->parentStorage
,
6335 This
->ownerDirEntry
,
6338 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6340 StorageImpl_WriteDirEntry(
6341 This
->parentStorage
,
6342 This
->ownerDirEntry
,
6346 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6349 This
->numBlocks
= numBlocks
;
6352 * Mark the extra blocks as free
6354 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6356 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6357 StorageImpl_FreeBigBlock(This
->parentStorage
,
6358 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6359 if (last_run
->lastOffset
== last_run
->firstOffset
)
6360 This
->indexCacheLen
--;
6362 last_run
->lastOffset
--;
6366 * Reset the last accessed block cache.
6370 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6372 This
->cachedBlocks
[i
].index
= 0xffffffff;
6373 This
->cachedBlocks
[i
].dirty
= 0;
6380 /******************************************************************************
6381 * BlockChainStream_Enlarge
6383 * Grows this chain in the big block depot.
6385 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6386 ULARGE_INTEGER newSize
)
6388 ULONG blockIndex
, currentBlock
;
6390 ULONG oldNumBlocks
= 0;
6392 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6395 * Empty chain. Create the head.
6397 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6399 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6400 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6402 BLOCK_END_OF_CHAIN
);
6404 if (This
->headOfStreamPlaceHolder
!= 0)
6406 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6410 DirEntry chainEntry
;
6411 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6413 StorageImpl_ReadDirEntry(
6414 This
->parentStorage
,
6415 This
->ownerDirEntry
,
6418 chainEntry
.startingBlock
= blockIndex
;
6420 StorageImpl_WriteDirEntry(
6421 This
->parentStorage
,
6422 This
->ownerDirEntry
,
6426 This
->tailIndex
= blockIndex
;
6427 This
->numBlocks
= 1;
6431 * Figure out how many blocks are needed to contain this stream
6433 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6435 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6439 * Go to the current end of chain
6441 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6443 currentBlock
= blockIndex
;
6445 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6448 currentBlock
= blockIndex
;
6450 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6455 This
->tailIndex
= currentBlock
;
6458 currentBlock
= This
->tailIndex
;
6459 oldNumBlocks
= This
->numBlocks
;
6462 * Add new blocks to the chain
6464 if (oldNumBlocks
< newNumBlocks
)
6466 while (oldNumBlocks
< newNumBlocks
)
6468 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6470 StorageImpl_SetNextBlockInChain(
6471 This
->parentStorage
,
6475 StorageImpl_SetNextBlockInChain(
6476 This
->parentStorage
,
6478 BLOCK_END_OF_CHAIN
);
6480 currentBlock
= blockIndex
;
6484 This
->tailIndex
= blockIndex
;
6485 This
->numBlocks
= newNumBlocks
;
6488 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6494 /******************************************************************************
6495 * BlockChainStream_SetSize
6497 * Sets the size of this stream. The big block depot will be updated.
6498 * The file will grow if we grow the chain.
6500 * TODO: Free the actual blocks in the file when we shrink the chain.
6501 * Currently, the blocks are still in the file. So the file size
6502 * doesn't shrink even if we shrink streams.
6504 BOOL
BlockChainStream_SetSize(
6505 BlockChainStream
* This
,
6506 ULARGE_INTEGER newSize
)
6508 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6510 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6513 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6515 BlockChainStream_Shrink(This
, newSize
);
6519 BlockChainStream_Enlarge(This
, newSize
);
6525 /******************************************************************************
6526 * BlockChainStream_GetSize
6528 * Returns the size of this chain.
6529 * Will return the block count if this chain doesn't have a directory entry.
6531 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6533 DirEntry chainEntry
;
6535 if(This
->headOfStreamPlaceHolder
== NULL
)
6538 * This chain has a directory entry so use the size value from there.
6540 StorageImpl_ReadDirEntry(
6541 This
->parentStorage
,
6542 This
->ownerDirEntry
,
6545 return chainEntry
.size
;
6550 * this chain is a chain that does not have a directory entry, figure out the
6551 * size by making the product number of used blocks times the
6554 ULARGE_INTEGER result
;
6555 result
.u
.HighPart
= 0;
6558 BlockChainStream_GetCount(This
) *
6559 This
->parentStorage
->bigBlockSize
;
6565 /******************************************************************************
6566 ** SmallBlockChainStream implementation
6569 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6570 StorageImpl
* parentStorage
,
6571 ULONG
* headOfStreamPlaceHolder
,
6574 SmallBlockChainStream
* newStream
;
6576 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6578 newStream
->parentStorage
= parentStorage
;
6579 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6580 newStream
->ownerDirEntry
= dirEntry
;
6585 void SmallBlockChainStream_Destroy(
6586 SmallBlockChainStream
* This
)
6588 HeapFree(GetProcessHeap(), 0, This
);
6591 /******************************************************************************
6592 * SmallBlockChainStream_GetHeadOfChain
6594 * Returns the head of this chain of small blocks.
6596 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6597 SmallBlockChainStream
* This
)
6599 DirEntry chainEntry
;
6602 if (This
->headOfStreamPlaceHolder
!= NULL
)
6603 return *(This
->headOfStreamPlaceHolder
);
6605 if (This
->ownerDirEntry
)
6607 hr
= StorageImpl_ReadDirEntry(
6608 This
->parentStorage
,
6609 This
->ownerDirEntry
,
6614 return chainEntry
.startingBlock
;
6619 return BLOCK_END_OF_CHAIN
;
6622 /******************************************************************************
6623 * SmallBlockChainStream_GetNextBlockInChain
6625 * Returns the index of the next small block in this chain.
6628 * - BLOCK_END_OF_CHAIN: end of this chain
6629 * - BLOCK_UNUSED: small block 'blockIndex' is free
6631 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6632 SmallBlockChainStream
* This
,
6634 ULONG
* nextBlockInChain
)
6636 ULARGE_INTEGER offsetOfBlockInDepot
;
6641 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6643 offsetOfBlockInDepot
.u
.HighPart
= 0;
6644 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6647 * Read those bytes in the buffer from the small block file.
6649 res
= BlockChainStream_ReadAt(
6650 This
->parentStorage
->smallBlockDepotChain
,
6651 offsetOfBlockInDepot
,
6656 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6657 res
= STG_E_READFAULT
;
6661 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6668 /******************************************************************************
6669 * SmallBlockChainStream_SetNextBlockInChain
6671 * Writes the index of the next block of the specified block in the small
6673 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6674 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6676 static void SmallBlockChainStream_SetNextBlockInChain(
6677 SmallBlockChainStream
* This
,
6681 ULARGE_INTEGER offsetOfBlockInDepot
;
6685 offsetOfBlockInDepot
.u
.HighPart
= 0;
6686 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6688 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6691 * Read those bytes in the buffer from the small block file.
6693 BlockChainStream_WriteAt(
6694 This
->parentStorage
->smallBlockDepotChain
,
6695 offsetOfBlockInDepot
,
6701 /******************************************************************************
6702 * SmallBlockChainStream_FreeBlock
6704 * Flag small block 'blockIndex' as free in the small block depot.
6706 static void SmallBlockChainStream_FreeBlock(
6707 SmallBlockChainStream
* This
,
6710 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6713 /******************************************************************************
6714 * SmallBlockChainStream_GetNextFreeBlock
6716 * Returns the index of a free small block. The small block depot will be
6717 * enlarged if necessary. The small block chain will also be enlarged if
6720 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6721 SmallBlockChainStream
* This
)
6723 ULARGE_INTEGER offsetOfBlockInDepot
;
6726 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6727 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6729 ULONG smallBlocksPerBigBlock
;
6731 ULONG blocksRequired
;
6732 ULARGE_INTEGER old_size
, size_required
;
6734 offsetOfBlockInDepot
.u
.HighPart
= 0;
6737 * Scan the small block depot for a free block
6739 while (nextBlockIndex
!= BLOCK_UNUSED
)
6741 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6743 res
= BlockChainStream_ReadAt(
6744 This
->parentStorage
->smallBlockDepotChain
,
6745 offsetOfBlockInDepot
,
6751 * If we run out of space for the small block depot, enlarge it
6753 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6755 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6757 if (nextBlockIndex
!= BLOCK_UNUSED
)
6763 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6765 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6766 ULARGE_INTEGER newSize
, offset
;
6769 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6770 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6773 * Initialize all the small blocks to free
6775 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6776 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6777 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6778 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6780 StorageImpl_SaveFileHeader(This
->parentStorage
);
6784 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6786 smallBlocksPerBigBlock
=
6787 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6790 * Verify if we have to allocate big blocks to contain small blocks
6792 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6794 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6796 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6798 if (size_required
.QuadPart
> old_size
.QuadPart
)
6800 BlockChainStream_SetSize(
6801 This
->parentStorage
->smallBlockRootChain
,
6804 StorageImpl_ReadDirEntry(
6805 This
->parentStorage
,
6806 This
->parentStorage
->base
.storageDirEntry
,
6809 rootEntry
.size
= size_required
;
6811 StorageImpl_WriteDirEntry(
6812 This
->parentStorage
,
6813 This
->parentStorage
->base
.storageDirEntry
,
6820 /******************************************************************************
6821 * SmallBlockChainStream_ReadAt
6823 * Reads a specified number of bytes from this chain at the specified offset.
6824 * bytesRead may be NULL.
6825 * Failure will be returned if the specified number of bytes has not been read.
6827 HRESULT
SmallBlockChainStream_ReadAt(
6828 SmallBlockChainStream
* This
,
6829 ULARGE_INTEGER offset
,
6835 ULARGE_INTEGER offsetInBigBlockFile
;
6836 ULONG blockNoInSequence
=
6837 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6839 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6840 ULONG bytesToReadInBuffer
;
6842 ULONG bytesReadFromBigBlockFile
;
6844 ULARGE_INTEGER stream_size
;
6847 * This should never happen on a small block file.
6849 assert(offset
.u
.HighPart
==0);
6853 stream_size
= SmallBlockChainStream_GetSize(This
);
6854 if (stream_size
.QuadPart
> offset
.QuadPart
)
6855 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6860 * Find the first block in the stream that contains part of the buffer.
6862 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6864 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6866 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6869 blockNoInSequence
--;
6873 * Start reading the buffer.
6875 bufferWalker
= buffer
;
6877 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6880 * Calculate how many bytes we can copy from this small block.
6882 bytesToReadInBuffer
=
6883 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6886 * Calculate the offset of the small block in the small block file.
6888 offsetInBigBlockFile
.u
.HighPart
= 0;
6889 offsetInBigBlockFile
.u
.LowPart
=
6890 blockIndex
* This
->parentStorage
->smallBlockSize
;
6892 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6895 * Read those bytes in the buffer from the small block file.
6896 * The small block has already been identified so it shouldn't fail
6897 * unless the file is corrupt.
6899 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6900 offsetInBigBlockFile
,
6901 bytesToReadInBuffer
,
6903 &bytesReadFromBigBlockFile
);
6908 if (!bytesReadFromBigBlockFile
)
6909 return STG_E_DOCFILECORRUPT
;
6912 * Step to the next big block.
6914 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6916 return STG_E_DOCFILECORRUPT
;
6918 bufferWalker
+= bytesReadFromBigBlockFile
;
6919 size
-= bytesReadFromBigBlockFile
;
6920 *bytesRead
+= bytesReadFromBigBlockFile
;
6921 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6927 /******************************************************************************
6928 * SmallBlockChainStream_WriteAt
6930 * Writes the specified number of bytes to this chain at the specified offset.
6931 * Will fail if not all specified number of bytes have been written.
6933 HRESULT
SmallBlockChainStream_WriteAt(
6934 SmallBlockChainStream
* This
,
6935 ULARGE_INTEGER offset
,
6938 ULONG
* bytesWritten
)
6940 ULARGE_INTEGER offsetInBigBlockFile
;
6941 ULONG blockNoInSequence
=
6942 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6944 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6945 ULONG bytesToWriteInBuffer
;
6947 ULONG bytesWrittenToBigBlockFile
;
6948 const BYTE
* bufferWalker
;
6952 * This should never happen on a small block file.
6954 assert(offset
.u
.HighPart
==0);
6957 * Find the first block in the stream that contains part of the buffer.
6959 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6961 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6963 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6964 return STG_E_DOCFILECORRUPT
;
6965 blockNoInSequence
--;
6969 * Start writing the buffer.
6972 bufferWalker
= buffer
;
6973 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6976 * Calculate how many bytes we can copy to this small block.
6978 bytesToWriteInBuffer
=
6979 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6982 * Calculate the offset of the small block in the small block file.
6984 offsetInBigBlockFile
.u
.HighPart
= 0;
6985 offsetInBigBlockFile
.u
.LowPart
=
6986 blockIndex
* This
->parentStorage
->smallBlockSize
;
6988 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6991 * Write those bytes in the buffer to the small block file.
6993 res
= BlockChainStream_WriteAt(
6994 This
->parentStorage
->smallBlockRootChain
,
6995 offsetInBigBlockFile
,
6996 bytesToWriteInBuffer
,
6998 &bytesWrittenToBigBlockFile
);
7003 * Step to the next big block.
7005 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7008 bufferWalker
+= bytesWrittenToBigBlockFile
;
7009 size
-= bytesWrittenToBigBlockFile
;
7010 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7011 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7014 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7017 /******************************************************************************
7018 * SmallBlockChainStream_Shrink
7020 * Shrinks this chain in the small block depot.
7022 static BOOL
SmallBlockChainStream_Shrink(
7023 SmallBlockChainStream
* This
,
7024 ULARGE_INTEGER newSize
)
7026 ULONG blockIndex
, extraBlock
;
7030 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7032 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7035 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7038 * Go to the new end of chain
7040 while (count
< numBlocks
)
7042 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7049 * If the count is 0, we have a special case, the head of the chain was
7054 DirEntry chainEntry
;
7056 StorageImpl_ReadDirEntry(This
->parentStorage
,
7057 This
->ownerDirEntry
,
7060 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7062 StorageImpl_WriteDirEntry(This
->parentStorage
,
7063 This
->ownerDirEntry
,
7067 * We start freeing the chain at the head block.
7069 extraBlock
= blockIndex
;
7073 /* Get the next block before marking the new end */
7074 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7078 /* Mark the new end of chain */
7079 SmallBlockChainStream_SetNextBlockInChain(
7082 BLOCK_END_OF_CHAIN
);
7086 * Mark the extra blocks as free
7088 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7090 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7093 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7094 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7095 extraBlock
= blockIndex
;
7101 /******************************************************************************
7102 * SmallBlockChainStream_Enlarge
7104 * Grows this chain in the small block depot.
7106 static BOOL
SmallBlockChainStream_Enlarge(
7107 SmallBlockChainStream
* This
,
7108 ULARGE_INTEGER newSize
)
7110 ULONG blockIndex
, currentBlock
;
7112 ULONG oldNumBlocks
= 0;
7114 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7117 * Empty chain. Create the head.
7119 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7121 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7122 SmallBlockChainStream_SetNextBlockInChain(
7125 BLOCK_END_OF_CHAIN
);
7127 if (This
->headOfStreamPlaceHolder
!= NULL
)
7129 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7133 DirEntry chainEntry
;
7135 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7138 chainEntry
.startingBlock
= blockIndex
;
7140 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7145 currentBlock
= blockIndex
;
7148 * Figure out how many blocks are needed to contain this stream
7150 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7152 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7156 * Go to the current end of chain
7158 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7161 currentBlock
= blockIndex
;
7162 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7167 * Add new blocks to the chain
7169 while (oldNumBlocks
< newNumBlocks
)
7171 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7172 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7174 SmallBlockChainStream_SetNextBlockInChain(
7177 BLOCK_END_OF_CHAIN
);
7179 currentBlock
= blockIndex
;
7186 /******************************************************************************
7187 * SmallBlockChainStream_SetSize
7189 * Sets the size of this stream.
7190 * The file will grow if we grow the chain.
7192 * TODO: Free the actual blocks in the file when we shrink the chain.
7193 * Currently, the blocks are still in the file. So the file size
7194 * doesn't shrink even if we shrink streams.
7196 BOOL
SmallBlockChainStream_SetSize(
7197 SmallBlockChainStream
* This
,
7198 ULARGE_INTEGER newSize
)
7200 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7202 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7205 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7207 SmallBlockChainStream_Shrink(This
, newSize
);
7211 SmallBlockChainStream_Enlarge(This
, newSize
);
7217 /******************************************************************************
7218 * SmallBlockChainStream_GetCount
7220 * Returns the number of small blocks that comprises this chain.
7221 * This is not the size of the stream as the last block may not be full!
7224 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7229 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7231 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7235 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7236 blockIndex
, &blockIndex
)))
7243 /******************************************************************************
7244 * SmallBlockChainStream_GetSize
7246 * Returns the size of this chain.
7248 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7250 DirEntry chainEntry
;
7252 if(This
->headOfStreamPlaceHolder
!= NULL
)
7254 ULARGE_INTEGER result
;
7255 result
.u
.HighPart
= 0;
7257 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7258 This
->parentStorage
->smallBlockSize
;
7263 StorageImpl_ReadDirEntry(
7264 This
->parentStorage
,
7265 This
->ownerDirEntry
,
7268 return chainEntry
.size
;
7271 static HRESULT
create_storagefile(
7275 STGOPTIONS
* pStgOptions
,
7279 StorageBaseImpl
* newStorage
= 0;
7280 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7281 HRESULT hr
= STG_E_INVALIDFLAG
;
7285 DWORD fileAttributes
;
7286 WCHAR tempFileName
[MAX_PATH
];
7289 return STG_E_INVALIDPOINTER
;
7291 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7292 return STG_E_INVALIDPARAMETER
;
7294 /* if no share mode given then DENY_NONE is the default */
7295 if (STGM_SHARE_MODE(grfMode
) == 0)
7296 grfMode
|= STGM_SHARE_DENY_NONE
;
7298 if ( FAILED( validateSTGM(grfMode
) ))
7301 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7302 switch(STGM_ACCESS_MODE(grfMode
))
7305 case STGM_READWRITE
:
7311 /* in direct mode, can only use SHARE_EXCLUSIVE */
7312 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7315 /* but in transacted mode, any share mode is valid */
7318 * Generate a unique name.
7322 WCHAR tempPath
[MAX_PATH
];
7323 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7325 memset(tempPath
, 0, sizeof(tempPath
));
7326 memset(tempFileName
, 0, sizeof(tempFileName
));
7328 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7331 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7332 pwcsName
= tempFileName
;
7335 hr
= STG_E_INSUFFICIENTMEMORY
;
7339 creationMode
= TRUNCATE_EXISTING
;
7343 creationMode
= GetCreationModeFromSTGM(grfMode
);
7347 * Interpret the STGM value grfMode
7349 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7350 accessMode
= GetAccessModeFromSTGM(grfMode
);
7352 if (grfMode
& STGM_DELETEONRELEASE
)
7353 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7355 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7357 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7361 FIXME("Storage share mode not implemented.\n");
7366 hFile
= CreateFileW(pwcsName
,
7374 if (hFile
== INVALID_HANDLE_VALUE
)
7376 if(GetLastError() == ERROR_FILE_EXISTS
)
7377 hr
= STG_E_FILEALREADYEXISTS
;
7384 * Allocate and initialize the new IStorage32object.
7386 hr
= Storage_Construct(
7393 pStgOptions
->ulSectorSize
,
7401 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
7402 IStorage_Release(&newStorage
->IStorage_iface
);
7405 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7410 /******************************************************************************
7411 * StgCreateDocfile [OLE32.@]
7412 * Creates a new compound file storage object
7415 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7416 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7417 * reserved [ ?] unused?, usually 0
7418 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7421 * S_OK if the file was successfully created
7422 * some STG_E_ value if error
7424 * if pwcsName is NULL, create file with new unique name
7425 * the function can returns
7426 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7429 HRESULT WINAPI
StgCreateDocfile(
7433 IStorage
**ppstgOpen
)
7435 STGOPTIONS stgoptions
= {1, 0, 512};
7437 TRACE("(%s, %x, %d, %p)\n",
7438 debugstr_w(pwcsName
), grfMode
,
7439 reserved
, ppstgOpen
);
7442 return STG_E_INVALIDPOINTER
;
7444 return STG_E_INVALIDPARAMETER
;
7446 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7449 /******************************************************************************
7450 * StgCreateStorageEx [OLE32.@]
7452 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7454 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7455 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7457 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7459 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7460 return STG_E_INVALIDPARAMETER
;
7463 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7465 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7466 return STG_E_INVALIDPARAMETER
;
7469 if (stgfmt
== STGFMT_FILE
)
7471 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7472 return STG_E_INVALIDPARAMETER
;
7475 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7477 STGOPTIONS defaultOptions
= {1, 0, 512};
7479 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7480 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7484 ERR("Invalid stgfmt argument\n");
7485 return STG_E_INVALIDPARAMETER
;
7488 /******************************************************************************
7489 * StgCreatePropSetStg [OLE32.@]
7491 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7492 IPropertySetStorage
**propset
)
7494 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
7496 return STG_E_INVALIDPARAMETER
;
7498 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
7501 /******************************************************************************
7502 * StgOpenStorageEx [OLE32.@]
7504 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7506 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7507 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7509 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7511 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7512 return STG_E_INVALIDPARAMETER
;
7518 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7519 return STG_E_INVALIDPARAMETER
;
7521 case STGFMT_STORAGE
:
7524 case STGFMT_DOCFILE
:
7525 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7527 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7528 return STG_E_INVALIDPARAMETER
;
7530 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7534 WARN("STGFMT_ANY assuming storage\n");
7538 return STG_E_INVALIDPARAMETER
;
7541 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7545 /******************************************************************************
7546 * StgOpenStorage [OLE32.@]
7548 HRESULT WINAPI
StgOpenStorage(
7549 const OLECHAR
*pwcsName
,
7550 IStorage
*pstgPriority
,
7554 IStorage
**ppstgOpen
)
7556 StorageBaseImpl
* newStorage
= 0;
7562 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7563 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7564 snbExclude
, reserved
, ppstgOpen
);
7568 hr
= STG_E_INVALIDNAME
;
7574 hr
= STG_E_INVALIDPOINTER
;
7580 hr
= STG_E_INVALIDPARAMETER
;
7584 if (grfMode
& STGM_PRIORITY
)
7586 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7587 return STG_E_INVALIDFLAG
;
7588 if (grfMode
& STGM_DELETEONRELEASE
)
7589 return STG_E_INVALIDFUNCTION
;
7590 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7591 return STG_E_INVALIDFLAG
;
7592 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7593 grfMode
|= STGM_SHARE_DENY_NONE
;
7595 /* STGM_PRIORITY stops other IStorage objects on the same file from
7596 * committing until the STGM_PRIORITY IStorage is closed. it also
7597 * stops non-transacted mode StgOpenStorage calls with write access from
7598 * succeeding. obviously, both of these cannot be achieved through just
7599 * file share flags */
7600 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7604 * Validate the sharing mode
7606 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7607 switch(STGM_SHARE_MODE(grfMode
))
7609 case STGM_SHARE_EXCLUSIVE
:
7610 case STGM_SHARE_DENY_WRITE
:
7613 hr
= STG_E_INVALIDFLAG
;
7617 if ( FAILED( validateSTGM(grfMode
) ) ||
7618 (grfMode
&STGM_CREATE
))
7620 hr
= STG_E_INVALIDFLAG
;
7624 /* shared reading requires transacted mode */
7625 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7626 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7627 !(grfMode
&STGM_TRANSACTED
) )
7629 hr
= STG_E_INVALIDFLAG
;
7634 * Interpret the STGM value grfMode
7636 shareMode
= GetShareModeFromSTGM(grfMode
);
7637 accessMode
= GetAccessModeFromSTGM(grfMode
);
7641 hFile
= CreateFileW( pwcsName
,
7646 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7649 if (hFile
==INVALID_HANDLE_VALUE
)
7651 DWORD last_error
= GetLastError();
7657 case ERROR_FILE_NOT_FOUND
:
7658 hr
= STG_E_FILENOTFOUND
;
7661 case ERROR_PATH_NOT_FOUND
:
7662 hr
= STG_E_PATHNOTFOUND
;
7665 case ERROR_ACCESS_DENIED
:
7666 case ERROR_WRITE_PROTECT
:
7667 hr
= STG_E_ACCESSDENIED
;
7670 case ERROR_SHARING_VIOLATION
:
7671 hr
= STG_E_SHAREVIOLATION
;
7682 * Refuse to open the file if it's too small to be a structured storage file
7683 * FIXME: verify the file when reading instead of here
7685 if (GetFileSize(hFile
, NULL
) < 0x100)
7688 hr
= STG_E_FILEALREADYEXISTS
;
7693 * Allocate and initialize the new IStorage32object.
7695 hr
= Storage_Construct(
7708 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7710 if(hr
== STG_E_INVALIDHEADER
)
7711 hr
= STG_E_FILEALREADYEXISTS
;
7715 *ppstgOpen
= &newStorage
->IStorage_iface
;
7718 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7722 /******************************************************************************
7723 * StgCreateDocfileOnILockBytes [OLE32.@]
7725 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7729 IStorage
** ppstgOpen
)
7731 StorageBaseImpl
* newStorage
= 0;
7734 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7735 return STG_E_INVALIDPOINTER
;
7738 * Allocate and initialize the new IStorage object.
7740 hr
= Storage_Construct(
7755 *ppstgOpen
= &newStorage
->IStorage_iface
;
7760 /******************************************************************************
7761 * StgOpenStorageOnILockBytes [OLE32.@]
7763 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7765 IStorage
*pstgPriority
,
7769 IStorage
**ppstgOpen
)
7771 StorageBaseImpl
* newStorage
= 0;
7774 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7775 return STG_E_INVALIDPOINTER
;
7777 if ( FAILED( validateSTGM(grfMode
) ))
7778 return STG_E_INVALIDFLAG
;
7783 * Allocate and initialize the new IStorage object.
7785 hr
= Storage_Construct(
7800 *ppstgOpen
= &newStorage
->IStorage_iface
;
7805 /******************************************************************************
7806 * StgSetTimes [ole32.@]
7807 * StgSetTimes [OLE32.@]
7811 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7812 FILETIME
const *patime
, FILETIME
const *pmtime
)
7814 IStorage
*stg
= NULL
;
7817 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7819 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7823 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7824 IStorage_Release(stg
);
7830 /******************************************************************************
7831 * StgIsStorageILockBytes [OLE32.@]
7833 * Determines if the ILockBytes contains a storage object.
7835 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7837 BYTE sig
[sizeof(STORAGE_magic
)];
7838 ULARGE_INTEGER offset
;
7841 offset
.u
.HighPart
= 0;
7842 offset
.u
.LowPart
= 0;
7844 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7846 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7852 /******************************************************************************
7853 * WriteClassStg [OLE32.@]
7855 * This method will store the specified CLSID in the specified storage object
7857 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7860 return E_INVALIDARG
;
7863 return STG_E_INVALIDPOINTER
;
7865 return IStorage_SetClass(pStg
, rclsid
);
7868 /***********************************************************************
7869 * ReadClassStg (OLE32.@)
7871 * This method reads the CLSID previously written to a storage object with
7872 * the WriteClassStg.
7875 * pstg [I] IStorage pointer
7876 * pclsid [O] Pointer to where the CLSID is written
7880 * Failure: HRESULT code.
7882 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7887 TRACE("(%p, %p)\n", pstg
, pclsid
);
7889 if(!pstg
|| !pclsid
)
7890 return E_INVALIDARG
;
7893 * read a STATSTG structure (contains the clsid) from the storage
7895 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7898 *pclsid
=pstatstg
.clsid
;
7903 /***********************************************************************
7904 * OleLoadFromStream (OLE32.@)
7906 * This function loads an object from stream
7908 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7912 LPPERSISTSTREAM xstm
;
7914 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7916 res
=ReadClassStm(pStm
,&clsid
);
7919 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7922 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7924 IUnknown_Release((IUnknown
*)*ppvObj
);
7927 res
=IPersistStream_Load(xstm
,pStm
);
7928 IPersistStream_Release(xstm
);
7929 /* FIXME: all refcounts ok at this point? I think they should be:
7932 * xstm : 0 (released)
7937 /***********************************************************************
7938 * OleSaveToStream (OLE32.@)
7940 * This function saves an object with the IPersistStream interface on it
7941 * to the specified stream.
7943 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7949 TRACE("(%p,%p)\n",pPStm
,pStm
);
7951 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7953 if (SUCCEEDED(res
)){
7955 res
=WriteClassStm(pStm
,&clsid
);
7959 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7962 TRACE("Finished Save\n");
7966 /****************************************************************************
7967 * This method validate a STGM parameter that can contain the values below
7969 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7970 * The stgm values contained in 0xffff0000 are bitmasks.
7972 * STGM_DIRECT 0x00000000
7973 * STGM_TRANSACTED 0x00010000
7974 * STGM_SIMPLE 0x08000000
7976 * STGM_READ 0x00000000
7977 * STGM_WRITE 0x00000001
7978 * STGM_READWRITE 0x00000002
7980 * STGM_SHARE_DENY_NONE 0x00000040
7981 * STGM_SHARE_DENY_READ 0x00000030
7982 * STGM_SHARE_DENY_WRITE 0x00000020
7983 * STGM_SHARE_EXCLUSIVE 0x00000010
7985 * STGM_PRIORITY 0x00040000
7986 * STGM_DELETEONRELEASE 0x04000000
7988 * STGM_CREATE 0x00001000
7989 * STGM_CONVERT 0x00020000
7990 * STGM_FAILIFTHERE 0x00000000
7992 * STGM_NOSCRATCH 0x00100000
7993 * STGM_NOSNAPSHOT 0x00200000
7995 static HRESULT
validateSTGM(DWORD stgm
)
7997 DWORD access
= STGM_ACCESS_MODE(stgm
);
7998 DWORD share
= STGM_SHARE_MODE(stgm
);
7999 DWORD create
= STGM_CREATE_MODE(stgm
);
8001 if (stgm
&~STGM_KNOWN_FLAGS
)
8003 ERR("unknown flags %08x\n", stgm
);
8011 case STGM_READWRITE
:
8019 case STGM_SHARE_DENY_NONE
:
8020 case STGM_SHARE_DENY_READ
:
8021 case STGM_SHARE_DENY_WRITE
:
8022 case STGM_SHARE_EXCLUSIVE
:
8031 case STGM_FAILIFTHERE
:
8038 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8040 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8044 * STGM_CREATE | STGM_CONVERT
8045 * if both are false, STGM_FAILIFTHERE is set to TRUE
8047 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8051 * STGM_NOSCRATCH requires STGM_TRANSACTED
8053 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8057 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8058 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8060 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8061 (!(stgm
& STGM_TRANSACTED
) ||
8062 share
== STGM_SHARE_EXCLUSIVE
||
8063 share
== STGM_SHARE_DENY_WRITE
) )
8069 /****************************************************************************
8070 * GetShareModeFromSTGM
8072 * This method will return a share mode flag from a STGM value.
8073 * The STGM value is assumed valid.
8075 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8077 switch (STGM_SHARE_MODE(stgm
))
8079 case STGM_SHARE_DENY_NONE
:
8080 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8081 case STGM_SHARE_DENY_READ
:
8082 return FILE_SHARE_WRITE
;
8083 case STGM_SHARE_DENY_WRITE
:
8084 return FILE_SHARE_READ
;
8085 case STGM_SHARE_EXCLUSIVE
:
8088 ERR("Invalid share mode!\n");
8093 /****************************************************************************
8094 * GetAccessModeFromSTGM
8096 * This method will return an access mode flag from a STGM value.
8097 * The STGM value is assumed valid.
8099 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8101 switch (STGM_ACCESS_MODE(stgm
))
8104 return GENERIC_READ
;
8106 case STGM_READWRITE
:
8107 return GENERIC_READ
| GENERIC_WRITE
;
8109 ERR("Invalid access mode!\n");
8114 /****************************************************************************
8115 * GetCreationModeFromSTGM
8117 * This method will return a creation mode flag from a STGM value.
8118 * The STGM value is assumed valid.
8120 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8122 switch(STGM_CREATE_MODE(stgm
))
8125 return CREATE_ALWAYS
;
8127 FIXME("STGM_CONVERT not implemented!\n");
8129 case STGM_FAILIFTHERE
:
8132 ERR("Invalid create mode!\n");
8138 /*************************************************************************
8139 * OLECONVERT_LoadOLE10 [Internal]
8141 * Loads the OLE10 STREAM to memory
8144 * pOleStream [I] The OLESTREAM
8145 * pData [I] Data Structure for the OLESTREAM Data
8149 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8150 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8153 * This function is used by OleConvertOLESTREAMToIStorage only.
8155 * Memory allocated for pData must be freed by the caller
8157 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8160 HRESULT hRes
= S_OK
;
8164 pData
->pData
= NULL
;
8165 pData
->pstrOleObjFileName
= NULL
;
8167 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8170 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8171 if(dwSize
!= sizeof(pData
->dwOleID
))
8173 hRes
= CONVERT10_E_OLESTREAM_GET
;
8175 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8177 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8188 /* Get the TypeID... more info needed for this field */
8189 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8190 if(dwSize
!= sizeof(pData
->dwTypeID
))
8192 hRes
= CONVERT10_E_OLESTREAM_GET
;
8197 if(pData
->dwTypeID
!= 0)
8199 /* Get the length of the OleTypeName */
8200 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8201 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8203 hRes
= CONVERT10_E_OLESTREAM_GET
;
8208 if(pData
->dwOleTypeNameLength
> 0)
8210 /* Get the OleTypeName */
8211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8212 if(dwSize
!= pData
->dwOleTypeNameLength
)
8214 hRes
= CONVERT10_E_OLESTREAM_GET
;
8220 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8221 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8223 hRes
= CONVERT10_E_OLESTREAM_GET
;
8227 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8228 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8229 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8230 if(pData
->pstrOleObjFileName
)
8232 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8233 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8235 hRes
= CONVERT10_E_OLESTREAM_GET
;
8239 hRes
= CONVERT10_E_OLESTREAM_GET
;
8244 /* Get the Width of the Metafile */
8245 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8246 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8248 hRes
= CONVERT10_E_OLESTREAM_GET
;
8252 /* Get the Height of the Metafile */
8253 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8254 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8256 hRes
= CONVERT10_E_OLESTREAM_GET
;
8262 /* Get the Length of the Data */
8263 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8264 if(dwSize
!= sizeof(pData
->dwDataLength
))
8266 hRes
= CONVERT10_E_OLESTREAM_GET
;
8270 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8272 if(!bStrem1
) /* if it is a second OLE stream data */
8274 pData
->dwDataLength
-= 8;
8275 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8276 if(dwSize
!= sizeof(pData
->strUnknown
))
8278 hRes
= CONVERT10_E_OLESTREAM_GET
;
8284 if(pData
->dwDataLength
> 0)
8286 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8288 /* Get Data (ex. IStorage, Metafile, or BMP) */
8291 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8292 if(dwSize
!= pData
->dwDataLength
)
8294 hRes
= CONVERT10_E_OLESTREAM_GET
;
8299 hRes
= CONVERT10_E_OLESTREAM_GET
;
8308 /*************************************************************************
8309 * OLECONVERT_SaveOLE10 [Internal]
8311 * Saves the OLE10 STREAM From memory
8314 * pData [I] Data Structure for the OLESTREAM Data
8315 * pOleStream [I] The OLESTREAM to save
8319 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8322 * This function is used by OleConvertIStorageToOLESTREAM only.
8325 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8328 HRESULT hRes
= S_OK
;
8332 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8333 if(dwSize
!= sizeof(pData
->dwOleID
))
8335 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8340 /* Set the TypeID */
8341 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8342 if(dwSize
!= sizeof(pData
->dwTypeID
))
8344 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8348 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8350 /* Set the Length of the OleTypeName */
8351 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8352 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8354 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8359 if(pData
->dwOleTypeNameLength
> 0)
8361 /* Set the OleTypeName */
8362 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8363 if(dwSize
!= pData
->dwOleTypeNameLength
)
8365 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8372 /* Set the width of the Metafile */
8373 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8374 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8376 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8382 /* Set the height of the Metafile */
8383 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8384 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8386 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8392 /* Set the length of the Data */
8393 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8394 if(dwSize
!= sizeof(pData
->dwDataLength
))
8396 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8402 if(pData
->dwDataLength
> 0)
8404 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8405 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8406 if(dwSize
!= pData
->dwDataLength
)
8408 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8416 /*************************************************************************
8417 * OLECONVERT_GetOLE20FromOLE10[Internal]
8419 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8420 * opens it, and copies the content to the dest IStorage for
8421 * OleConvertOLESTREAMToIStorage
8425 * pDestStorage [I] The IStorage to copy the data to
8426 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8427 * nBufferLength [I] The size of the buffer
8436 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8440 IStorage
*pTempStorage
;
8441 DWORD dwNumOfBytesWritten
;
8442 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8443 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8445 /* Create a temp File */
8446 GetTempPathW(MAX_PATH
, wstrTempDir
);
8447 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8448 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8450 if(hFile
!= INVALID_HANDLE_VALUE
)
8452 /* Write IStorage Data to File */
8453 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8456 /* Open and copy temp storage to the Dest Storage */
8457 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8460 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8461 IStorage_Release(pTempStorage
);
8463 DeleteFileW(wstrTempFile
);
8468 /*************************************************************************
8469 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8471 * Saves the OLE10 STREAM From memory
8474 * pStorage [I] The Src IStorage to copy
8475 * pData [I] The Dest Memory to write to.
8478 * The size in bytes allocated for pData
8481 * Memory allocated for pData must be freed by the caller
8483 * Used by OleConvertIStorageToOLESTREAM only.
8486 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8490 DWORD nDataLength
= 0;
8491 IStorage
*pTempStorage
;
8492 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8493 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8497 /* Create temp Storage */
8498 GetTempPathW(MAX_PATH
, wstrTempDir
);
8499 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8500 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8504 /* Copy Src Storage to the Temp Storage */
8505 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8506 IStorage_Release(pTempStorage
);
8508 /* Open Temp Storage as a file and copy to memory */
8509 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8510 if(hFile
!= INVALID_HANDLE_VALUE
)
8512 nDataLength
= GetFileSize(hFile
, NULL
);
8513 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8514 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8517 DeleteFileW(wstrTempFile
);
8522 /*************************************************************************
8523 * STORAGE_CreateOleStream [Internal]
8525 * Creates the "\001OLE" stream in the IStorage if necessary.
8528 * storage [I] Dest storage to create the stream in
8529 * flags [I] flags to be set for newly created stream
8532 * HRESULT return value
8536 * This stream is still unknown, MS Word seems to have extra data
8537 * but since the data is stored in the OLESTREAM there should be
8538 * no need to recreate the stream. If the stream is manually
8539 * deleted it will create it with this default data.
8542 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
8544 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
8545 static const DWORD version_magic
= 0x02000001;
8549 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
8552 struct empty_1ole_stream
{
8553 DWORD version_magic
;
8555 DWORD update_options
;
8557 DWORD mon_stream_size
;
8559 struct empty_1ole_stream stream_data
;
8561 stream_data
.version_magic
= version_magic
;
8562 stream_data
.flags
= flags
;
8563 stream_data
.update_options
= 0;
8564 stream_data
.reserved
= 0;
8565 stream_data
.mon_stream_size
= 0;
8567 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
8568 IStream_Release(stream
);
8574 /* write a string to a stream, preceded by its length */
8575 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8582 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8583 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8588 str
= CoTaskMemAlloc( len
);
8589 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8590 r
= IStream_Write( stm
, str
, len
, NULL
);
8591 CoTaskMemFree( str
);
8595 /* read a string preceded by its length from a stream */
8596 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8599 DWORD len
, count
= 0;
8603 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8606 if( count
!= sizeof(len
) )
8607 return E_OUTOFMEMORY
;
8609 TRACE("%d bytes\n",len
);
8611 str
= CoTaskMemAlloc( len
);
8613 return E_OUTOFMEMORY
;
8615 r
= IStream_Read( stm
, str
, len
, &count
);
8620 CoTaskMemFree( str
);
8621 return E_OUTOFMEMORY
;
8624 TRACE("Read string %s\n",debugstr_an(str
,len
));
8626 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8627 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8629 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8630 CoTaskMemFree( str
);
8638 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8639 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8643 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8645 static const BYTE unknown1
[12] =
8646 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8647 0xFF, 0xFF, 0xFF, 0xFF};
8648 static const BYTE unknown2
[16] =
8649 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8650 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8652 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8653 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8654 debugstr_w(szProgIDName
));
8656 /* Create a CompObj stream */
8657 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8658 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8662 /* Write CompObj Structure to stream */
8663 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8665 if( SUCCEEDED( r
) )
8666 r
= WriteClassStm( pstm
, clsid
);
8668 if( SUCCEEDED( r
) )
8669 r
= STREAM_WriteString( pstm
, lpszUserType
);
8670 if( SUCCEEDED( r
) )
8671 r
= STREAM_WriteString( pstm
, szClipName
);
8672 if( SUCCEEDED( r
) )
8673 r
= STREAM_WriteString( pstm
, szProgIDName
);
8674 if( SUCCEEDED( r
) )
8675 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8677 IStream_Release( pstm
);
8682 /***********************************************************************
8683 * WriteFmtUserTypeStg (OLE32.@)
8685 HRESULT WINAPI
WriteFmtUserTypeStg(
8686 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8689 WCHAR szwClipName
[0x40];
8690 CLSID clsid
= CLSID_NULL
;
8691 LPWSTR wstrProgID
= NULL
;
8694 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8696 /* get the clipboard format name */
8697 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8700 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8702 /* FIXME: There's room to save a CLSID and its ProgID, but
8703 the CLSID is not looked up in the registry and in all the
8704 tests I wrote it was CLSID_NULL. Where does it come from?
8707 /* get the real program ID. This may fail, but that's fine */
8708 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8710 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8712 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8713 lpszUserType
, szwClipName
, wstrProgID
);
8715 CoTaskMemFree(wstrProgID
);
8721 /******************************************************************************
8722 * ReadFmtUserTypeStg [OLE32.@]
8724 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8728 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8729 unsigned char unknown1
[12];
8730 unsigned char unknown2
[16];
8732 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8735 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8737 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8738 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8741 WARN("Failed to open stream r = %08x\n", r
);
8745 /* read the various parts of the structure */
8746 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8747 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8749 r
= ReadClassStm( stm
, &clsid
);
8753 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8757 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8761 r
= STREAM_ReadString( stm
, &szProgIDName
);
8765 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8766 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8769 /* ok, success... now we just need to store what we found */
8771 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8772 CoTaskMemFree( szOleTypeName
);
8774 if( lplpszUserType
)
8775 *lplpszUserType
= szCLSIDName
;
8776 CoTaskMemFree( szProgIDName
);
8779 IStream_Release( stm
);
8785 /*************************************************************************
8786 * OLECONVERT_CreateCompObjStream [Internal]
8788 * Creates a "\001CompObj" is the destination IStorage if necessary.
8791 * pStorage [I] The dest IStorage to create the CompObj Stream
8793 * strOleTypeName [I] The ProgID
8797 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8800 * This function is used by OleConvertOLESTREAMToIStorage only.
8802 * The stream data is stored in the OLESTREAM and there should be
8803 * no need to recreate the stream. If the stream is manually
8804 * deleted it will attempt to create it by querying the registry.
8808 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8811 HRESULT hStorageRes
, hRes
= S_OK
;
8812 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8813 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8814 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8816 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8817 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8819 /* Initialize the CompObj structure */
8820 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8821 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8822 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8825 /* Create a CompObj stream if it doesn't exist */
8826 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8827 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8828 if(hStorageRes
== S_OK
)
8830 /* copy the OleTypeName to the compobj struct */
8831 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8832 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8834 /* copy the OleTypeName to the compobj struct */
8835 /* Note: in the test made, these were Identical */
8836 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8837 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8840 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8841 bufferW
, OLESTREAM_MAX_STR_LEN
);
8842 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8848 /* Get the CLSID Default Name from the Registry */
8849 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8850 if(hErr
== ERROR_SUCCESS
)
8852 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8853 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8854 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8855 if(hErr
== ERROR_SUCCESS
)
8857 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8863 /* Write CompObj Structure to stream */
8864 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8866 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8868 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8869 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8871 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8873 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8874 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8876 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8878 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8879 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8881 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8883 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8884 IStream_Release(pStream
);
8890 /*************************************************************************
8891 * OLECONVERT_CreateOlePresStream[Internal]
8893 * Creates the "\002OlePres000" Stream with the Metafile data
8896 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8897 * dwExtentX [I] Width of the Metafile
8898 * dwExtentY [I] Height of the Metafile
8899 * pData [I] Metafile data
8900 * dwDataLength [I] Size of the Metafile data
8904 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8907 * This function is used by OleConvertOLESTREAMToIStorage only.
8910 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8914 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8915 BYTE pOlePresStreamHeader
[] =
8917 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8918 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8919 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8920 0x00, 0x00, 0x00, 0x00
8923 BYTE pOlePresStreamHeaderEmpty
[] =
8925 0x00, 0x00, 0x00, 0x00,
8926 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8927 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8928 0x00, 0x00, 0x00, 0x00
8931 /* Create the OlePres000 Stream */
8932 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8933 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8938 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8940 memset(&OlePres
, 0, sizeof(OlePres
));
8941 /* Do we have any metafile data to save */
8942 if(dwDataLength
> 0)
8944 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8945 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8949 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8950 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8952 /* Set width and height of the metafile */
8953 OlePres
.dwExtentX
= dwExtentX
;
8954 OlePres
.dwExtentY
= -dwExtentY
;
8956 /* Set Data and Length */
8957 if(dwDataLength
> sizeof(METAFILEPICT16
))
8959 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8960 OlePres
.pData
= &(pData
[8]);
8962 /* Save OlePres000 Data to Stream */
8963 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8964 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8965 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8966 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8967 if(OlePres
.dwSize
> 0)
8969 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8971 IStream_Release(pStream
);
8975 /*************************************************************************
8976 * OLECONVERT_CreateOle10NativeStream [Internal]
8978 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8981 * pStorage [I] Dest storage to create the stream in
8982 * pData [I] Ole10 Native Data (ex. bmp)
8983 * dwDataLength [I] Size of the Ole10 Native Data
8989 * This function is used by OleConvertOLESTREAMToIStorage only.
8991 * Might need to verify the data and return appropriate error message
8994 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8998 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9000 /* Create the Ole10Native Stream */
9001 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9002 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9006 /* Write info to stream */
9007 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9008 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9009 IStream_Release(pStream
);
9014 /*************************************************************************
9015 * OLECONVERT_GetOLE10ProgID [Internal]
9017 * Finds the ProgID (or OleTypeID) from the IStorage
9020 * pStorage [I] The Src IStorage to get the ProgID
9021 * strProgID [I] the ProgID string to get
9022 * dwSize [I] the size of the string
9026 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9029 * This function is used by OleConvertIStorageToOLESTREAM only.
9033 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9037 LARGE_INTEGER iSeekPos
;
9038 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9039 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9041 /* Open the CompObj Stream */
9042 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9043 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9047 /*Get the OleType from the CompObj Stream */
9048 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9049 iSeekPos
.u
.HighPart
= 0;
9051 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9052 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9053 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9054 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9055 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9056 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9057 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9059 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9062 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9064 IStream_Release(pStream
);
9069 LPOLESTR wstrProgID
;
9071 /* Get the OleType from the registry */
9072 REFCLSID clsid
= &(stat
.clsid
);
9073 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9074 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9077 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9084 /*************************************************************************
9085 * OLECONVERT_GetOle10PresData [Internal]
9087 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9090 * pStorage [I] Src IStroage
9091 * pOleStream [I] Dest OleStream Mem Struct
9097 * This function is used by OleConvertIStorageToOLESTREAM only.
9099 * Memory allocated for pData must be freed by the caller
9103 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9108 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9110 /* Initialize Default data for OLESTREAM */
9111 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9112 pOleStreamData
[0].dwTypeID
= 2;
9113 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9114 pOleStreamData
[1].dwTypeID
= 0;
9115 pOleStreamData
[0].dwMetaFileWidth
= 0;
9116 pOleStreamData
[0].dwMetaFileHeight
= 0;
9117 pOleStreamData
[0].pData
= NULL
;
9118 pOleStreamData
[1].pData
= NULL
;
9120 /* Open Ole10Native Stream */
9121 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9122 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9126 /* Read Size and Data */
9127 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9128 if(pOleStreamData
->dwDataLength
> 0)
9130 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9131 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9133 IStream_Release(pStream
);
9139 /*************************************************************************
9140 * OLECONVERT_GetOle20PresData[Internal]
9142 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9145 * pStorage [I] Src IStroage
9146 * pOleStreamData [I] Dest OleStream Mem Struct
9152 * This function is used by OleConvertIStorageToOLESTREAM only.
9154 * Memory allocated for pData must be freed by the caller
9156 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9160 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9161 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9163 /* Initialize Default data for OLESTREAM */
9164 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9165 pOleStreamData
[0].dwTypeID
= 2;
9166 pOleStreamData
[0].dwMetaFileWidth
= 0;
9167 pOleStreamData
[0].dwMetaFileHeight
= 0;
9168 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9169 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9170 pOleStreamData
[1].dwTypeID
= 0;
9171 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9172 pOleStreamData
[1].strOleTypeName
[0] = 0;
9173 pOleStreamData
[1].dwMetaFileWidth
= 0;
9174 pOleStreamData
[1].dwMetaFileHeight
= 0;
9175 pOleStreamData
[1].pData
= NULL
;
9176 pOleStreamData
[1].dwDataLength
= 0;
9179 /* Open OlePress000 stream */
9180 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9181 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9184 LARGE_INTEGER iSeekPos
;
9185 METAFILEPICT16 MetaFilePict
;
9186 static const char strMetafilePictName
[] = "METAFILEPICT";
9188 /* Set the TypeID for a Metafile */
9189 pOleStreamData
[1].dwTypeID
= 5;
9191 /* Set the OleTypeName to Metafile */
9192 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9193 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9195 iSeekPos
.u
.HighPart
= 0;
9196 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9198 /* Get Presentation Data */
9199 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9200 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9201 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9202 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9204 /*Set width and Height */
9205 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9206 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9207 if(olePress
.dwSize
> 0)
9210 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9212 /* Set MetaFilePict struct */
9213 MetaFilePict
.mm
= 8;
9214 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9215 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9216 MetaFilePict
.hMF
= 0;
9218 /* Get Metafile Data */
9219 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9220 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9221 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9223 IStream_Release(pStream
);
9227 /*************************************************************************
9228 * OleConvertOLESTREAMToIStorage [OLE32.@]
9233 * DVTARGETDEVICE parameter is not handled
9234 * Still unsure of some mem fields for OLE 10 Stream
9235 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9236 * and "\001OLE" streams
9239 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9240 LPOLESTREAM pOleStream
,
9242 const DVTARGETDEVICE
* ptd
)
9246 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9248 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9250 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9254 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9257 if(pstg
== NULL
|| pOleStream
== NULL
)
9259 hRes
= E_INVALIDARG
;
9264 /* Load the OLESTREAM to Memory */
9265 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9270 /* Load the OLESTREAM to Memory (part 2)*/
9271 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9277 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9279 /* Do we have the IStorage Data in the OLESTREAM */
9280 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9282 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9283 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9287 /* It must be an original OLE 1.0 source */
9288 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9293 /* It must be an original OLE 1.0 source */
9294 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9297 /* Create CompObj Stream if necessary */
9298 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9301 /*Create the Ole Stream if necessary */
9302 STORAGE_CreateOleStream(pstg
, 0);
9307 /* Free allocated memory */
9308 for(i
=0; i
< 2; i
++)
9310 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9311 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9312 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9317 /*************************************************************************
9318 * OleConvertIStorageToOLESTREAM [OLE32.@]
9325 * Still unsure of some mem fields for OLE 10 Stream
9326 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9327 * and "\001OLE" streams.
9330 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9332 LPOLESTREAM pOleStream
)
9335 HRESULT hRes
= S_OK
;
9337 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9338 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9340 TRACE("%p %p\n", pstg
, pOleStream
);
9342 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9344 if(pstg
== NULL
|| pOleStream
== NULL
)
9346 hRes
= E_INVALIDARG
;
9350 /* Get the ProgID */
9351 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9352 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9356 /* Was it originally Ole10 */
9357 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9360 IStream_Release(pStream
);
9361 /* Get Presentation Data for Ole10Native */
9362 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9366 /* Get Presentation Data (OLE20) */
9367 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9370 /* Save OLESTREAM */
9371 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9374 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9379 /* Free allocated memory */
9380 for(i
=0; i
< 2; i
++)
9382 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9388 enum stream_1ole_flags
{
9389 OleStream_LinkedObject
= 0x00000001,
9390 OleStream_Convert
= 0x00000004
9393 /***********************************************************************
9394 * GetConvertStg (OLE32.@)
9396 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
9398 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9399 static const DWORD version_magic
= 0x02000001;
9406 if (!stg
) return E_INVALIDARG
;
9408 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9409 if (FAILED(hr
)) return hr
;
9411 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9412 IStream_Release(stream
);
9413 if (FAILED(hr
)) return hr
;
9415 if (header
[0] != version_magic
)
9417 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
9421 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
9424 /***********************************************************************
9425 * SetConvertStg (OLE32.@)
9427 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
9429 DWORD flags
= convert
? OleStream_Convert
: 0;
9432 TRACE("(%p, %d)\n", storage
, convert
);
9434 hr
= STORAGE_CreateOleStream(storage
, flags
);
9435 if (hr
== STG_E_FILEALREADYEXISTS
)
9437 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9441 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9442 if (FAILED(hr
)) return hr
;
9444 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9447 IStream_Release(stream
);
9451 /* update flag if differs */
9452 if ((header
[1] ^ flags
) & OleStream_Convert
)
9456 if (header
[1] & OleStream_Convert
)
9457 flags
= header
[1] & ~OleStream_Convert
;
9459 flags
= header
[1] | OleStream_Convert
;
9461 pos
.QuadPart
= sizeof(DWORD
);
9462 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
9465 IStream_Release(stream
);
9469 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
9471 IStream_Release(stream
);
9477 /******************************************************************************
9478 * StgIsStorageFile [OLE32.@]
9479 * Verify if the file contains a storage object
9485 * S_OK if file has magic bytes as a storage object
9486 * S_FALSE if file is not storage
9489 StgIsStorageFile(LPCOLESTR fn
)
9495 TRACE("%s\n", debugstr_w(fn
));
9496 hf
= CreateFileW(fn
, GENERIC_READ
,
9497 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9498 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9500 if (hf
== INVALID_HANDLE_VALUE
)
9501 return STG_E_FILENOTFOUND
;
9503 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9505 WARN(" unable to read file\n");
9512 if (bytes_read
!= 8) {
9513 TRACE(" too short\n");
9517 if (!memcmp(magic
,STORAGE_magic
,8)) {
9522 TRACE(" -> Invalid header.\n");
9526 /***********************************************************************
9527 * WriteClassStm (OLE32.@)
9529 * Writes a CLSID to a stream.
9532 * pStm [I] Stream to write to.
9533 * rclsid [I] CLSID to write.
9537 * Failure: HRESULT code.
9539 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9541 TRACE("(%p,%p)\n",pStm
,rclsid
);
9543 if (!pStm
|| !rclsid
)
9544 return E_INVALIDARG
;
9546 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9549 /***********************************************************************
9550 * ReadClassStm (OLE32.@)
9552 * Reads a CLSID from a stream.
9555 * pStm [I] Stream to read from.
9556 * rclsid [O] CLSID to read.
9560 * Failure: HRESULT code.
9562 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9567 TRACE("(%p,%p)\n",pStm
,pclsid
);
9569 if (!pStm
|| !pclsid
)
9570 return E_INVALIDARG
;
9572 /* clear the output args */
9573 *pclsid
= CLSID_NULL
;
9575 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9580 if (nbByte
!= sizeof(CLSID
))
9581 return STG_E_READFAULT
;