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
= StorageBaseImpl_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 TransactedSnapshotImpl_Revert(&This
->base
.IStorage_iface
);
4803 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
4805 IStorage_Release(&This
->scratch
->IStorage_iface
);
4807 HeapFree(GetProcessHeap(), 0, This
->entries
);
4809 HeapFree(GetProcessHeap(), 0, This
);
4812 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4814 /* We only need to flush when committing. */
4818 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4820 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4822 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4825 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4826 const DirEntry
*newData
, DirRef
*index
)
4828 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4830 TransactedDirEntry
*new_entry
;
4832 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4833 if (new_ref
== DIRENTRY_NULL
)
4834 return E_OUTOFMEMORY
;
4836 new_entry
= &This
->entries
[new_ref
];
4838 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4839 new_entry
->read
= 1;
4840 new_entry
->dirty
= 1;
4841 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4845 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4850 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4851 DirRef index
, const DirEntry
*data
)
4853 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4856 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4858 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4859 if (FAILED(hr
)) return hr
;
4861 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4863 if (index
!= This
->base
.storageDirEntry
)
4865 This
->entries
[index
].dirty
= 1;
4867 if (data
->size
.QuadPart
== 0 &&
4868 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4870 /* Since this entry is modified, and we aren't using its stream data, we
4871 * no longer care about the original entry. */
4873 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4875 if (delete_ref
!= DIRENTRY_NULL
)
4876 This
->entries
[delete_ref
].deleted
= 1;
4878 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4885 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4886 DirRef index
, DirEntry
*data
)
4888 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4891 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4892 if (FAILED(hr
)) return hr
;
4894 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4896 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4901 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4904 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4906 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4907 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4909 /* If we deleted this entry while it has stream data. We must have left the
4910 * data because some other entry is using it, and we need to leave the
4911 * original entry alone. */
4912 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4913 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4917 This
->entries
[index
].deleted
= 1;
4923 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4924 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4926 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4928 if (This
->entries
[index
].stream_dirty
)
4930 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4931 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4933 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4935 /* This stream doesn't live in the parent, and we haven't allocated storage
4942 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4943 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4947 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4948 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4950 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4953 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4954 if (FAILED(hr
)) return hr
;
4956 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4957 if (FAILED(hr
)) return hr
;
4959 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4960 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4962 if (SUCCEEDED(hr
) && size
!= 0)
4963 This
->entries
[index
].data
.size
.QuadPart
= max(
4964 This
->entries
[index
].data
.size
.QuadPart
,
4965 offset
.QuadPart
+ size
);
4970 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4971 DirRef index
, ULARGE_INTEGER newsize
)
4973 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4976 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4977 if (FAILED(hr
)) return hr
;
4979 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4982 if (newsize
.QuadPart
== 0)
4984 /* Destroy any parent references or entries in the scratch file. */
4985 if (This
->entries
[index
].stream_dirty
)
4987 ULARGE_INTEGER zero
;
4989 StorageBaseImpl_StreamSetSize(This
->scratch
,
4990 This
->entries
[index
].stream_entry
, zero
);
4991 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4992 This
->entries
[index
].stream_entry
);
4993 This
->entries
[index
].stream_dirty
= 0;
4995 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4998 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5000 if (delete_ref
!= DIRENTRY_NULL
)
5001 This
->entries
[delete_ref
].deleted
= 1;
5003 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5008 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5009 if (FAILED(hr
)) return hr
;
5011 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5012 This
->entries
[index
].stream_entry
, newsize
);
5016 This
->entries
[index
].data
.size
= newsize
;
5021 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5022 DirRef dst
, DirRef src
)
5024 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5026 TransactedDirEntry
*dst_entry
, *src_entry
;
5028 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5029 if (FAILED(hr
)) return hr
;
5031 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5032 if (FAILED(hr
)) return hr
;
5034 dst_entry
= &This
->entries
[dst
];
5035 src_entry
= &This
->entries
[src
];
5037 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5038 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5039 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5040 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5041 dst_entry
->data
.size
= src_entry
->data
.size
;
5046 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5048 StorageBaseImpl_QueryInterface
,
5049 StorageBaseImpl_AddRef
,
5050 StorageBaseImpl_Release
,
5051 StorageBaseImpl_CreateStream
,
5052 StorageBaseImpl_OpenStream
,
5053 StorageBaseImpl_CreateStorage
,
5054 StorageBaseImpl_OpenStorage
,
5055 StorageBaseImpl_CopyTo
,
5056 StorageBaseImpl_MoveElementTo
,
5057 TransactedSnapshotImpl_Commit
,
5058 TransactedSnapshotImpl_Revert
,
5059 StorageBaseImpl_EnumElements
,
5060 StorageBaseImpl_DestroyElement
,
5061 StorageBaseImpl_RenameElement
,
5062 StorageBaseImpl_SetElementTimes
,
5063 StorageBaseImpl_SetClass
,
5064 StorageBaseImpl_SetStateBits
,
5065 StorageBaseImpl_Stat
5068 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5070 TransactedSnapshotImpl_Destroy
,
5071 TransactedSnapshotImpl_Invalidate
,
5072 TransactedSnapshotImpl_Flush
,
5073 TransactedSnapshotImpl_GetFilename
,
5074 TransactedSnapshotImpl_CreateDirEntry
,
5075 TransactedSnapshotImpl_WriteDirEntry
,
5076 TransactedSnapshotImpl_ReadDirEntry
,
5077 TransactedSnapshotImpl_DestroyDirEntry
,
5078 TransactedSnapshotImpl_StreamReadAt
,
5079 TransactedSnapshotImpl_StreamWriteAt
,
5080 TransactedSnapshotImpl_StreamSetSize
,
5081 TransactedSnapshotImpl_StreamLink
5084 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5085 TransactedSnapshotImpl
** result
)
5089 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5094 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5096 /* This is OK because the property set storage functions use the IStorage functions. */
5097 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5098 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5100 list_init(&(*result
)->base
.strmHead
);
5102 list_init(&(*result
)->base
.storageHead
);
5104 (*result
)->base
.ref
= 1;
5106 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5108 /* Create a new temporary storage to act as the scratch file. */
5109 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5111 (*result
)->scratch
= impl_from_IStorage(scratch
);
5115 ULONG num_entries
= 20;
5117 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5118 (*result
)->entries_size
= num_entries
;
5119 (*result
)->firstFreeEntry
= 0;
5121 if ((*result
)->entries
)
5123 /* parentStorage already has 1 reference, which we take over here. */
5124 (*result
)->transactedParent
= parentStorage
;
5126 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5128 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5132 IStorage_Release(scratch
);
5138 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5143 return E_OUTOFMEMORY
;
5146 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5147 StorageBaseImpl
** result
)
5151 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5153 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5156 return TransactedSnapshotImpl_Construct(parentStorage
,
5157 (TransactedSnapshotImpl
**)result
);
5160 static HRESULT
Storage_Construct(
5168 StorageBaseImpl
** result
)
5170 StorageImpl
*newStorage
;
5171 StorageBaseImpl
*newTransactedStorage
;
5174 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5175 if (FAILED(hr
)) goto end
;
5177 if (openFlags
& STGM_TRANSACTED
)
5179 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5181 IStorage_Release(&newStorage
->base
.IStorage_iface
);
5183 *result
= newTransactedStorage
;
5186 *result
= &newStorage
->base
;
5192 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5194 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5196 if (!This
->base
.reverted
)
5198 TRACE("Storage invalidated (stg=%p)\n", This
);
5200 This
->base
.reverted
= 1;
5202 This
->parentStorage
= NULL
;
5204 StorageBaseImpl_DeleteAll(&This
->base
);
5206 list_remove(&This
->ParentListEntry
);
5210 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5212 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5214 StorageInternalImpl_Invalidate(&This
->base
);
5216 HeapFree(GetProcessHeap(), 0, This
);
5219 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5221 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5223 return StorageBaseImpl_Flush(This
->parentStorage
);
5226 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5228 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5230 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5233 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5234 const DirEntry
*newData
, DirRef
*index
)
5236 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5238 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5242 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5243 DirRef index
, const DirEntry
*data
)
5245 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5247 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5251 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5252 DirRef index
, DirEntry
*data
)
5254 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5256 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5260 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5263 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5265 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5269 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5270 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5272 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5274 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5275 index
, offset
, size
, buffer
, bytesRead
);
5278 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5279 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5281 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5283 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5284 index
, offset
, size
, buffer
, bytesWritten
);
5287 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5288 DirRef index
, ULARGE_INTEGER newsize
)
5290 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5292 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5296 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5297 DirRef dst
, DirRef src
)
5299 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5301 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5305 /******************************************************************************
5307 ** Storage32InternalImpl_Commit
5310 static HRESULT WINAPI
StorageInternalImpl_Commit(
5312 DWORD grfCommitFlags
) /* [in] */
5314 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5315 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5316 return StorageBaseImpl_Flush(This
);
5319 /******************************************************************************
5321 ** Storage32InternalImpl_Revert
5324 static HRESULT WINAPI
StorageInternalImpl_Revert(
5327 FIXME("(%p): stub\n", iface
);
5331 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5333 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
5334 HeapFree(GetProcessHeap(), 0, This
);
5337 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5338 IEnumSTATSTG
* iface
,
5342 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5345 return E_INVALIDARG
;
5349 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5350 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5353 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5357 return E_NOINTERFACE
;
5360 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5361 IEnumSTATSTG
* iface
)
5363 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5364 return InterlockedIncrement(&This
->ref
);
5367 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5368 IEnumSTATSTG
* iface
)
5370 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5374 newRef
= InterlockedDecrement(&This
->ref
);
5378 IEnumSTATSTGImpl_Destroy(This
);
5384 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5385 IEnumSTATSTGImpl
* This
,
5388 DirRef result
= DIRENTRY_NULL
;
5392 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5394 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5395 This
->parentStorage
->storageDirEntry
, &entry
);
5396 searchNode
= entry
.dirRootEntry
;
5398 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5400 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5404 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5408 searchNode
= entry
.rightChild
;
5412 result
= searchNode
;
5413 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5414 searchNode
= entry
.leftChild
;
5422 if (result
!= DIRENTRY_NULL
)
5423 memcpy(This
->name
, result_name
, sizeof(result_name
));
5429 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5430 IEnumSTATSTG
* iface
,
5433 ULONG
* pceltFetched
)
5435 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5437 DirEntry currentEntry
;
5438 STATSTG
* currentReturnStruct
= rgelt
;
5439 ULONG objectFetched
= 0;
5440 DirRef currentSearchNode
;
5443 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5444 return E_INVALIDARG
;
5446 if (This
->parentStorage
->reverted
)
5447 return STG_E_REVERTED
;
5450 * To avoid the special case, get another pointer to a ULONG value if
5451 * the caller didn't supply one.
5453 if (pceltFetched
==0)
5454 pceltFetched
= &objectFetched
;
5457 * Start the iteration, we will iterate until we hit the end of the
5458 * linked list or until we hit the number of items to iterate through
5462 while ( *pceltFetched
< celt
)
5464 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5466 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5470 * Read the entry from the storage.
5472 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5477 * Copy the information to the return buffer.
5479 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5480 currentReturnStruct
,
5485 * Step to the next item in the iteration
5488 currentReturnStruct
++;
5491 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5498 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5499 IEnumSTATSTG
* iface
,
5502 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5504 ULONG objectFetched
= 0;
5505 DirRef currentSearchNode
;
5508 if (This
->parentStorage
->reverted
)
5509 return STG_E_REVERTED
;
5511 while ( (objectFetched
< celt
) )
5513 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5515 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5521 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5527 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5528 IEnumSTATSTG
* iface
)
5530 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5532 if (This
->parentStorage
->reverted
)
5533 return STG_E_REVERTED
;
5540 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5541 IEnumSTATSTG
* iface
,
5542 IEnumSTATSTG
** ppenum
)
5544 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5546 IEnumSTATSTGImpl
* newClone
;
5548 if (This
->parentStorage
->reverted
)
5549 return STG_E_REVERTED
;
5552 * Perform a sanity check on the parameters.
5555 return E_INVALIDARG
;
5557 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5558 This
->storageDirEntry
);
5562 * The new clone enumeration must point to the same current node as
5565 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5567 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5570 * Don't forget to nail down a reference to the clone before
5573 IEnumSTATSTGImpl_AddRef(*ppenum
);
5579 * Virtual function table for the IEnumSTATSTGImpl class.
5581 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5583 IEnumSTATSTGImpl_QueryInterface
,
5584 IEnumSTATSTGImpl_AddRef
,
5585 IEnumSTATSTGImpl_Release
,
5586 IEnumSTATSTGImpl_Next
,
5587 IEnumSTATSTGImpl_Skip
,
5588 IEnumSTATSTGImpl_Reset
,
5589 IEnumSTATSTGImpl_Clone
5592 /******************************************************************************
5593 ** IEnumSTATSTGImpl implementation
5596 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5597 StorageBaseImpl
* parentStorage
,
5598 DirRef storageDirEntry
)
5600 IEnumSTATSTGImpl
* newEnumeration
;
5602 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5604 if (newEnumeration
!=0)
5607 * Set-up the virtual function table and reference count.
5609 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5610 newEnumeration
->ref
= 0;
5613 * We want to nail-down the reference to the storage in case the
5614 * enumeration out-lives the storage in the client application.
5616 newEnumeration
->parentStorage
= parentStorage
;
5617 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
5619 newEnumeration
->storageDirEntry
= storageDirEntry
;
5622 * Make sure the current node of the iterator is the first one.
5624 IEnumSTATSTGImpl_Reset(&newEnumeration
->IEnumSTATSTG_iface
);
5627 return newEnumeration
;
5631 * Virtual function table for the Storage32InternalImpl class.
5633 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5635 StorageBaseImpl_QueryInterface
,
5636 StorageBaseImpl_AddRef
,
5637 StorageBaseImpl_Release
,
5638 StorageBaseImpl_CreateStream
,
5639 StorageBaseImpl_OpenStream
,
5640 StorageBaseImpl_CreateStorage
,
5641 StorageBaseImpl_OpenStorage
,
5642 StorageBaseImpl_CopyTo
,
5643 StorageBaseImpl_MoveElementTo
,
5644 StorageInternalImpl_Commit
,
5645 StorageInternalImpl_Revert
,
5646 StorageBaseImpl_EnumElements
,
5647 StorageBaseImpl_DestroyElement
,
5648 StorageBaseImpl_RenameElement
,
5649 StorageBaseImpl_SetElementTimes
,
5650 StorageBaseImpl_SetClass
,
5651 StorageBaseImpl_SetStateBits
,
5652 StorageBaseImpl_Stat
5655 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5657 StorageInternalImpl_Destroy
,
5658 StorageInternalImpl_Invalidate
,
5659 StorageInternalImpl_Flush
,
5660 StorageInternalImpl_GetFilename
,
5661 StorageInternalImpl_CreateDirEntry
,
5662 StorageInternalImpl_WriteDirEntry
,
5663 StorageInternalImpl_ReadDirEntry
,
5664 StorageInternalImpl_DestroyDirEntry
,
5665 StorageInternalImpl_StreamReadAt
,
5666 StorageInternalImpl_StreamWriteAt
,
5667 StorageInternalImpl_StreamSetSize
,
5668 StorageInternalImpl_StreamLink
5671 /******************************************************************************
5672 ** Storage32InternalImpl implementation
5675 static StorageInternalImpl
* StorageInternalImpl_Construct(
5676 StorageBaseImpl
* parentStorage
,
5678 DirRef storageDirEntry
)
5680 StorageInternalImpl
* newStorage
;
5682 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5686 list_init(&newStorage
->base
.strmHead
);
5688 list_init(&newStorage
->base
.storageHead
);
5691 * Initialize the virtual function table.
5693 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5694 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5695 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5696 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5698 newStorage
->base
.reverted
= 0;
5700 newStorage
->base
.ref
= 1;
5702 newStorage
->parentStorage
= parentStorage
;
5705 * Keep a reference to the directory entry of this storage
5707 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5709 newStorage
->base
.create
= 0;
5717 /******************************************************************************
5718 ** StorageUtl implementation
5721 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5725 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5726 *value
= lendian16toh(tmp
);
5729 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5731 value
= htole16(value
);
5732 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5735 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5739 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5740 *value
= lendian32toh(tmp
);
5743 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5745 value
= htole32(value
);
5746 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5749 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5750 ULARGE_INTEGER
* value
)
5752 #ifdef WORDS_BIGENDIAN
5755 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5756 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5757 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5759 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5763 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5764 const ULARGE_INTEGER
*value
)
5766 #ifdef WORDS_BIGENDIAN
5769 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5770 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5771 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5773 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5777 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5779 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5780 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5781 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5783 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5786 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5788 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5789 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5790 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5792 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5795 void StorageUtl_CopyDirEntryToSTATSTG(
5796 StorageBaseImpl
* storage
,
5797 STATSTG
* destination
,
5798 const DirEntry
* source
,
5802 * The copy of the string occurs only when the flag is not set
5804 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5806 /* Use the filename for the root storage. */
5807 destination
->pwcsName
= 0;
5808 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5810 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5811 (source
->name
[0] == 0) )
5813 destination
->pwcsName
= 0;
5817 destination
->pwcsName
=
5818 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5820 strcpyW(destination
->pwcsName
, source
->name
);
5823 switch (source
->stgType
)
5827 destination
->type
= STGTY_STORAGE
;
5830 destination
->type
= STGTY_STREAM
;
5833 destination
->type
= STGTY_STREAM
;
5837 destination
->cbSize
= source
->size
;
5839 currentReturnStruct->mtime = {0}; TODO
5840 currentReturnStruct->ctime = {0};
5841 currentReturnStruct->atime = {0};
5843 destination
->grfMode
= 0;
5844 destination
->grfLocksSupported
= 0;
5845 destination
->clsid
= source
->clsid
;
5846 destination
->grfStateBits
= 0;
5847 destination
->reserved
= 0;
5850 /******************************************************************************
5851 ** BlockChainStream implementation
5854 /* Read and save the index of all blocks in this stream. */
5855 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5857 ULONG next_sector
, next_offset
;
5859 struct BlockChainRun
*last_run
;
5861 if (This
->indexCacheLen
== 0)
5865 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5869 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5870 next_offset
= last_run
->lastOffset
+1;
5871 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5872 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5874 if (FAILED(hr
)) return hr
;
5877 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5879 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5881 /* Add the current block to the cache. */
5882 if (This
->indexCacheSize
== 0)
5884 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5885 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5886 This
->indexCacheSize
= 16;
5888 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5890 struct BlockChainRun
*new_cache
;
5893 new_size
= This
->indexCacheSize
* 2;
5894 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5895 if (!new_cache
) return E_OUTOFMEMORY
;
5896 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5898 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5899 This
->indexCache
= new_cache
;
5900 This
->indexCacheSize
= new_size
;
5903 This
->indexCacheLen
++;
5904 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5905 last_run
->firstSector
= next_sector
;
5906 last_run
->firstOffset
= next_offset
;
5909 last_run
->lastOffset
= next_offset
;
5911 /* Find the next block. */
5913 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5914 if (FAILED(hr
)) return hr
;
5917 if (This
->indexCacheLen
)
5919 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5920 This
->numBlocks
= last_run
->lastOffset
+1;
5924 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5925 This
->numBlocks
= 0;
5931 /* Locate the nth block in this stream. */
5932 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5934 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5935 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5937 if (offset
>= This
->numBlocks
)
5938 return BLOCK_END_OF_CHAIN
;
5940 while (min_run
< max_run
)
5942 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5943 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5945 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5946 max_run
= run_to_check
-1;
5948 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5950 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5951 min_run
= run_to_check
+1;
5954 /* Block is in this run. */
5955 min_run
= max_run
= run_to_check
;
5958 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5961 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5962 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5964 BlockChainBlock
*result
=NULL
;
5968 if (This
->cachedBlocks
[i
].index
== index
)
5970 *sector
= This
->cachedBlocks
[i
].sector
;
5971 *block
= &This
->cachedBlocks
[i
];
5975 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5976 if (*sector
== BLOCK_END_OF_CHAIN
)
5977 return STG_E_DOCFILECORRUPT
;
5981 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5982 result
= &This
->cachedBlocks
[0];
5983 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5984 result
= &This
->cachedBlocks
[1];
5987 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5988 if (This
->blockToEvict
== 2)
5989 This
->blockToEvict
= 0;
5994 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5995 return STG_E_WRITEFAULT
;
6000 result
->index
= index
;
6001 result
->sector
= *sector
;
6008 BlockChainStream
* BlockChainStream_Construct(
6009 StorageImpl
* parentStorage
,
6010 ULONG
* headOfStreamPlaceHolder
,
6013 BlockChainStream
* newStream
;
6015 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6017 newStream
->parentStorage
= parentStorage
;
6018 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6019 newStream
->ownerDirEntry
= dirEntry
;
6020 newStream
->indexCache
= NULL
;
6021 newStream
->indexCacheLen
= 0;
6022 newStream
->indexCacheSize
= 0;
6023 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6024 newStream
->cachedBlocks
[0].dirty
= 0;
6025 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6026 newStream
->cachedBlocks
[1].dirty
= 0;
6027 newStream
->blockToEvict
= 0;
6029 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6031 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6032 HeapFree(GetProcessHeap(), 0, newStream
);
6039 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6042 if (!This
) return S_OK
;
6045 if (This
->cachedBlocks
[i
].dirty
)
6047 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6048 This
->cachedBlocks
[i
].dirty
= 0;
6050 return STG_E_WRITEFAULT
;
6056 void BlockChainStream_Destroy(BlockChainStream
* This
)
6060 BlockChainStream_Flush(This
);
6061 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6063 HeapFree(GetProcessHeap(), 0, This
);
6066 /******************************************************************************
6067 * BlockChainStream_GetHeadOfChain
6069 * Returns the head of this stream chain.
6070 * Some special chains don't have directory entries, their heads are kept in
6071 * This->headOfStreamPlaceHolder.
6074 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6076 DirEntry chainEntry
;
6079 if (This
->headOfStreamPlaceHolder
!= 0)
6080 return *(This
->headOfStreamPlaceHolder
);
6082 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6084 hr
= StorageImpl_ReadDirEntry(
6085 This
->parentStorage
,
6086 This
->ownerDirEntry
,
6091 return chainEntry
.startingBlock
;
6095 return BLOCK_END_OF_CHAIN
;
6098 /******************************************************************************
6099 * BlockChainStream_GetCount
6101 * Returns the number of blocks that comprises this chain.
6102 * This is not the size of the stream as the last block may not be full!
6104 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6106 return This
->numBlocks
;
6109 /******************************************************************************
6110 * BlockChainStream_ReadAt
6112 * Reads a specified number of bytes from this chain at the specified offset.
6113 * bytesRead may be NULL.
6114 * Failure will be returned if the specified number of bytes has not been read.
6116 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6117 ULARGE_INTEGER offset
,
6122 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6123 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6124 ULONG bytesToReadInBuffer
;
6127 ULARGE_INTEGER stream_size
;
6129 BlockChainBlock
*cachedBlock
;
6131 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6134 * Find the first block in the stream that contains part of the buffer.
6136 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6140 stream_size
= BlockChainStream_GetSize(This
);
6141 if (stream_size
.QuadPart
> offset
.QuadPart
)
6142 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6147 * Start reading the buffer.
6149 bufferWalker
= buffer
;
6153 ULARGE_INTEGER ulOffset
;
6157 * Calculate how many bytes we can copy from this big block.
6159 bytesToReadInBuffer
=
6160 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6162 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6169 /* Not in cache, and we're going to read past the end of the block. */
6170 ulOffset
.u
.HighPart
= 0;
6171 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6174 StorageImpl_ReadAt(This
->parentStorage
,
6177 bytesToReadInBuffer
,
6182 if (!cachedBlock
->read
)
6184 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6185 return STG_E_READFAULT
;
6187 cachedBlock
->read
= 1;
6190 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6191 bytesReadAt
= bytesToReadInBuffer
;
6194 blockNoInSequence
++;
6195 bufferWalker
+= bytesReadAt
;
6196 size
-= bytesReadAt
;
6197 *bytesRead
+= bytesReadAt
;
6198 offsetInBlock
= 0; /* There is no offset on the next block */
6200 if (bytesToReadInBuffer
!= bytesReadAt
)
6207 /******************************************************************************
6208 * BlockChainStream_WriteAt
6210 * Writes the specified number of bytes to this chain at the specified offset.
6211 * Will fail if not all specified number of bytes have been written.
6213 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6214 ULARGE_INTEGER offset
,
6217 ULONG
* bytesWritten
)
6219 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6220 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6223 const BYTE
* bufferWalker
;
6225 BlockChainBlock
*cachedBlock
;
6228 bufferWalker
= buffer
;
6232 ULARGE_INTEGER ulOffset
;
6233 DWORD bytesWrittenAt
;
6236 * Calculate how many bytes we can copy to this big block.
6239 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6241 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6243 /* BlockChainStream_SetSize should have already been called to ensure we have
6244 * enough blocks in the chain to write into */
6247 ERR("not enough blocks in chain to write data\n");
6253 /* Not in cache, and we're going to write past the end of the block. */
6254 ulOffset
.u
.HighPart
= 0;
6255 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6258 StorageImpl_WriteAt(This
->parentStorage
,
6266 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6268 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6269 return STG_E_READFAULT
;
6272 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6273 bytesWrittenAt
= bytesToWrite
;
6274 cachedBlock
->read
= 1;
6275 cachedBlock
->dirty
= 1;
6278 blockNoInSequence
++;
6279 bufferWalker
+= bytesWrittenAt
;
6280 size
-= bytesWrittenAt
;
6281 *bytesWritten
+= bytesWrittenAt
;
6282 offsetInBlock
= 0; /* There is no offset on the next block */
6284 if (bytesWrittenAt
!= bytesToWrite
)
6288 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6291 /******************************************************************************
6292 * BlockChainStream_Shrink
6294 * Shrinks this chain in the big block depot.
6296 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6297 ULARGE_INTEGER newSize
)
6304 * Figure out how many blocks are needed to contain the new size
6306 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6308 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6314 * Go to the new end of chain
6316 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6318 /* Mark the new end of chain */
6319 StorageImpl_SetNextBlockInChain(
6320 This
->parentStorage
,
6322 BLOCK_END_OF_CHAIN
);
6324 This
->tailIndex
= blockIndex
;
6328 if (This
->headOfStreamPlaceHolder
!= 0)
6330 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6334 DirEntry chainEntry
;
6335 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6337 StorageImpl_ReadDirEntry(
6338 This
->parentStorage
,
6339 This
->ownerDirEntry
,
6342 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6344 StorageImpl_WriteDirEntry(
6345 This
->parentStorage
,
6346 This
->ownerDirEntry
,
6350 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6353 This
->numBlocks
= numBlocks
;
6356 * Mark the extra blocks as free
6358 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6360 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6361 StorageImpl_FreeBigBlock(This
->parentStorage
,
6362 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6363 if (last_run
->lastOffset
== last_run
->firstOffset
)
6364 This
->indexCacheLen
--;
6366 last_run
->lastOffset
--;
6370 * Reset the last accessed block cache.
6374 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6376 This
->cachedBlocks
[i
].index
= 0xffffffff;
6377 This
->cachedBlocks
[i
].dirty
= 0;
6384 /******************************************************************************
6385 * BlockChainStream_Enlarge
6387 * Grows this chain in the big block depot.
6389 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6390 ULARGE_INTEGER newSize
)
6392 ULONG blockIndex
, currentBlock
;
6394 ULONG oldNumBlocks
= 0;
6396 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6399 * Empty chain. Create the head.
6401 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6403 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6404 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6406 BLOCK_END_OF_CHAIN
);
6408 if (This
->headOfStreamPlaceHolder
!= 0)
6410 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6414 DirEntry chainEntry
;
6415 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6417 StorageImpl_ReadDirEntry(
6418 This
->parentStorage
,
6419 This
->ownerDirEntry
,
6422 chainEntry
.startingBlock
= blockIndex
;
6424 StorageImpl_WriteDirEntry(
6425 This
->parentStorage
,
6426 This
->ownerDirEntry
,
6430 This
->tailIndex
= blockIndex
;
6431 This
->numBlocks
= 1;
6435 * Figure out how many blocks are needed to contain this stream
6437 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6439 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6443 * Go to the current end of chain
6445 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6447 currentBlock
= blockIndex
;
6449 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6452 currentBlock
= blockIndex
;
6454 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6459 This
->tailIndex
= currentBlock
;
6462 currentBlock
= This
->tailIndex
;
6463 oldNumBlocks
= This
->numBlocks
;
6466 * Add new blocks to the chain
6468 if (oldNumBlocks
< newNumBlocks
)
6470 while (oldNumBlocks
< newNumBlocks
)
6472 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6474 StorageImpl_SetNextBlockInChain(
6475 This
->parentStorage
,
6479 StorageImpl_SetNextBlockInChain(
6480 This
->parentStorage
,
6482 BLOCK_END_OF_CHAIN
);
6484 currentBlock
= blockIndex
;
6488 This
->tailIndex
= blockIndex
;
6489 This
->numBlocks
= newNumBlocks
;
6492 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6498 /******************************************************************************
6499 * BlockChainStream_SetSize
6501 * Sets the size of this stream. The big block depot will be updated.
6502 * The file will grow if we grow the chain.
6504 * TODO: Free the actual blocks in the file when we shrink the chain.
6505 * Currently, the blocks are still in the file. So the file size
6506 * doesn't shrink even if we shrink streams.
6508 BOOL
BlockChainStream_SetSize(
6509 BlockChainStream
* This
,
6510 ULARGE_INTEGER newSize
)
6512 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6514 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6517 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6519 BlockChainStream_Shrink(This
, newSize
);
6523 BlockChainStream_Enlarge(This
, newSize
);
6529 /******************************************************************************
6530 * BlockChainStream_GetSize
6532 * Returns the size of this chain.
6533 * Will return the block count if this chain doesn't have a directory entry.
6535 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6537 DirEntry chainEntry
;
6539 if(This
->headOfStreamPlaceHolder
== NULL
)
6542 * This chain has a directory entry so use the size value from there.
6544 StorageImpl_ReadDirEntry(
6545 This
->parentStorage
,
6546 This
->ownerDirEntry
,
6549 return chainEntry
.size
;
6554 * this chain is a chain that does not have a directory entry, figure out the
6555 * size by making the product number of used blocks times the
6558 ULARGE_INTEGER result
;
6559 result
.u
.HighPart
= 0;
6562 BlockChainStream_GetCount(This
) *
6563 This
->parentStorage
->bigBlockSize
;
6569 /******************************************************************************
6570 ** SmallBlockChainStream implementation
6573 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6574 StorageImpl
* parentStorage
,
6575 ULONG
* headOfStreamPlaceHolder
,
6578 SmallBlockChainStream
* newStream
;
6580 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6582 newStream
->parentStorage
= parentStorage
;
6583 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6584 newStream
->ownerDirEntry
= dirEntry
;
6589 void SmallBlockChainStream_Destroy(
6590 SmallBlockChainStream
* This
)
6592 HeapFree(GetProcessHeap(), 0, This
);
6595 /******************************************************************************
6596 * SmallBlockChainStream_GetHeadOfChain
6598 * Returns the head of this chain of small blocks.
6600 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6601 SmallBlockChainStream
* This
)
6603 DirEntry chainEntry
;
6606 if (This
->headOfStreamPlaceHolder
!= NULL
)
6607 return *(This
->headOfStreamPlaceHolder
);
6609 if (This
->ownerDirEntry
)
6611 hr
= StorageImpl_ReadDirEntry(
6612 This
->parentStorage
,
6613 This
->ownerDirEntry
,
6618 return chainEntry
.startingBlock
;
6623 return BLOCK_END_OF_CHAIN
;
6626 /******************************************************************************
6627 * SmallBlockChainStream_GetNextBlockInChain
6629 * Returns the index of the next small block in this chain.
6632 * - BLOCK_END_OF_CHAIN: end of this chain
6633 * - BLOCK_UNUSED: small block 'blockIndex' is free
6635 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6636 SmallBlockChainStream
* This
,
6638 ULONG
* nextBlockInChain
)
6640 ULARGE_INTEGER offsetOfBlockInDepot
;
6645 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6647 offsetOfBlockInDepot
.u
.HighPart
= 0;
6648 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6651 * Read those bytes in the buffer from the small block file.
6653 res
= BlockChainStream_ReadAt(
6654 This
->parentStorage
->smallBlockDepotChain
,
6655 offsetOfBlockInDepot
,
6660 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6661 res
= STG_E_READFAULT
;
6665 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6672 /******************************************************************************
6673 * SmallBlockChainStream_SetNextBlockInChain
6675 * Writes the index of the next block of the specified block in the small
6677 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6678 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6680 static void SmallBlockChainStream_SetNextBlockInChain(
6681 SmallBlockChainStream
* This
,
6685 ULARGE_INTEGER offsetOfBlockInDepot
;
6689 offsetOfBlockInDepot
.u
.HighPart
= 0;
6690 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6692 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6695 * Read those bytes in the buffer from the small block file.
6697 BlockChainStream_WriteAt(
6698 This
->parentStorage
->smallBlockDepotChain
,
6699 offsetOfBlockInDepot
,
6705 /******************************************************************************
6706 * SmallBlockChainStream_FreeBlock
6708 * Flag small block 'blockIndex' as free in the small block depot.
6710 static void SmallBlockChainStream_FreeBlock(
6711 SmallBlockChainStream
* This
,
6714 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6717 /******************************************************************************
6718 * SmallBlockChainStream_GetNextFreeBlock
6720 * Returns the index of a free small block. The small block depot will be
6721 * enlarged if necessary. The small block chain will also be enlarged if
6724 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6725 SmallBlockChainStream
* This
)
6727 ULARGE_INTEGER offsetOfBlockInDepot
;
6730 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6731 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6733 ULONG smallBlocksPerBigBlock
;
6735 ULONG blocksRequired
;
6736 ULARGE_INTEGER old_size
, size_required
;
6738 offsetOfBlockInDepot
.u
.HighPart
= 0;
6741 * Scan the small block depot for a free block
6743 while (nextBlockIndex
!= BLOCK_UNUSED
)
6745 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6747 res
= BlockChainStream_ReadAt(
6748 This
->parentStorage
->smallBlockDepotChain
,
6749 offsetOfBlockInDepot
,
6755 * If we run out of space for the small block depot, enlarge it
6757 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6759 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6761 if (nextBlockIndex
!= BLOCK_UNUSED
)
6767 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6769 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6770 ULARGE_INTEGER newSize
, offset
;
6773 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6774 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6777 * Initialize all the small blocks to free
6779 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6780 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6781 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6782 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6784 StorageImpl_SaveFileHeader(This
->parentStorage
);
6788 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6790 smallBlocksPerBigBlock
=
6791 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6794 * Verify if we have to allocate big blocks to contain small blocks
6796 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6798 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6800 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6802 if (size_required
.QuadPart
> old_size
.QuadPart
)
6804 BlockChainStream_SetSize(
6805 This
->parentStorage
->smallBlockRootChain
,
6808 StorageImpl_ReadDirEntry(
6809 This
->parentStorage
,
6810 This
->parentStorage
->base
.storageDirEntry
,
6813 rootEntry
.size
= size_required
;
6815 StorageImpl_WriteDirEntry(
6816 This
->parentStorage
,
6817 This
->parentStorage
->base
.storageDirEntry
,
6824 /******************************************************************************
6825 * SmallBlockChainStream_ReadAt
6827 * Reads a specified number of bytes from this chain at the specified offset.
6828 * bytesRead may be NULL.
6829 * Failure will be returned if the specified number of bytes has not been read.
6831 HRESULT
SmallBlockChainStream_ReadAt(
6832 SmallBlockChainStream
* This
,
6833 ULARGE_INTEGER offset
,
6839 ULARGE_INTEGER offsetInBigBlockFile
;
6840 ULONG blockNoInSequence
=
6841 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6843 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6844 ULONG bytesToReadInBuffer
;
6846 ULONG bytesReadFromBigBlockFile
;
6848 ULARGE_INTEGER stream_size
;
6851 * This should never happen on a small block file.
6853 assert(offset
.u
.HighPart
==0);
6857 stream_size
= SmallBlockChainStream_GetSize(This
);
6858 if (stream_size
.QuadPart
> offset
.QuadPart
)
6859 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6864 * Find the first block in the stream that contains part of the buffer.
6866 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6868 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6870 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6873 blockNoInSequence
--;
6877 * Start reading the buffer.
6879 bufferWalker
= buffer
;
6881 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6884 * Calculate how many bytes we can copy from this small block.
6886 bytesToReadInBuffer
=
6887 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6890 * Calculate the offset of the small block in the small block file.
6892 offsetInBigBlockFile
.u
.HighPart
= 0;
6893 offsetInBigBlockFile
.u
.LowPart
=
6894 blockIndex
* This
->parentStorage
->smallBlockSize
;
6896 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6899 * Read those bytes in the buffer from the small block file.
6900 * The small block has already been identified so it shouldn't fail
6901 * unless the file is corrupt.
6903 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6904 offsetInBigBlockFile
,
6905 bytesToReadInBuffer
,
6907 &bytesReadFromBigBlockFile
);
6912 if (!bytesReadFromBigBlockFile
)
6913 return STG_E_DOCFILECORRUPT
;
6916 * Step to the next big block.
6918 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6920 return STG_E_DOCFILECORRUPT
;
6922 bufferWalker
+= bytesReadFromBigBlockFile
;
6923 size
-= bytesReadFromBigBlockFile
;
6924 *bytesRead
+= bytesReadFromBigBlockFile
;
6925 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6931 /******************************************************************************
6932 * SmallBlockChainStream_WriteAt
6934 * Writes the specified number of bytes to this chain at the specified offset.
6935 * Will fail if not all specified number of bytes have been written.
6937 HRESULT
SmallBlockChainStream_WriteAt(
6938 SmallBlockChainStream
* This
,
6939 ULARGE_INTEGER offset
,
6942 ULONG
* bytesWritten
)
6944 ULARGE_INTEGER offsetInBigBlockFile
;
6945 ULONG blockNoInSequence
=
6946 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6948 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6949 ULONG bytesToWriteInBuffer
;
6951 ULONG bytesWrittenToBigBlockFile
;
6952 const BYTE
* bufferWalker
;
6956 * This should never happen on a small block file.
6958 assert(offset
.u
.HighPart
==0);
6961 * Find the first block in the stream that contains part of the buffer.
6963 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6965 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6967 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6968 return STG_E_DOCFILECORRUPT
;
6969 blockNoInSequence
--;
6973 * Start writing the buffer.
6976 bufferWalker
= buffer
;
6977 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6980 * Calculate how many bytes we can copy to this small block.
6982 bytesToWriteInBuffer
=
6983 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6986 * Calculate the offset of the small block in the small block file.
6988 offsetInBigBlockFile
.u
.HighPart
= 0;
6989 offsetInBigBlockFile
.u
.LowPart
=
6990 blockIndex
* This
->parentStorage
->smallBlockSize
;
6992 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6995 * Write those bytes in the buffer to the small block file.
6997 res
= BlockChainStream_WriteAt(
6998 This
->parentStorage
->smallBlockRootChain
,
6999 offsetInBigBlockFile
,
7000 bytesToWriteInBuffer
,
7002 &bytesWrittenToBigBlockFile
);
7007 * Step to the next big block.
7009 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7012 bufferWalker
+= bytesWrittenToBigBlockFile
;
7013 size
-= bytesWrittenToBigBlockFile
;
7014 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7015 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7018 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7021 /******************************************************************************
7022 * SmallBlockChainStream_Shrink
7024 * Shrinks this chain in the small block depot.
7026 static BOOL
SmallBlockChainStream_Shrink(
7027 SmallBlockChainStream
* This
,
7028 ULARGE_INTEGER newSize
)
7030 ULONG blockIndex
, extraBlock
;
7034 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7036 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7039 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7042 * Go to the new end of chain
7044 while (count
< numBlocks
)
7046 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7053 * If the count is 0, we have a special case, the head of the chain was
7058 DirEntry chainEntry
;
7060 StorageImpl_ReadDirEntry(This
->parentStorage
,
7061 This
->ownerDirEntry
,
7064 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7066 StorageImpl_WriteDirEntry(This
->parentStorage
,
7067 This
->ownerDirEntry
,
7071 * We start freeing the chain at the head block.
7073 extraBlock
= blockIndex
;
7077 /* Get the next block before marking the new end */
7078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7082 /* Mark the new end of chain */
7083 SmallBlockChainStream_SetNextBlockInChain(
7086 BLOCK_END_OF_CHAIN
);
7090 * Mark the extra blocks as free
7092 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7094 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7097 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7098 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7099 extraBlock
= blockIndex
;
7105 /******************************************************************************
7106 * SmallBlockChainStream_Enlarge
7108 * Grows this chain in the small block depot.
7110 static BOOL
SmallBlockChainStream_Enlarge(
7111 SmallBlockChainStream
* This
,
7112 ULARGE_INTEGER newSize
)
7114 ULONG blockIndex
, currentBlock
;
7116 ULONG oldNumBlocks
= 0;
7118 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7121 * Empty chain. Create the head.
7123 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7125 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7126 SmallBlockChainStream_SetNextBlockInChain(
7129 BLOCK_END_OF_CHAIN
);
7131 if (This
->headOfStreamPlaceHolder
!= NULL
)
7133 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7137 DirEntry chainEntry
;
7139 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7142 chainEntry
.startingBlock
= blockIndex
;
7144 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7149 currentBlock
= blockIndex
;
7152 * Figure out how many blocks are needed to contain this stream
7154 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7156 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7160 * Go to the current end of chain
7162 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7165 currentBlock
= blockIndex
;
7166 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7171 * Add new blocks to the chain
7173 while (oldNumBlocks
< newNumBlocks
)
7175 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7176 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7178 SmallBlockChainStream_SetNextBlockInChain(
7181 BLOCK_END_OF_CHAIN
);
7183 currentBlock
= blockIndex
;
7190 /******************************************************************************
7191 * SmallBlockChainStream_SetSize
7193 * Sets the size of this stream.
7194 * The file will grow if we grow the chain.
7196 * TODO: Free the actual blocks in the file when we shrink the chain.
7197 * Currently, the blocks are still in the file. So the file size
7198 * doesn't shrink even if we shrink streams.
7200 BOOL
SmallBlockChainStream_SetSize(
7201 SmallBlockChainStream
* This
,
7202 ULARGE_INTEGER newSize
)
7204 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7206 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7209 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7211 SmallBlockChainStream_Shrink(This
, newSize
);
7215 SmallBlockChainStream_Enlarge(This
, newSize
);
7221 /******************************************************************************
7222 * SmallBlockChainStream_GetCount
7224 * Returns the number of small blocks that comprises this chain.
7225 * This is not the size of the stream as the last block may not be full!
7228 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7233 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7235 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7239 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7240 blockIndex
, &blockIndex
)))
7247 /******************************************************************************
7248 * SmallBlockChainStream_GetSize
7250 * Returns the size of this chain.
7252 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7254 DirEntry chainEntry
;
7256 if(This
->headOfStreamPlaceHolder
!= NULL
)
7258 ULARGE_INTEGER result
;
7259 result
.u
.HighPart
= 0;
7261 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7262 This
->parentStorage
->smallBlockSize
;
7267 StorageImpl_ReadDirEntry(
7268 This
->parentStorage
,
7269 This
->ownerDirEntry
,
7272 return chainEntry
.size
;
7275 static HRESULT
create_storagefile(
7279 STGOPTIONS
* pStgOptions
,
7283 StorageBaseImpl
* newStorage
= 0;
7284 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7285 HRESULT hr
= STG_E_INVALIDFLAG
;
7289 DWORD fileAttributes
;
7290 WCHAR tempFileName
[MAX_PATH
];
7293 return STG_E_INVALIDPOINTER
;
7295 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7296 return STG_E_INVALIDPARAMETER
;
7298 /* if no share mode given then DENY_NONE is the default */
7299 if (STGM_SHARE_MODE(grfMode
) == 0)
7300 grfMode
|= STGM_SHARE_DENY_NONE
;
7302 if ( FAILED( validateSTGM(grfMode
) ))
7305 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7306 switch(STGM_ACCESS_MODE(grfMode
))
7309 case STGM_READWRITE
:
7315 /* in direct mode, can only use SHARE_EXCLUSIVE */
7316 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7319 /* but in transacted mode, any share mode is valid */
7322 * Generate a unique name.
7326 WCHAR tempPath
[MAX_PATH
];
7327 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7329 memset(tempPath
, 0, sizeof(tempPath
));
7330 memset(tempFileName
, 0, sizeof(tempFileName
));
7332 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7335 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7336 pwcsName
= tempFileName
;
7339 hr
= STG_E_INSUFFICIENTMEMORY
;
7343 creationMode
= TRUNCATE_EXISTING
;
7347 creationMode
= GetCreationModeFromSTGM(grfMode
);
7351 * Interpret the STGM value grfMode
7353 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7354 accessMode
= GetAccessModeFromSTGM(grfMode
);
7356 if (grfMode
& STGM_DELETEONRELEASE
)
7357 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7359 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7361 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7365 FIXME("Storage share mode not implemented.\n");
7370 hFile
= CreateFileW(pwcsName
,
7378 if (hFile
== INVALID_HANDLE_VALUE
)
7380 if(GetLastError() == ERROR_FILE_EXISTS
)
7381 hr
= STG_E_FILEALREADYEXISTS
;
7388 * Allocate and initialize the new IStorage32object.
7390 hr
= Storage_Construct(
7397 pStgOptions
->ulSectorSize
,
7405 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
7406 IStorage_Release(&newStorage
->IStorage_iface
);
7409 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7414 /******************************************************************************
7415 * StgCreateDocfile [OLE32.@]
7416 * Creates a new compound file storage object
7419 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7420 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7421 * reserved [ ?] unused?, usually 0
7422 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7425 * S_OK if the file was successfully created
7426 * some STG_E_ value if error
7428 * if pwcsName is NULL, create file with new unique name
7429 * the function can returns
7430 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7433 HRESULT WINAPI
StgCreateDocfile(
7437 IStorage
**ppstgOpen
)
7439 STGOPTIONS stgoptions
= {1, 0, 512};
7441 TRACE("(%s, %x, %d, %p)\n",
7442 debugstr_w(pwcsName
), grfMode
,
7443 reserved
, ppstgOpen
);
7446 return STG_E_INVALIDPOINTER
;
7448 return STG_E_INVALIDPARAMETER
;
7450 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7453 /******************************************************************************
7454 * StgCreateStorageEx [OLE32.@]
7456 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7458 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7459 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7461 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7463 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7464 return STG_E_INVALIDPARAMETER
;
7467 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7469 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7470 return STG_E_INVALIDPARAMETER
;
7473 if (stgfmt
== STGFMT_FILE
)
7475 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7476 return STG_E_INVALIDPARAMETER
;
7479 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7481 STGOPTIONS defaultOptions
= {1, 0, 512};
7483 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7484 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7488 ERR("Invalid stgfmt argument\n");
7489 return STG_E_INVALIDPARAMETER
;
7492 /******************************************************************************
7493 * StgCreatePropSetStg [OLE32.@]
7495 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7496 IPropertySetStorage
**propset
)
7498 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
7500 return STG_E_INVALIDPARAMETER
;
7502 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
7505 /******************************************************************************
7506 * StgOpenStorageEx [OLE32.@]
7508 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7510 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7511 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7513 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7515 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7516 return STG_E_INVALIDPARAMETER
;
7522 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7523 return STG_E_INVALIDPARAMETER
;
7525 case STGFMT_STORAGE
:
7528 case STGFMT_DOCFILE
:
7529 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7531 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7532 return STG_E_INVALIDPARAMETER
;
7534 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7538 WARN("STGFMT_ANY assuming storage\n");
7542 return STG_E_INVALIDPARAMETER
;
7545 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7549 /******************************************************************************
7550 * StgOpenStorage [OLE32.@]
7552 HRESULT WINAPI
StgOpenStorage(
7553 const OLECHAR
*pwcsName
,
7554 IStorage
*pstgPriority
,
7558 IStorage
**ppstgOpen
)
7560 StorageBaseImpl
* newStorage
= 0;
7566 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7567 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7568 snbExclude
, reserved
, ppstgOpen
);
7572 hr
= STG_E_INVALIDNAME
;
7578 hr
= STG_E_INVALIDPOINTER
;
7584 hr
= STG_E_INVALIDPARAMETER
;
7588 if (grfMode
& STGM_PRIORITY
)
7590 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7591 return STG_E_INVALIDFLAG
;
7592 if (grfMode
& STGM_DELETEONRELEASE
)
7593 return STG_E_INVALIDFUNCTION
;
7594 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7595 return STG_E_INVALIDFLAG
;
7596 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7597 grfMode
|= STGM_SHARE_DENY_NONE
;
7599 /* STGM_PRIORITY stops other IStorage objects on the same file from
7600 * committing until the STGM_PRIORITY IStorage is closed. it also
7601 * stops non-transacted mode StgOpenStorage calls with write access from
7602 * succeeding. obviously, both of these cannot be achieved through just
7603 * file share flags */
7604 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7608 * Validate the sharing mode
7610 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7611 switch(STGM_SHARE_MODE(grfMode
))
7613 case STGM_SHARE_EXCLUSIVE
:
7614 case STGM_SHARE_DENY_WRITE
:
7617 hr
= STG_E_INVALIDFLAG
;
7621 if ( FAILED( validateSTGM(grfMode
) ) ||
7622 (grfMode
&STGM_CREATE
))
7624 hr
= STG_E_INVALIDFLAG
;
7628 /* shared reading requires transacted mode */
7629 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7630 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7631 !(grfMode
&STGM_TRANSACTED
) )
7633 hr
= STG_E_INVALIDFLAG
;
7638 * Interpret the STGM value grfMode
7640 shareMode
= GetShareModeFromSTGM(grfMode
);
7641 accessMode
= GetAccessModeFromSTGM(grfMode
);
7645 hFile
= CreateFileW( pwcsName
,
7650 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7653 if (hFile
==INVALID_HANDLE_VALUE
)
7655 DWORD last_error
= GetLastError();
7661 case ERROR_FILE_NOT_FOUND
:
7662 hr
= STG_E_FILENOTFOUND
;
7665 case ERROR_PATH_NOT_FOUND
:
7666 hr
= STG_E_PATHNOTFOUND
;
7669 case ERROR_ACCESS_DENIED
:
7670 case ERROR_WRITE_PROTECT
:
7671 hr
= STG_E_ACCESSDENIED
;
7674 case ERROR_SHARING_VIOLATION
:
7675 hr
= STG_E_SHAREVIOLATION
;
7686 * Refuse to open the file if it's too small to be a structured storage file
7687 * FIXME: verify the file when reading instead of here
7689 if (GetFileSize(hFile
, NULL
) < 0x100)
7692 hr
= STG_E_FILEALREADYEXISTS
;
7697 * Allocate and initialize the new IStorage32object.
7699 hr
= Storage_Construct(
7712 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7714 if(hr
== STG_E_INVALIDHEADER
)
7715 hr
= STG_E_FILEALREADYEXISTS
;
7719 *ppstgOpen
= &newStorage
->IStorage_iface
;
7722 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7726 /******************************************************************************
7727 * StgCreateDocfileOnILockBytes [OLE32.@]
7729 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7733 IStorage
** ppstgOpen
)
7735 StorageBaseImpl
* newStorage
= 0;
7738 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7739 return STG_E_INVALIDPOINTER
;
7742 * Allocate and initialize the new IStorage object.
7744 hr
= Storage_Construct(
7759 *ppstgOpen
= &newStorage
->IStorage_iface
;
7764 /******************************************************************************
7765 * StgOpenStorageOnILockBytes [OLE32.@]
7767 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7769 IStorage
*pstgPriority
,
7773 IStorage
**ppstgOpen
)
7775 StorageBaseImpl
* newStorage
= 0;
7778 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7779 return STG_E_INVALIDPOINTER
;
7781 if ( FAILED( validateSTGM(grfMode
) ))
7782 return STG_E_INVALIDFLAG
;
7787 * Allocate and initialize the new IStorage object.
7789 hr
= Storage_Construct(
7804 *ppstgOpen
= &newStorage
->IStorage_iface
;
7809 /******************************************************************************
7810 * StgSetTimes [ole32.@]
7811 * StgSetTimes [OLE32.@]
7815 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7816 FILETIME
const *patime
, FILETIME
const *pmtime
)
7818 IStorage
*stg
= NULL
;
7821 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7823 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7827 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7828 IStorage_Release(stg
);
7834 /******************************************************************************
7835 * StgIsStorageILockBytes [OLE32.@]
7837 * Determines if the ILockBytes contains a storage object.
7839 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7841 BYTE sig
[sizeof(STORAGE_magic
)];
7842 ULARGE_INTEGER offset
;
7845 offset
.u
.HighPart
= 0;
7846 offset
.u
.LowPart
= 0;
7848 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7850 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7856 /******************************************************************************
7857 * WriteClassStg [OLE32.@]
7859 * This method will store the specified CLSID in the specified storage object
7861 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7864 return E_INVALIDARG
;
7867 return STG_E_INVALIDPOINTER
;
7869 return IStorage_SetClass(pStg
, rclsid
);
7872 /***********************************************************************
7873 * ReadClassStg (OLE32.@)
7875 * This method reads the CLSID previously written to a storage object with
7876 * the WriteClassStg.
7879 * pstg [I] IStorage pointer
7880 * pclsid [O] Pointer to where the CLSID is written
7884 * Failure: HRESULT code.
7886 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7891 TRACE("(%p, %p)\n", pstg
, pclsid
);
7893 if(!pstg
|| !pclsid
)
7894 return E_INVALIDARG
;
7897 * read a STATSTG structure (contains the clsid) from the storage
7899 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7902 *pclsid
=pstatstg
.clsid
;
7907 /***********************************************************************
7908 * OleLoadFromStream (OLE32.@)
7910 * This function loads an object from stream
7912 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7916 LPPERSISTSTREAM xstm
;
7918 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7920 res
=ReadClassStm(pStm
,&clsid
);
7923 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7926 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7928 IUnknown_Release((IUnknown
*)*ppvObj
);
7931 res
=IPersistStream_Load(xstm
,pStm
);
7932 IPersistStream_Release(xstm
);
7933 /* FIXME: all refcounts ok at this point? I think they should be:
7936 * xstm : 0 (released)
7941 /***********************************************************************
7942 * OleSaveToStream (OLE32.@)
7944 * This function saves an object with the IPersistStream interface on it
7945 * to the specified stream.
7947 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7953 TRACE("(%p,%p)\n",pPStm
,pStm
);
7955 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7957 if (SUCCEEDED(res
)){
7959 res
=WriteClassStm(pStm
,&clsid
);
7963 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7966 TRACE("Finished Save\n");
7970 /****************************************************************************
7971 * This method validate a STGM parameter that can contain the values below
7973 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7974 * The stgm values contained in 0xffff0000 are bitmasks.
7976 * STGM_DIRECT 0x00000000
7977 * STGM_TRANSACTED 0x00010000
7978 * STGM_SIMPLE 0x08000000
7980 * STGM_READ 0x00000000
7981 * STGM_WRITE 0x00000001
7982 * STGM_READWRITE 0x00000002
7984 * STGM_SHARE_DENY_NONE 0x00000040
7985 * STGM_SHARE_DENY_READ 0x00000030
7986 * STGM_SHARE_DENY_WRITE 0x00000020
7987 * STGM_SHARE_EXCLUSIVE 0x00000010
7989 * STGM_PRIORITY 0x00040000
7990 * STGM_DELETEONRELEASE 0x04000000
7992 * STGM_CREATE 0x00001000
7993 * STGM_CONVERT 0x00020000
7994 * STGM_FAILIFTHERE 0x00000000
7996 * STGM_NOSCRATCH 0x00100000
7997 * STGM_NOSNAPSHOT 0x00200000
7999 static HRESULT
validateSTGM(DWORD stgm
)
8001 DWORD access
= STGM_ACCESS_MODE(stgm
);
8002 DWORD share
= STGM_SHARE_MODE(stgm
);
8003 DWORD create
= STGM_CREATE_MODE(stgm
);
8005 if (stgm
&~STGM_KNOWN_FLAGS
)
8007 ERR("unknown flags %08x\n", stgm
);
8015 case STGM_READWRITE
:
8023 case STGM_SHARE_DENY_NONE
:
8024 case STGM_SHARE_DENY_READ
:
8025 case STGM_SHARE_DENY_WRITE
:
8026 case STGM_SHARE_EXCLUSIVE
:
8035 case STGM_FAILIFTHERE
:
8042 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8044 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8048 * STGM_CREATE | STGM_CONVERT
8049 * if both are false, STGM_FAILIFTHERE is set to TRUE
8051 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8055 * STGM_NOSCRATCH requires STGM_TRANSACTED
8057 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8061 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8062 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8064 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8065 (!(stgm
& STGM_TRANSACTED
) ||
8066 share
== STGM_SHARE_EXCLUSIVE
||
8067 share
== STGM_SHARE_DENY_WRITE
) )
8073 /****************************************************************************
8074 * GetShareModeFromSTGM
8076 * This method will return a share mode flag from a STGM value.
8077 * The STGM value is assumed valid.
8079 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8081 switch (STGM_SHARE_MODE(stgm
))
8083 case STGM_SHARE_DENY_NONE
:
8084 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8085 case STGM_SHARE_DENY_READ
:
8086 return FILE_SHARE_WRITE
;
8087 case STGM_SHARE_DENY_WRITE
:
8088 return FILE_SHARE_READ
;
8089 case STGM_SHARE_EXCLUSIVE
:
8092 ERR("Invalid share mode!\n");
8097 /****************************************************************************
8098 * GetAccessModeFromSTGM
8100 * This method will return an access mode flag from a STGM value.
8101 * The STGM value is assumed valid.
8103 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8105 switch (STGM_ACCESS_MODE(stgm
))
8108 return GENERIC_READ
;
8110 case STGM_READWRITE
:
8111 return GENERIC_READ
| GENERIC_WRITE
;
8113 ERR("Invalid access mode!\n");
8118 /****************************************************************************
8119 * GetCreationModeFromSTGM
8121 * This method will return a creation mode flag from a STGM value.
8122 * The STGM value is assumed valid.
8124 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8126 switch(STGM_CREATE_MODE(stgm
))
8129 return CREATE_ALWAYS
;
8131 FIXME("STGM_CONVERT not implemented!\n");
8133 case STGM_FAILIFTHERE
:
8136 ERR("Invalid create mode!\n");
8142 /*************************************************************************
8143 * OLECONVERT_LoadOLE10 [Internal]
8145 * Loads the OLE10 STREAM to memory
8148 * pOleStream [I] The OLESTREAM
8149 * pData [I] Data Structure for the OLESTREAM Data
8153 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8154 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8157 * This function is used by OleConvertOLESTREAMToIStorage only.
8159 * Memory allocated for pData must be freed by the caller
8161 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8164 HRESULT hRes
= S_OK
;
8168 pData
->pData
= NULL
;
8169 pData
->pstrOleObjFileName
= NULL
;
8171 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8174 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8175 if(dwSize
!= sizeof(pData
->dwOleID
))
8177 hRes
= CONVERT10_E_OLESTREAM_GET
;
8179 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8181 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8192 /* Get the TypeID... more info needed for this field */
8193 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8194 if(dwSize
!= sizeof(pData
->dwTypeID
))
8196 hRes
= CONVERT10_E_OLESTREAM_GET
;
8201 if(pData
->dwTypeID
!= 0)
8203 /* Get the length of the OleTypeName */
8204 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8205 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8207 hRes
= CONVERT10_E_OLESTREAM_GET
;
8212 if(pData
->dwOleTypeNameLength
> 0)
8214 /* Get the OleTypeName */
8215 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8216 if(dwSize
!= pData
->dwOleTypeNameLength
)
8218 hRes
= CONVERT10_E_OLESTREAM_GET
;
8224 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8225 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8227 hRes
= CONVERT10_E_OLESTREAM_GET
;
8231 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8232 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8233 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8234 if(pData
->pstrOleObjFileName
)
8236 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8237 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8239 hRes
= CONVERT10_E_OLESTREAM_GET
;
8243 hRes
= CONVERT10_E_OLESTREAM_GET
;
8248 /* Get the Width of the Metafile */
8249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8250 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8252 hRes
= CONVERT10_E_OLESTREAM_GET
;
8256 /* Get the Height of the Metafile */
8257 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8258 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8260 hRes
= CONVERT10_E_OLESTREAM_GET
;
8266 /* Get the Length of the Data */
8267 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8268 if(dwSize
!= sizeof(pData
->dwDataLength
))
8270 hRes
= CONVERT10_E_OLESTREAM_GET
;
8274 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8276 if(!bStrem1
) /* if it is a second OLE stream data */
8278 pData
->dwDataLength
-= 8;
8279 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8280 if(dwSize
!= sizeof(pData
->strUnknown
))
8282 hRes
= CONVERT10_E_OLESTREAM_GET
;
8288 if(pData
->dwDataLength
> 0)
8290 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8292 /* Get Data (ex. IStorage, Metafile, or BMP) */
8295 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8296 if(dwSize
!= pData
->dwDataLength
)
8298 hRes
= CONVERT10_E_OLESTREAM_GET
;
8303 hRes
= CONVERT10_E_OLESTREAM_GET
;
8312 /*************************************************************************
8313 * OLECONVERT_SaveOLE10 [Internal]
8315 * Saves the OLE10 STREAM From memory
8318 * pData [I] Data Structure for the OLESTREAM Data
8319 * pOleStream [I] The OLESTREAM to save
8323 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8326 * This function is used by OleConvertIStorageToOLESTREAM only.
8329 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8332 HRESULT hRes
= S_OK
;
8336 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8337 if(dwSize
!= sizeof(pData
->dwOleID
))
8339 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8344 /* Set the TypeID */
8345 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8346 if(dwSize
!= sizeof(pData
->dwTypeID
))
8348 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8352 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8354 /* Set the Length of the OleTypeName */
8355 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8356 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8358 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8363 if(pData
->dwOleTypeNameLength
> 0)
8365 /* Set the OleTypeName */
8366 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8367 if(dwSize
!= pData
->dwOleTypeNameLength
)
8369 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8376 /* Set the width of the Metafile */
8377 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8378 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8380 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8386 /* Set the height of the Metafile */
8387 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8388 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8390 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8396 /* Set the length of the Data */
8397 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8398 if(dwSize
!= sizeof(pData
->dwDataLength
))
8400 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8406 if(pData
->dwDataLength
> 0)
8408 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8409 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8410 if(dwSize
!= pData
->dwDataLength
)
8412 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8420 /*************************************************************************
8421 * OLECONVERT_GetOLE20FromOLE10[Internal]
8423 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8424 * opens it, and copies the content to the dest IStorage for
8425 * OleConvertOLESTREAMToIStorage
8429 * pDestStorage [I] The IStorage to copy the data to
8430 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8431 * nBufferLength [I] The size of the buffer
8440 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8444 IStorage
*pTempStorage
;
8445 DWORD dwNumOfBytesWritten
;
8446 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8447 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8449 /* Create a temp File */
8450 GetTempPathW(MAX_PATH
, wstrTempDir
);
8451 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8452 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8454 if(hFile
!= INVALID_HANDLE_VALUE
)
8456 /* Write IStorage Data to File */
8457 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8460 /* Open and copy temp storage to the Dest Storage */
8461 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8464 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8465 IStorage_Release(pTempStorage
);
8467 DeleteFileW(wstrTempFile
);
8472 /*************************************************************************
8473 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8475 * Saves the OLE10 STREAM From memory
8478 * pStorage [I] The Src IStorage to copy
8479 * pData [I] The Dest Memory to write to.
8482 * The size in bytes allocated for pData
8485 * Memory allocated for pData must be freed by the caller
8487 * Used by OleConvertIStorageToOLESTREAM only.
8490 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8494 DWORD nDataLength
= 0;
8495 IStorage
*pTempStorage
;
8496 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8497 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8501 /* Create temp Storage */
8502 GetTempPathW(MAX_PATH
, wstrTempDir
);
8503 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8504 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8508 /* Copy Src Storage to the Temp Storage */
8509 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8510 IStorage_Release(pTempStorage
);
8512 /* Open Temp Storage as a file and copy to memory */
8513 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8514 if(hFile
!= INVALID_HANDLE_VALUE
)
8516 nDataLength
= GetFileSize(hFile
, NULL
);
8517 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8518 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8521 DeleteFileW(wstrTempFile
);
8526 /*************************************************************************
8527 * OLECONVERT_CreateOleStream [Internal]
8529 * Creates the "\001OLE" stream in the IStorage if necessary.
8532 * pStorage [I] Dest storage to create the stream in
8538 * This function is used by OleConvertOLESTREAMToIStorage only.
8540 * This stream is still unknown, MS Word seems to have extra data
8541 * but since the data is stored in the OLESTREAM there should be
8542 * no need to recreate the stream. If the stream is manually
8543 * deleted it will create it with this default data.
8546 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8550 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8551 BYTE pOleStreamHeader
[] =
8553 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8554 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8555 0x00, 0x00, 0x00, 0x00
8558 /* Create stream if not present */
8559 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8560 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8564 /* Write default Data */
8565 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8566 IStream_Release(pStream
);
8570 /* write a string to a stream, preceded by its length */
8571 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8578 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8579 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8584 str
= CoTaskMemAlloc( len
);
8585 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8586 r
= IStream_Write( stm
, str
, len
, NULL
);
8587 CoTaskMemFree( str
);
8591 /* read a string preceded by its length from a stream */
8592 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8595 DWORD len
, count
= 0;
8599 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8602 if( count
!= sizeof(len
) )
8603 return E_OUTOFMEMORY
;
8605 TRACE("%d bytes\n",len
);
8607 str
= CoTaskMemAlloc( len
);
8609 return E_OUTOFMEMORY
;
8611 r
= IStream_Read( stm
, str
, len
, &count
);
8616 CoTaskMemFree( str
);
8617 return E_OUTOFMEMORY
;
8620 TRACE("Read string %s\n",debugstr_an(str
,len
));
8622 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8623 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8625 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8626 CoTaskMemFree( str
);
8634 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8635 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8639 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8641 static const BYTE unknown1
[12] =
8642 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8643 0xFF, 0xFF, 0xFF, 0xFF};
8644 static const BYTE unknown2
[16] =
8645 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8646 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8648 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8649 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8650 debugstr_w(szProgIDName
));
8652 /* Create a CompObj stream */
8653 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8654 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8658 /* Write CompObj Structure to stream */
8659 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8661 if( SUCCEEDED( r
) )
8662 r
= WriteClassStm( pstm
, clsid
);
8664 if( SUCCEEDED( r
) )
8665 r
= STREAM_WriteString( pstm
, lpszUserType
);
8666 if( SUCCEEDED( r
) )
8667 r
= STREAM_WriteString( pstm
, szClipName
);
8668 if( SUCCEEDED( r
) )
8669 r
= STREAM_WriteString( pstm
, szProgIDName
);
8670 if( SUCCEEDED( r
) )
8671 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8673 IStream_Release( pstm
);
8678 /***********************************************************************
8679 * WriteFmtUserTypeStg (OLE32.@)
8681 HRESULT WINAPI
WriteFmtUserTypeStg(
8682 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8685 WCHAR szwClipName
[0x40];
8686 CLSID clsid
= CLSID_NULL
;
8687 LPWSTR wstrProgID
= NULL
;
8690 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8692 /* get the clipboard format name */
8693 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8696 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8698 /* FIXME: There's room to save a CLSID and its ProgID, but
8699 the CLSID is not looked up in the registry and in all the
8700 tests I wrote it was CLSID_NULL. Where does it come from?
8703 /* get the real program ID. This may fail, but that's fine */
8704 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8706 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8708 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8709 lpszUserType
, szwClipName
, wstrProgID
);
8711 CoTaskMemFree(wstrProgID
);
8717 /******************************************************************************
8718 * ReadFmtUserTypeStg [OLE32.@]
8720 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8724 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8725 unsigned char unknown1
[12];
8726 unsigned char unknown2
[16];
8728 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8731 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8733 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8734 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8737 WARN("Failed to open stream r = %08x\n", r
);
8741 /* read the various parts of the structure */
8742 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8743 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8745 r
= ReadClassStm( stm
, &clsid
);
8749 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8753 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8757 r
= STREAM_ReadString( stm
, &szProgIDName
);
8761 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8762 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8765 /* ok, success... now we just need to store what we found */
8767 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8768 CoTaskMemFree( szOleTypeName
);
8770 if( lplpszUserType
)
8771 *lplpszUserType
= szCLSIDName
;
8772 CoTaskMemFree( szProgIDName
);
8775 IStream_Release( stm
);
8781 /*************************************************************************
8782 * OLECONVERT_CreateCompObjStream [Internal]
8784 * Creates a "\001CompObj" is the destination IStorage if necessary.
8787 * pStorage [I] The dest IStorage to create the CompObj Stream
8789 * strOleTypeName [I] The ProgID
8793 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8796 * This function is used by OleConvertOLESTREAMToIStorage only.
8798 * The stream data is stored in the OLESTREAM and there should be
8799 * no need to recreate the stream. If the stream is manually
8800 * deleted it will attempt to create it by querying the registry.
8804 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8807 HRESULT hStorageRes
, hRes
= S_OK
;
8808 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8809 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8810 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8812 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8813 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8815 /* Initialize the CompObj structure */
8816 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8817 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8818 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8821 /* Create a CompObj stream if it doesn't exist */
8822 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8823 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8824 if(hStorageRes
== S_OK
)
8826 /* copy the OleTypeName to the compobj struct */
8827 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8828 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8830 /* copy the OleTypeName to the compobj struct */
8831 /* Note: in the test made, these were Identical */
8832 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8833 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8836 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8837 bufferW
, OLESTREAM_MAX_STR_LEN
);
8838 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8844 /* Get the CLSID Default Name from the Registry */
8845 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8846 if(hErr
== ERROR_SUCCESS
)
8848 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8849 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8850 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8851 if(hErr
== ERROR_SUCCESS
)
8853 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8859 /* Write CompObj Structure to stream */
8860 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8862 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8864 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8865 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8867 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8869 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8870 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8872 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8874 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8875 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8877 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8879 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8880 IStream_Release(pStream
);
8886 /*************************************************************************
8887 * OLECONVERT_CreateOlePresStream[Internal]
8889 * Creates the "\002OlePres000" Stream with the Metafile data
8892 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8893 * dwExtentX [I] Width of the Metafile
8894 * dwExtentY [I] Height of the Metafile
8895 * pData [I] Metafile data
8896 * dwDataLength [I] Size of the Metafile data
8900 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8903 * This function is used by OleConvertOLESTREAMToIStorage only.
8906 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8910 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8911 BYTE pOlePresStreamHeader
[] =
8913 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8914 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8915 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8916 0x00, 0x00, 0x00, 0x00
8919 BYTE pOlePresStreamHeaderEmpty
[] =
8921 0x00, 0x00, 0x00, 0x00,
8922 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8923 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8924 0x00, 0x00, 0x00, 0x00
8927 /* Create the OlePres000 Stream */
8928 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8929 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8934 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8936 memset(&OlePres
, 0, sizeof(OlePres
));
8937 /* Do we have any metafile data to save */
8938 if(dwDataLength
> 0)
8940 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8941 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8945 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8946 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8948 /* Set width and height of the metafile */
8949 OlePres
.dwExtentX
= dwExtentX
;
8950 OlePres
.dwExtentY
= -dwExtentY
;
8952 /* Set Data and Length */
8953 if(dwDataLength
> sizeof(METAFILEPICT16
))
8955 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8956 OlePres
.pData
= &(pData
[8]);
8958 /* Save OlePres000 Data to Stream */
8959 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8960 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8961 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8962 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8963 if(OlePres
.dwSize
> 0)
8965 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8967 IStream_Release(pStream
);
8971 /*************************************************************************
8972 * OLECONVERT_CreateOle10NativeStream [Internal]
8974 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8977 * pStorage [I] Dest storage to create the stream in
8978 * pData [I] Ole10 Native Data (ex. bmp)
8979 * dwDataLength [I] Size of the Ole10 Native Data
8985 * This function is used by OleConvertOLESTREAMToIStorage only.
8987 * Might need to verify the data and return appropriate error message
8990 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8994 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8996 /* Create the Ole10Native Stream */
8997 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8998 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9002 /* Write info to stream */
9003 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9004 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9005 IStream_Release(pStream
);
9010 /*************************************************************************
9011 * OLECONVERT_GetOLE10ProgID [Internal]
9013 * Finds the ProgID (or OleTypeID) from the IStorage
9016 * pStorage [I] The Src IStorage to get the ProgID
9017 * strProgID [I] the ProgID string to get
9018 * dwSize [I] the size of the string
9022 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9025 * This function is used by OleConvertIStorageToOLESTREAM only.
9029 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9033 LARGE_INTEGER iSeekPos
;
9034 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9035 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9037 /* Open the CompObj Stream */
9038 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9039 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9043 /*Get the OleType from the CompObj Stream */
9044 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9045 iSeekPos
.u
.HighPart
= 0;
9047 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9048 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9049 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9050 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9051 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9052 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9053 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9055 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9058 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9060 IStream_Release(pStream
);
9065 LPOLESTR wstrProgID
;
9067 /* Get the OleType from the registry */
9068 REFCLSID clsid
= &(stat
.clsid
);
9069 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9070 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9073 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9080 /*************************************************************************
9081 * OLECONVERT_GetOle10PresData [Internal]
9083 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9086 * pStorage [I] Src IStroage
9087 * pOleStream [I] Dest OleStream Mem Struct
9093 * This function is used by OleConvertIStorageToOLESTREAM only.
9095 * Memory allocated for pData must be freed by the caller
9099 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9104 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9106 /* Initialize Default data for OLESTREAM */
9107 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9108 pOleStreamData
[0].dwTypeID
= 2;
9109 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9110 pOleStreamData
[1].dwTypeID
= 0;
9111 pOleStreamData
[0].dwMetaFileWidth
= 0;
9112 pOleStreamData
[0].dwMetaFileHeight
= 0;
9113 pOleStreamData
[0].pData
= NULL
;
9114 pOleStreamData
[1].pData
= NULL
;
9116 /* Open Ole10Native Stream */
9117 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9118 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9122 /* Read Size and Data */
9123 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9124 if(pOleStreamData
->dwDataLength
> 0)
9126 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9127 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9129 IStream_Release(pStream
);
9135 /*************************************************************************
9136 * OLECONVERT_GetOle20PresData[Internal]
9138 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9141 * pStorage [I] Src IStroage
9142 * pOleStreamData [I] Dest OleStream Mem Struct
9148 * This function is used by OleConvertIStorageToOLESTREAM only.
9150 * Memory allocated for pData must be freed by the caller
9152 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9156 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9157 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9159 /* Initialize Default data for OLESTREAM */
9160 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9161 pOleStreamData
[0].dwTypeID
= 2;
9162 pOleStreamData
[0].dwMetaFileWidth
= 0;
9163 pOleStreamData
[0].dwMetaFileHeight
= 0;
9164 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9165 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9166 pOleStreamData
[1].dwTypeID
= 0;
9167 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9168 pOleStreamData
[1].strOleTypeName
[0] = 0;
9169 pOleStreamData
[1].dwMetaFileWidth
= 0;
9170 pOleStreamData
[1].dwMetaFileHeight
= 0;
9171 pOleStreamData
[1].pData
= NULL
;
9172 pOleStreamData
[1].dwDataLength
= 0;
9175 /* Open OlePress000 stream */
9176 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9177 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9180 LARGE_INTEGER iSeekPos
;
9181 METAFILEPICT16 MetaFilePict
;
9182 static const char strMetafilePictName
[] = "METAFILEPICT";
9184 /* Set the TypeID for a Metafile */
9185 pOleStreamData
[1].dwTypeID
= 5;
9187 /* Set the OleTypeName to Metafile */
9188 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9189 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9191 iSeekPos
.u
.HighPart
= 0;
9192 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9194 /* Get Presentation Data */
9195 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9196 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9197 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9198 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9200 /*Set width and Height */
9201 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9202 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9203 if(olePress
.dwSize
> 0)
9206 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9208 /* Set MetaFilePict struct */
9209 MetaFilePict
.mm
= 8;
9210 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9211 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9212 MetaFilePict
.hMF
= 0;
9214 /* Get Metafile Data */
9215 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9216 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9217 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9219 IStream_Release(pStream
);
9223 /*************************************************************************
9224 * OleConvertOLESTREAMToIStorage [OLE32.@]
9229 * DVTARGETDEVICE parameter is not handled
9230 * Still unsure of some mem fields for OLE 10 Stream
9231 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9232 * and "\001OLE" streams
9235 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9236 LPOLESTREAM pOleStream
,
9238 const DVTARGETDEVICE
* ptd
)
9242 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9244 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9246 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9250 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9253 if(pstg
== NULL
|| pOleStream
== NULL
)
9255 hRes
= E_INVALIDARG
;
9260 /* Load the OLESTREAM to Memory */
9261 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9266 /* Load the OLESTREAM to Memory (part 2)*/
9267 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9273 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9275 /* Do we have the IStorage Data in the OLESTREAM */
9276 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9278 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9279 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9283 /* It must be an original OLE 1.0 source */
9284 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9289 /* It must be an original OLE 1.0 source */
9290 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9293 /* Create CompObj Stream if necessary */
9294 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9297 /*Create the Ole Stream if necessary */
9298 OLECONVERT_CreateOleStream(pstg
);
9303 /* Free allocated memory */
9304 for(i
=0; i
< 2; i
++)
9306 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9307 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9308 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9313 /*************************************************************************
9314 * OleConvertIStorageToOLESTREAM [OLE32.@]
9321 * Still unsure of some mem fields for OLE 10 Stream
9322 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9323 * and "\001OLE" streams.
9326 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9328 LPOLESTREAM pOleStream
)
9331 HRESULT hRes
= S_OK
;
9333 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9334 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9336 TRACE("%p %p\n", pstg
, pOleStream
);
9338 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9340 if(pstg
== NULL
|| pOleStream
== NULL
)
9342 hRes
= E_INVALIDARG
;
9346 /* Get the ProgID */
9347 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9348 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9352 /* Was it originally Ole10 */
9353 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9356 IStream_Release(pStream
);
9357 /* Get Presentation Data for Ole10Native */
9358 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9362 /* Get Presentation Data (OLE20) */
9363 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9366 /* Save OLESTREAM */
9367 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9370 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9375 /* Free allocated memory */
9376 for(i
=0; i
< 2; i
++)
9378 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9384 enum stream_1ole_flags
{
9385 OleStream_LinkedObject
= 0x00000001,
9386 OleStream_Convert
= 0x00000100
9389 /***********************************************************************
9390 * GetConvertStg (OLE32.@)
9392 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
9394 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9395 static const DWORD version_magic
= 0x02000001;
9403 if (!stg
) return E_INVALIDARG
;
9405 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9406 if (FAILED(hr
)) return hr
;
9409 hr
= IStream_Read(stream
, header
, sizeof(header
), &len
);
9410 IStream_Release(stream
);
9411 if (FAILED(hr
)) return hr
;
9413 if (header
[0] != version_magic
)
9415 ERR("got wrong version magic for \1Ole stream, 0x%08x\n", header
[0]);
9419 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
9422 /******************************************************************************
9423 * StgIsStorageFile [OLE32.@]
9424 * Verify if the file contains a storage object
9430 * S_OK if file has magic bytes as a storage object
9431 * S_FALSE if file is not storage
9434 StgIsStorageFile(LPCOLESTR fn
)
9440 TRACE("%s\n", debugstr_w(fn
));
9441 hf
= CreateFileW(fn
, GENERIC_READ
,
9442 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9443 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9445 if (hf
== INVALID_HANDLE_VALUE
)
9446 return STG_E_FILENOTFOUND
;
9448 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9450 WARN(" unable to read file\n");
9457 if (bytes_read
!= 8) {
9458 TRACE(" too short\n");
9462 if (!memcmp(magic
,STORAGE_magic
,8)) {
9467 TRACE(" -> Invalid header.\n");
9471 /***********************************************************************
9472 * WriteClassStm (OLE32.@)
9474 * Writes a CLSID to a stream.
9477 * pStm [I] Stream to write to.
9478 * rclsid [I] CLSID to write.
9482 * Failure: HRESULT code.
9484 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9486 TRACE("(%p,%p)\n",pStm
,rclsid
);
9488 if (!pStm
|| !rclsid
)
9489 return E_INVALIDARG
;
9491 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9494 /***********************************************************************
9495 * ReadClassStm (OLE32.@)
9497 * Reads a CLSID from a stream.
9500 * pStm [I] Stream to read from.
9501 * rclsid [O] CLSID to read.
9505 * Failure: HRESULT code.
9507 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9512 TRACE("(%p,%p)\n",pStm
,pclsid
);
9514 if (!pStm
|| !pclsid
)
9515 return E_INVALIDARG
;
9517 /* clear the output args */
9518 *pclsid
= CLSID_NULL
;
9520 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9525 if (nbByte
!= sizeof(CLSID
))
9526 return STG_E_READFAULT
;