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"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
71 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
74 /****************************************************************************
75 * Storage32InternalImpl definitions.
77 * Definition of the implementation structure for the IStorage32 interface.
78 * This one implements the IStorage32 interface for storage that are
79 * inside another storage.
81 struct StorageInternalImpl
83 struct StorageBaseImpl base
;
86 * Entry in the parent's stream tracking list
88 struct list ParentListEntry
;
90 StorageBaseImpl
*parentStorage
;
92 typedef struct StorageInternalImpl StorageInternalImpl
;
94 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
95 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
97 /* Method definitions for the Storage32InternalImpl class. */
98 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
99 DWORD openFlags
, DirRef storageDirEntry
);
100 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
101 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
102 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
103 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
104 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
105 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
106 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
107 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
109 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
110 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
111 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
112 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
113 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
115 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
116 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
117 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
119 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
120 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
121 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
122 ULONG blockIndex
, ULONG offset
, DWORD value
);
123 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
124 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
126 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
127 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
129 typedef struct TransactedDirEntry
131 /* If applicable, a reference to the original DirEntry in the transacted
132 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
133 DirRef transactedParentEntry
;
135 /* True if this entry is being used. */
138 /* True if data is up to date. */
141 /* True if this entry has been modified. */
144 /* True if this entry's stream has been modified. */
147 /* True if this entry has been deleted in the transacted storage, but the
148 * delete has not yet been committed. */
151 /* If this entry's stream has been modified, a reference to where the stream
152 * is stored in the snapshot file. */
155 /* This directory entry's data, including any changes that have been made. */
158 /* A reference to the parent of this node. This is only valid while we are
159 * committing changes. */
162 /* A reference to a newly-created entry in the transacted parent. This is
163 * always equal to transactedParentEntry except when committing changes. */
164 DirRef newTransactedParentEntry
;
165 } TransactedDirEntry
;
167 /****************************************************************************
168 * Transacted storage object.
170 typedef struct TransactedSnapshotImpl
172 struct StorageBaseImpl base
;
175 * Modified streams are temporarily saved to the scratch file.
177 StorageBaseImpl
*scratch
;
179 /* The directory structure is kept here, so that we can track how these
180 * entries relate to those in the parent storage. */
181 TransactedDirEntry
*entries
;
183 ULONG firstFreeEntry
;
186 * Changes are committed to the transacted parent.
188 StorageBaseImpl
*transactedParent
;
189 } TransactedSnapshotImpl
;
191 /* Generic function to create a transacted wrapper for a direct storage object. */
192 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
194 /* OLESTREAM memory structure to use for Get and Put Routines */
195 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
200 DWORD dwOleTypeNameLength
;
201 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
202 CHAR
*pstrOleObjFileName
;
203 DWORD dwOleObjFileNameLength
;
204 DWORD dwMetaFileWidth
;
205 DWORD dwMetaFileHeight
;
206 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
209 }OLECONVERT_OLESTREAM_DATA
;
211 /* CompObj Stream structure */
212 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
217 DWORD dwCLSIDNameLength
;
218 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
219 DWORD dwOleTypeNameLength
;
220 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
221 DWORD dwProgIDNameLength
;
222 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
224 }OLECONVERT_ISTORAGE_COMPOBJ
;
227 /* Ole Presentation Stream structure */
228 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
236 }OLECONVERT_ISTORAGE_OLEPRES
;
240 /***********************************************************************
241 * Forward declaration of internal functions used by the method DestroyElement
243 static HRESULT
deleteStorageContents(
244 StorageBaseImpl
*parentStorage
,
245 DirRef indexToDelete
,
246 DirEntry entryDataToDelete
);
248 static HRESULT
deleteStreamContents(
249 StorageBaseImpl
*parentStorage
,
250 DirRef indexToDelete
,
251 DirEntry entryDataToDelete
);
253 static HRESULT
removeFromTree(
254 StorageBaseImpl
*This
,
255 DirRef parentStorageIndex
,
256 DirRef deletedIndex
);
258 /***********************************************************************
259 * Declaration of the functions used to manipulate DirEntry
262 static HRESULT
insertIntoTree(
263 StorageBaseImpl
*This
,
264 DirRef parentStorageIndex
,
265 DirRef newEntryIndex
);
267 static LONG
entryNameCmp(
268 const OLECHAR
*name1
,
269 const OLECHAR
*name2
);
271 static DirRef
findElement(
272 StorageBaseImpl
*storage
,
277 static HRESULT
findTreeParent(
278 StorageBaseImpl
*storage
,
280 const OLECHAR
*childName
,
281 DirEntry
*parentData
,
285 /***********************************************************************
286 * Declaration of miscellaneous functions...
288 static HRESULT
validateSTGM(DWORD stgmValue
);
290 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
291 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
292 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
294 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
297 /****************************************************************************
298 * IEnumSTATSTGImpl definitions.
300 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
301 * This class allows iterating through the content of a storage and to find
302 * specific items inside it.
304 struct IEnumSTATSTGImpl
306 IEnumSTATSTG IEnumSTATSTG_iface
;
308 LONG ref
; /* Reference count */
309 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
310 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
312 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
315 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
317 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
321 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
322 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
324 /************************************************************************
328 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
330 return (index
+1) * This
->bigBlockSize
;
333 /************************************************************************
334 ** Storage32BaseImpl implementation
336 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
337 ULARGE_INTEGER offset
,
342 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
345 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
346 ULARGE_INTEGER offset
,
351 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
354 /************************************************************************
355 * Storage32BaseImpl_QueryInterface (IUnknown)
357 * This method implements the common QueryInterface for all IStorage32
358 * implementations contained in this file.
360 * See Windows documentation for more details on IUnknown methods.
362 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
367 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
374 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
375 IsEqualGUID(&IID_IStorage
, riid
))
377 *ppvObject
= &This
->IStorage_iface
;
379 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
381 *ppvObject
= &This
->IPropertySetStorage_iface
;
384 return E_NOINTERFACE
;
386 IStorage_AddRef(iface
);
391 /************************************************************************
392 * Storage32BaseImpl_AddRef (IUnknown)
394 * This method implements the common AddRef for all IStorage32
395 * implementations contained in this file.
397 * See Windows documentation for more details on IUnknown methods.
399 static ULONG WINAPI
StorageBaseImpl_AddRef(
402 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
403 ULONG ref
= InterlockedIncrement(&This
->ref
);
405 TRACE("(%p) AddRef to %d\n", This
, ref
);
410 /************************************************************************
411 * Storage32BaseImpl_Release (IUnknown)
413 * This method implements the common Release for all IStorage32
414 * implementations contained in this file.
416 * See Windows documentation for more details on IUnknown methods.
418 static ULONG WINAPI
StorageBaseImpl_Release(
421 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
423 ULONG ref
= InterlockedDecrement(&This
->ref
);
425 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
430 * Since we are using a system of base-classes, we want to call the
431 * destructor of the appropriate derived class. To do this, we are
432 * using virtual functions to implement the destructor.
434 StorageBaseImpl_Destroy(This
);
440 /************************************************************************
441 * Storage32BaseImpl_OpenStream (IStorage)
443 * This method will open the specified stream object from the current storage.
445 * See Windows documentation for more details on IStorage methods.
447 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
449 const OLECHAR
* pwcsName
, /* [string][in] */
450 void* reserved1
, /* [unique][in] */
451 DWORD grfMode
, /* [in] */
452 DWORD reserved2
, /* [in] */
453 IStream
** ppstm
) /* [out] */
455 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
456 StgStreamImpl
* newStream
;
457 DirEntry currentEntry
;
458 DirRef streamEntryRef
;
459 HRESULT res
= STG_E_UNKNOWN
;
461 TRACE("(%p, %s, %p, %x, %d, %p)\n",
462 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
464 if ( (pwcsName
==NULL
) || (ppstm
==0) )
472 if ( FAILED( validateSTGM(grfMode
) ) ||
473 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
475 res
= STG_E_INVALIDFLAG
;
482 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
484 res
= STG_E_INVALIDFUNCTION
;
490 res
= STG_E_REVERTED
;
495 * Check that we're compatible with the parent's storage mode, but
496 * only if we are not in transacted mode
498 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
499 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
501 res
= STG_E_INVALIDFLAG
;
507 * Search for the element with the given name
509 streamEntryRef
= findElement(
511 This
->storageDirEntry
,
516 * If it was found, construct the stream object and return a pointer to it.
518 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
519 (currentEntry
.stgType
==STGTY_STREAM
) )
521 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
523 /* A single stream cannot be opened a second time. */
524 res
= STG_E_ACCESSDENIED
;
528 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
532 newStream
->grfMode
= grfMode
;
533 *ppstm
= &newStream
->IStream_iface
;
535 IStream_AddRef(*ppstm
);
545 res
= STG_E_FILENOTFOUND
;
549 TRACE("<-- IStream %p\n", *ppstm
);
550 TRACE("<-- %08x\n", res
);
554 /************************************************************************
555 * Storage32BaseImpl_OpenStorage (IStorage)
557 * This method will open a new storage object from the current storage.
559 * See Windows documentation for more details on IStorage methods.
561 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
563 const OLECHAR
* pwcsName
, /* [string][unique][in] */
564 IStorage
* pstgPriority
, /* [unique][in] */
565 DWORD grfMode
, /* [in] */
566 SNB snbExclude
, /* [unique][in] */
567 DWORD reserved
, /* [in] */
568 IStorage
** ppstg
) /* [out] */
570 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
571 StorageInternalImpl
* newStorage
;
572 StorageBaseImpl
* newTransactedStorage
;
573 DirEntry currentEntry
;
574 DirRef storageEntryRef
;
575 HRESULT res
= STG_E_UNKNOWN
;
577 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
578 iface
, debugstr_w(pwcsName
), pstgPriority
,
579 grfMode
, snbExclude
, reserved
, ppstg
);
581 if ((pwcsName
==NULL
) || (ppstg
==0) )
587 if (This
->openFlags
& STGM_SIMPLE
)
589 res
= STG_E_INVALIDFUNCTION
;
594 if (snbExclude
!= NULL
)
596 res
= STG_E_INVALIDPARAMETER
;
600 if ( FAILED( validateSTGM(grfMode
) ))
602 res
= STG_E_INVALIDFLAG
;
609 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
610 (grfMode
& STGM_DELETEONRELEASE
) ||
611 (grfMode
& STGM_PRIORITY
) )
613 res
= STG_E_INVALIDFUNCTION
;
618 return STG_E_REVERTED
;
621 * Check that we're compatible with the parent's storage mode,
622 * but only if we are not transacted
624 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
625 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
627 res
= STG_E_ACCESSDENIED
;
634 storageEntryRef
= findElement(
636 This
->storageDirEntry
,
640 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
641 (currentEntry
.stgType
==STGTY_STORAGE
) )
643 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
645 /* A single storage cannot be opened a second time. */
646 res
= STG_E_ACCESSDENIED
;
650 newStorage
= StorageInternalImpl_Construct(
657 if (grfMode
& STGM_TRANSACTED
)
659 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
663 HeapFree(GetProcessHeap(), 0, newStorage
);
667 *ppstg
= &newTransactedStorage
->IStorage_iface
;
671 *ppstg
= &newStorage
->base
.IStorage_iface
;
674 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
680 res
= STG_E_INSUFFICIENTMEMORY
;
684 res
= STG_E_FILENOTFOUND
;
687 TRACE("<-- %08x\n", res
);
691 /************************************************************************
692 * Storage32BaseImpl_EnumElements (IStorage)
694 * This method will create an enumerator object that can be used to
695 * retrieve information about all the elements in the storage object.
697 * See Windows documentation for more details on IStorage methods.
699 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
701 DWORD reserved1
, /* [in] */
702 void* reserved2
, /* [size_is][unique][in] */
703 DWORD reserved3
, /* [in] */
704 IEnumSTATSTG
** ppenum
) /* [out] */
706 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
707 IEnumSTATSTGImpl
* newEnum
;
709 TRACE("(%p, %d, %p, %d, %p)\n",
710 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
716 return STG_E_REVERTED
;
718 newEnum
= IEnumSTATSTGImpl_Construct(
720 This
->storageDirEntry
);
724 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
726 IEnumSTATSTG_AddRef(*ppenum
);
731 return E_OUTOFMEMORY
;
734 /************************************************************************
735 * Storage32BaseImpl_Stat (IStorage)
737 * This method will retrieve information about this storage object.
739 * See Windows documentation for more details on IStorage methods.
741 static HRESULT WINAPI
StorageBaseImpl_Stat(
743 STATSTG
* pstatstg
, /* [out] */
744 DWORD grfStatFlag
) /* [in] */
746 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
747 DirEntry currentEntry
;
748 HRESULT res
= STG_E_UNKNOWN
;
750 TRACE("(%p, %p, %x)\n",
751 iface
, pstatstg
, grfStatFlag
);
761 res
= STG_E_REVERTED
;
765 res
= StorageBaseImpl_ReadDirEntry(
767 This
->storageDirEntry
,
772 StorageUtl_CopyDirEntryToSTATSTG(
778 pstatstg
->grfMode
= This
->openFlags
;
779 pstatstg
->grfStateBits
= This
->stateBits
;
785 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
787 TRACE("<-- %08x\n", res
);
791 /************************************************************************
792 * Storage32BaseImpl_RenameElement (IStorage)
794 * This method will rename the specified element.
796 * See Windows documentation for more details on IStorage methods.
798 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
800 const OLECHAR
* pwcsOldName
, /* [in] */
801 const OLECHAR
* pwcsNewName
) /* [in] */
803 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
804 DirEntry currentEntry
;
805 DirRef currentEntryRef
;
807 TRACE("(%p, %s, %s)\n",
808 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
811 return STG_E_REVERTED
;
813 currentEntryRef
= findElement(This
,
814 This
->storageDirEntry
,
818 if (currentEntryRef
!= DIRENTRY_NULL
)
821 * There is already an element with the new name
823 return STG_E_FILEALREADYEXISTS
;
827 * Search for the old element name
829 currentEntryRef
= findElement(This
,
830 This
->storageDirEntry
,
834 if (currentEntryRef
!= DIRENTRY_NULL
)
836 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
837 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
839 WARN("Element is already open; cannot rename.\n");
840 return STG_E_ACCESSDENIED
;
843 /* Remove the element from its current position in the tree */
844 removeFromTree(This
, This
->storageDirEntry
,
847 /* Change the name of the element */
848 strcpyW(currentEntry
.name
, pwcsNewName
);
850 /* Delete any sibling links */
851 currentEntry
.leftChild
= DIRENTRY_NULL
;
852 currentEntry
.rightChild
= DIRENTRY_NULL
;
854 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
857 /* Insert the element in a new position in the tree */
858 insertIntoTree(This
, This
->storageDirEntry
,
864 * There is no element with the old name
866 return STG_E_FILENOTFOUND
;
869 return StorageBaseImpl_Flush(This
);
872 /************************************************************************
873 * Storage32BaseImpl_CreateStream (IStorage)
875 * This method will create a stream object within this storage
877 * See Windows documentation for more details on IStorage methods.
879 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
881 const OLECHAR
* pwcsName
, /* [string][in] */
882 DWORD grfMode
, /* [in] */
883 DWORD reserved1
, /* [in] */
884 DWORD reserved2
, /* [in] */
885 IStream
** ppstm
) /* [out] */
887 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
888 StgStreamImpl
* newStream
;
889 DirEntry currentEntry
, newStreamEntry
;
890 DirRef currentEntryRef
, newStreamEntryRef
;
893 TRACE("(%p, %s, %x, %d, %d, %p)\n",
894 iface
, debugstr_w(pwcsName
), grfMode
,
895 reserved1
, reserved2
, ppstm
);
898 return STG_E_INVALIDPOINTER
;
901 return STG_E_INVALIDNAME
;
903 if (reserved1
|| reserved2
)
904 return STG_E_INVALIDPARAMETER
;
906 if ( FAILED( validateSTGM(grfMode
) ))
907 return STG_E_INVALIDFLAG
;
909 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
910 return STG_E_INVALIDFLAG
;
913 return STG_E_REVERTED
;
918 if ((grfMode
& STGM_DELETEONRELEASE
) ||
919 (grfMode
& STGM_TRANSACTED
))
920 return STG_E_INVALIDFUNCTION
;
923 * Don't worry about permissions in transacted mode, as we can always write
924 * changes; we just can't always commit them.
926 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
927 /* Can't create a stream on read-only storage */
928 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
929 return STG_E_ACCESSDENIED
;
931 /* Can't create a stream with greater access than the parent. */
932 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
933 return STG_E_ACCESSDENIED
;
936 if(This
->openFlags
& STGM_SIMPLE
)
937 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
941 currentEntryRef
= findElement(This
,
942 This
->storageDirEntry
,
946 if (currentEntryRef
!= DIRENTRY_NULL
)
949 * An element with this name already exists
951 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
953 IStorage_DestroyElement(iface
, pwcsName
);
956 return STG_E_FILEALREADYEXISTS
;
960 * memset the empty entry
962 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
964 newStreamEntry
.sizeOfNameString
=
965 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
967 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
968 return STG_E_INVALIDNAME
;
970 strcpyW(newStreamEntry
.name
, pwcsName
);
972 newStreamEntry
.stgType
= STGTY_STREAM
;
973 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
974 newStreamEntry
.size
.u
.LowPart
= 0;
975 newStreamEntry
.size
.u
.HighPart
= 0;
977 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
978 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
979 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
981 /* call CoFileTime to get the current time
986 /* newStreamEntry.clsid */
989 * Create an entry with the new data
991 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
996 * Insert the new entry in the parent storage's tree.
1000 This
->storageDirEntry
,
1004 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1009 * Open the stream to return it.
1011 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1015 *ppstm
= &newStream
->IStream_iface
;
1016 IStream_AddRef(*ppstm
);
1020 return STG_E_INSUFFICIENTMEMORY
;
1023 return StorageBaseImpl_Flush(This
);
1026 /************************************************************************
1027 * Storage32BaseImpl_SetClass (IStorage)
1029 * This method will write the specified CLSID in the directory entry of this
1032 * See Windows documentation for more details on IStorage methods.
1034 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1036 REFCLSID clsid
) /* [in] */
1038 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1040 DirEntry currentEntry
;
1042 TRACE("(%p, %p)\n", iface
, clsid
);
1045 return STG_E_REVERTED
;
1047 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1048 This
->storageDirEntry
,
1050 if (SUCCEEDED(hRes
))
1052 currentEntry
.clsid
= *clsid
;
1054 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1055 This
->storageDirEntry
,
1059 if (SUCCEEDED(hRes
))
1060 hRes
= StorageBaseImpl_Flush(This
);
1065 /************************************************************************
1066 ** Storage32Impl implementation
1069 /************************************************************************
1070 * Storage32BaseImpl_CreateStorage (IStorage)
1072 * This method will create the storage object within the provided storage.
1074 * See Windows documentation for more details on IStorage methods.
1076 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1078 const OLECHAR
*pwcsName
, /* [string][in] */
1079 DWORD grfMode
, /* [in] */
1080 DWORD reserved1
, /* [in] */
1081 DWORD reserved2
, /* [in] */
1082 IStorage
**ppstg
) /* [out] */
1084 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1086 DirEntry currentEntry
;
1088 DirRef currentEntryRef
;
1092 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1093 iface
, debugstr_w(pwcsName
), grfMode
,
1094 reserved1
, reserved2
, ppstg
);
1097 return STG_E_INVALIDPOINTER
;
1099 if (This
->openFlags
& STGM_SIMPLE
)
1101 return STG_E_INVALIDFUNCTION
;
1105 return STG_E_INVALIDNAME
;
1109 if ( FAILED( validateSTGM(grfMode
) ) ||
1110 (grfMode
& STGM_DELETEONRELEASE
) )
1112 WARN("bad grfMode: 0x%x\n", grfMode
);
1113 return STG_E_INVALIDFLAG
;
1117 return STG_E_REVERTED
;
1120 * Check that we're compatible with the parent's storage mode
1122 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1123 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1125 WARN("access denied\n");
1126 return STG_E_ACCESSDENIED
;
1129 currentEntryRef
= findElement(This
,
1130 This
->storageDirEntry
,
1134 if (currentEntryRef
!= DIRENTRY_NULL
)
1137 * An element with this name already exists
1139 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1140 ((This
->openFlags
& STGM_TRANSACTED
) ||
1141 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1143 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1149 WARN("file already exists\n");
1150 return STG_E_FILEALREADYEXISTS
;
1153 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1154 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1156 WARN("read-only storage\n");
1157 return STG_E_ACCESSDENIED
;
1160 memset(&newEntry
, 0, sizeof(DirEntry
));
1162 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1164 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1166 FIXME("name too long\n");
1167 return STG_E_INVALIDNAME
;
1170 strcpyW(newEntry
.name
, pwcsName
);
1172 newEntry
.stgType
= STGTY_STORAGE
;
1173 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1174 newEntry
.size
.u
.LowPart
= 0;
1175 newEntry
.size
.u
.HighPart
= 0;
1177 newEntry
.leftChild
= DIRENTRY_NULL
;
1178 newEntry
.rightChild
= DIRENTRY_NULL
;
1179 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1181 /* call CoFileTime to get the current time
1186 /* newEntry.clsid */
1189 * Create a new directory entry for the storage
1191 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1196 * Insert the new directory entry into the parent storage's tree
1198 hr
= insertIntoTree(
1200 This
->storageDirEntry
,
1204 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1209 * Open it to get a pointer to return.
1211 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1213 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1219 hr
= StorageBaseImpl_Flush(This
);
1225 /***************************************************************************
1229 * Reserve a directory entry in the file and initialize it.
1231 static HRESULT
StorageImpl_CreateDirEntry(
1232 StorageBaseImpl
*base
,
1233 const DirEntry
*newData
,
1236 StorageImpl
*storage
= (StorageImpl
*)base
;
1237 ULONG currentEntryIndex
= 0;
1238 ULONG newEntryIndex
= DIRENTRY_NULL
;
1240 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1241 WORD sizeOfNameString
;
1245 hr
= StorageImpl_ReadRawDirEntry(storage
,
1251 StorageUtl_ReadWord(
1253 OFFSET_PS_NAMELENGTH
,
1256 if (sizeOfNameString
== 0)
1259 * The entry exists and is available, we found it.
1261 newEntryIndex
= currentEntryIndex
;
1267 * We exhausted the directory entries, we will create more space below
1269 newEntryIndex
= currentEntryIndex
;
1271 currentEntryIndex
++;
1273 } while (newEntryIndex
== DIRENTRY_NULL
);
1276 * grow the directory stream
1280 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1281 ULARGE_INTEGER newSize
;
1283 ULONG lastEntry
= 0;
1284 ULONG blockCount
= 0;
1287 * obtain the new count of blocks in the directory stream
1289 blockCount
= BlockChainStream_GetCount(
1290 storage
->rootBlockChain
)+1;
1293 * initialize the size used by the directory stream
1295 newSize
.u
.HighPart
= 0;
1296 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1299 * add a block to the directory stream
1301 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1304 * memset the empty entry in order to initialize the unused newly
1307 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1312 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1315 entryIndex
= newEntryIndex
+ 1;
1316 entryIndex
< lastEntry
;
1319 StorageImpl_WriteRawDirEntry(
1325 StorageImpl_SaveFileHeader(storage
);
1328 UpdateRawDirEntry(currentData
, newData
);
1330 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1333 *index
= newEntryIndex
;
1338 /***************************************************************************
1342 * Mark a directory entry in the file as free.
1344 static HRESULT
StorageImpl_DestroyDirEntry(
1345 StorageBaseImpl
*base
,
1348 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1349 StorageImpl
*storage
= (StorageImpl
*)base
;
1351 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1353 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1357 /****************************************************************************
1361 * Case insensitive comparison of DirEntry.name by first considering
1364 * Returns <0 when name1 < name2
1365 * >0 when name1 > name2
1366 * 0 when name1 == name2
1368 static LONG
entryNameCmp(
1369 const OLECHAR
*name1
,
1370 const OLECHAR
*name2
)
1372 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1374 while (diff
== 0 && *name1
!= 0)
1377 * We compare the string themselves only when they are of the same length
1379 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1385 /****************************************************************************
1389 * Add a directory entry to a storage
1391 static HRESULT
insertIntoTree(
1392 StorageBaseImpl
*This
,
1393 DirRef parentStorageIndex
,
1394 DirRef newEntryIndex
)
1396 DirEntry currentEntry
;
1400 * Read the inserted entry
1402 StorageBaseImpl_ReadDirEntry(This
,
1407 * Read the storage entry
1409 StorageBaseImpl_ReadDirEntry(This
,
1413 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1416 * The root storage contains some element, therefore, start the research
1417 * for the appropriate location.
1420 DirRef current
, next
, previous
, currentEntryId
;
1423 * Keep a reference to the root of the storage's element tree
1425 currentEntryId
= currentEntry
.dirRootEntry
;
1430 StorageBaseImpl_ReadDirEntry(This
,
1431 currentEntry
.dirRootEntry
,
1434 previous
= currentEntry
.leftChild
;
1435 next
= currentEntry
.rightChild
;
1436 current
= currentEntryId
;
1440 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1444 if (previous
!= DIRENTRY_NULL
)
1446 StorageBaseImpl_ReadDirEntry(This
,
1453 currentEntry
.leftChild
= newEntryIndex
;
1454 StorageBaseImpl_WriteDirEntry(This
,
1462 if (next
!= DIRENTRY_NULL
)
1464 StorageBaseImpl_ReadDirEntry(This
,
1471 currentEntry
.rightChild
= newEntryIndex
;
1472 StorageBaseImpl_WriteDirEntry(This
,
1481 * Trying to insert an item with the same name in the
1482 * subtree structure.
1484 return STG_E_FILEALREADYEXISTS
;
1487 previous
= currentEntry
.leftChild
;
1488 next
= currentEntry
.rightChild
;
1494 * The storage is empty, make the new entry the root of its element tree
1496 currentEntry
.dirRootEntry
= newEntryIndex
;
1497 StorageBaseImpl_WriteDirEntry(This
,
1505 /****************************************************************************
1509 * Find and read the element of a storage with the given name.
1511 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1512 const OLECHAR
*name
, DirEntry
*data
)
1514 DirRef currentEntry
;
1516 /* Read the storage entry to find the root of the tree. */
1517 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1519 currentEntry
= data
->dirRootEntry
;
1521 while (currentEntry
!= DIRENTRY_NULL
)
1525 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1527 cmp
= entryNameCmp(name
, data
->name
);
1534 currentEntry
= data
->leftChild
;
1537 currentEntry
= data
->rightChild
;
1540 return currentEntry
;
1543 /****************************************************************************
1547 * Find and read the binary tree parent of the element with the given name.
1549 * If there is no such element, find a place where it could be inserted and
1550 * return STG_E_FILENOTFOUND.
1552 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1553 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1559 /* Read the storage entry to find the root of the tree. */
1560 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1562 *parentEntry
= storageEntry
;
1563 *relation
= DIRENTRY_RELATION_DIR
;
1565 childEntry
= parentData
->dirRootEntry
;
1567 while (childEntry
!= DIRENTRY_NULL
)
1571 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1573 cmp
= entryNameCmp(childName
, childData
.name
);
1581 *parentData
= childData
;
1582 *parentEntry
= childEntry
;
1583 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1585 childEntry
= parentData
->leftChild
;
1590 *parentData
= childData
;
1591 *parentEntry
= childEntry
;
1592 *relation
= DIRENTRY_RELATION_NEXT
;
1594 childEntry
= parentData
->rightChild
;
1598 if (childEntry
== DIRENTRY_NULL
)
1599 return STG_E_FILENOTFOUND
;
1605 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1606 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1607 SNB snbExclude
, IStorage
*pstgDest
);
1609 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1610 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1611 SNB snbExclude
, IStorage
*pstgDest
)
1617 IStream
*pstrChild
, *pstrTmp
;
1620 if (srcEntry
== DIRENTRY_NULL
)
1623 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1630 WCHAR
**snb
= snbExclude
;
1632 while ( *snb
!= NULL
&& !skip
)
1634 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1642 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1645 * create a new storage in destination storage
1647 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1648 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1653 * if it already exist, don't create a new one use this one
1655 if (hr
== STG_E_FILEALREADYEXISTS
)
1657 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1658 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1659 NULL
, 0, &pstgTmp
);
1664 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1665 skip_stream
, NULL
, pstgTmp
);
1667 IStorage_Release(pstgTmp
);
1670 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1677 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1681 * open child stream storage. This operation must succeed even if the
1682 * stream is already open, so we use internal functions to do it.
1686 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1690 pstrChild
= &streamimpl
->IStream_iface
;
1692 IStream_AddRef(pstrChild
);
1704 * Get the size of the source stream
1706 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1709 * Set the size of the destination stream.
1711 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1716 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1719 IStream_Release( pstrChild
);
1722 IStream_Release( pstrTmp
);
1728 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1729 skip_stream
, snbExclude
, pstgDest
);
1732 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1733 skip_stream
, snbExclude
, pstgDest
);
1738 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1739 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1740 SNB snbExclude
, IStorage
*pstgDest
)
1745 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1748 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1751 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1752 skip_stream
, snbExclude
, pstgDest
);
1757 /*************************************************************************
1760 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1762 DWORD ciidExclude
, /* [in] */
1763 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1764 SNB snbExclude
, /* [unique][in] */
1765 IStorage
* pstgDest
) /* [unique][in] */
1767 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1769 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1772 TRACE("(%p, %d, %p, %p, %p)\n",
1773 iface
, ciidExclude
, rgiidExclude
,
1774 snbExclude
, pstgDest
);
1776 if ( pstgDest
== 0 )
1777 return STG_E_INVALIDPOINTER
;
1779 for(i
= 0; i
< ciidExclude
; ++i
)
1781 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1782 skip_storage
= TRUE
;
1783 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1786 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1791 /* Give up early if it looks like this would be infinitely recursive.
1792 * Oddly enough, this includes some cases that aren't really recursive, like
1793 * copying to a transacted child. */
1794 IStorage
*pstgDestAncestor
= pstgDest
;
1795 IStorage
*pstgDestAncestorChild
= NULL
;
1797 /* Go up the chain from the destination until we find the source storage. */
1798 while (pstgDestAncestor
!= iface
) {
1799 pstgDestAncestorChild
= pstgDest
;
1801 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1803 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1805 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1807 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1809 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1811 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1817 if (pstgDestAncestor
== iface
)
1821 if (pstgDestAncestorChild
&& snbExclude
)
1823 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1825 WCHAR
**snb
= snbExclude
;
1827 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1829 while ( *snb
!= NULL
&& fail
)
1831 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1838 return STG_E_ACCESSDENIED
;
1842 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1843 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1846 /*************************************************************************
1847 * MoveElementTo (IStorage)
1849 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1851 const OLECHAR
*pwcsName
, /* [string][in] */
1852 IStorage
*pstgDest
, /* [unique][in] */
1853 const OLECHAR
*pwcsNewName
,/* [string][in] */
1854 DWORD grfFlags
) /* [in] */
1856 FIXME("(%p %s %p %s %u): stub\n", iface
,
1857 debugstr_w(pwcsName
), pstgDest
,
1858 debugstr_w(pwcsNewName
), grfFlags
);
1862 /*************************************************************************
1865 * Ensures that any changes made to a storage object open in transacted mode
1866 * are reflected in the parent storage
1868 * In a non-transacted mode, this ensures all cached writes are completed.
1870 static HRESULT WINAPI
StorageImpl_Commit(
1872 DWORD grfCommitFlags
)/* [in] */
1874 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1875 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1876 return StorageBaseImpl_Flush(This
);
1879 /*************************************************************************
1882 * Discard all changes that have been made since the last commit operation
1884 static HRESULT WINAPI
StorageImpl_Revert(
1887 TRACE("(%p)\n", iface
);
1891 /*************************************************************************
1892 * DestroyElement (IStorage)
1894 * Strategy: This implementation is built this way for simplicity not for speed.
1895 * I always delete the topmost element of the enumeration and adjust
1896 * the deleted element pointer all the time. This takes longer to
1897 * do but allow to reinvoke DestroyElement whenever we encounter a
1898 * storage object. The optimisation resides in the usage of another
1899 * enumeration strategy that would give all the leaves of a storage
1900 * first. (postfix order)
1902 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1904 const OLECHAR
*pwcsName
)/* [string][in] */
1906 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1909 DirEntry entryToDelete
;
1910 DirRef entryToDeleteRef
;
1913 iface
, debugstr_w(pwcsName
));
1916 return STG_E_INVALIDPOINTER
;
1919 return STG_E_REVERTED
;
1921 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1922 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1923 return STG_E_ACCESSDENIED
;
1925 entryToDeleteRef
= findElement(
1927 This
->storageDirEntry
,
1931 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1933 return STG_E_FILENOTFOUND
;
1936 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1938 hr
= deleteStorageContents(
1943 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1945 hr
= deleteStreamContents(
1955 * Remove the entry from its parent storage
1957 hr
= removeFromTree(
1959 This
->storageDirEntry
,
1963 * Invalidate the entry
1966 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1969 hr
= StorageBaseImpl_Flush(This
);
1975 /******************************************************************************
1976 * Internal stream list handlers
1979 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1981 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1982 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1985 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1987 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1988 list_remove(&(strm
->StrmListEntry
));
1991 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1993 StgStreamImpl
*strm
;
1995 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1997 if (strm
->dirEntry
== streamEntry
)
2006 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2008 StorageInternalImpl
*childstg
;
2010 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2012 if (childstg
->base
.storageDirEntry
== storageEntry
)
2021 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2023 struct list
*cur
, *cur2
;
2024 StgStreamImpl
*strm
=NULL
;
2025 StorageInternalImpl
*childstg
=NULL
;
2027 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2028 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2029 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2030 strm
->parentStorage
= NULL
;
2034 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2035 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2036 StorageBaseImpl_Invalidate( &childstg
->base
);
2039 if (stg
->transactedChild
)
2041 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2043 stg
->transactedChild
= NULL
;
2048 /*********************************************************************
2052 * Delete the contents of a storage entry.
2055 static HRESULT
deleteStorageContents(
2056 StorageBaseImpl
*parentStorage
,
2057 DirRef indexToDelete
,
2058 DirEntry entryDataToDelete
)
2060 IEnumSTATSTG
*elements
= 0;
2061 IStorage
*childStorage
= 0;
2062 STATSTG currentElement
;
2064 HRESULT destroyHr
= S_OK
;
2065 StorageInternalImpl
*stg
, *stg2
;
2067 /* Invalidate any open storage objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2070 if (stg
->base
.storageDirEntry
== indexToDelete
)
2072 StorageBaseImpl_Invalidate(&stg
->base
);
2077 * Open the storage and enumerate it
2079 hr
= IStorage_OpenStorage(
2080 &parentStorage
->IStorage_iface
,
2081 entryDataToDelete
.name
,
2083 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2094 * Enumerate the elements
2096 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2101 * Obtain the next element
2103 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2106 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2108 CoTaskMemFree(currentElement
.pwcsName
);
2112 * We need to Reset the enumeration every time because we delete elements
2113 * and the enumeration could be invalid
2115 IEnumSTATSTG_Reset(elements
);
2117 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2119 IStorage_Release(childStorage
);
2120 IEnumSTATSTG_Release(elements
);
2125 /*********************************************************************
2129 * Perform the deletion of a stream's data
2132 static HRESULT
deleteStreamContents(
2133 StorageBaseImpl
*parentStorage
,
2134 DirRef indexToDelete
,
2135 DirEntry entryDataToDelete
)
2139 ULARGE_INTEGER size
;
2140 StgStreamImpl
*strm
, *strm2
;
2142 /* Invalidate any open stream objects. */
2143 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2145 if (strm
->dirEntry
== indexToDelete
)
2147 TRACE("Stream deleted %p\n", strm
);
2148 strm
->parentStorage
= NULL
;
2149 list_remove(&strm
->StrmListEntry
);
2153 size
.u
.HighPart
= 0;
2156 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2157 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2167 hr
= IStream_SetSize(pis
, size
);
2175 * Release the stream object.
2177 IStream_Release(pis
);
2182 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2186 case DIRENTRY_RELATION_PREVIOUS
:
2187 entry
->leftChild
= new_target
;
2189 case DIRENTRY_RELATION_NEXT
:
2190 entry
->rightChild
= new_target
;
2192 case DIRENTRY_RELATION_DIR
:
2193 entry
->dirRootEntry
= new_target
;
2200 /*************************************************************************
2204 * This method removes a directory entry from its parent storage tree without
2205 * freeing any resources attached to it.
2207 static HRESULT
removeFromTree(
2208 StorageBaseImpl
*This
,
2209 DirRef parentStorageIndex
,
2210 DirRef deletedIndex
)
2212 DirEntry entryToDelete
;
2213 DirEntry parentEntry
;
2214 DirRef parentEntryRef
;
2215 ULONG typeOfRelation
;
2218 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2224 * Find the element that links to the one we want to delete.
2226 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2227 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2232 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2235 * Replace the deleted entry with its left child
2237 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2239 hr
= StorageBaseImpl_WriteDirEntry(
2248 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2251 * We need to reinsert the right child somewhere. We already know it and
2252 * its children are greater than everything in the left tree, so we
2253 * insert it at the rightmost point in the left tree.
2255 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2256 DirEntry newRightChildParentEntry
;
2260 hr
= StorageBaseImpl_ReadDirEntry(
2262 newRightChildParent
,
2263 &newRightChildParentEntry
);
2269 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2270 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2271 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2273 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2275 hr
= StorageBaseImpl_WriteDirEntry(
2277 newRightChildParent
,
2278 &newRightChildParentEntry
);
2288 * Replace the deleted entry with its right child
2290 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2292 hr
= StorageBaseImpl_WriteDirEntry(
2306 /******************************************************************************
2307 * SetElementTimes (IStorage)
2309 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2311 const OLECHAR
*pwcsName
,/* [string][in] */
2312 const FILETIME
*pctime
, /* [in] */
2313 const FILETIME
*patime
, /* [in] */
2314 const FILETIME
*pmtime
) /* [in] */
2316 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2320 /******************************************************************************
2321 * SetStateBits (IStorage)
2323 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2325 DWORD grfStateBits
,/* [in] */
2326 DWORD grfMask
) /* [in] */
2328 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2331 return STG_E_REVERTED
;
2333 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2337 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2338 DirRef index
, const DirEntry
*data
)
2340 StorageImpl
*This
= (StorageImpl
*)base
;
2341 return StorageImpl_WriteDirEntry(This
, index
, data
);
2344 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2345 DirRef index
, DirEntry
*data
)
2347 StorageImpl
*This
= (StorageImpl
*)base
;
2348 return StorageImpl_ReadDirEntry(This
, index
, data
);
2351 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2355 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2357 if (!This
->blockChainCache
[i
])
2359 return &This
->blockChainCache
[i
];
2363 i
= This
->blockChainToEvict
;
2365 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2366 This
->blockChainCache
[i
] = NULL
;
2368 This
->blockChainToEvict
++;
2369 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2370 This
->blockChainToEvict
= 0;
2372 return &This
->blockChainCache
[i
];
2375 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2378 int i
, free_index
=-1;
2380 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2382 if (!This
->blockChainCache
[i
])
2384 if (free_index
== -1) free_index
= i
;
2386 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2388 return &This
->blockChainCache
[i
];
2392 if (free_index
== -1)
2394 free_index
= This
->blockChainToEvict
;
2396 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2397 This
->blockChainCache
[free_index
] = NULL
;
2399 This
->blockChainToEvict
++;
2400 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2401 This
->blockChainToEvict
= 0;
2404 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2405 return &This
->blockChainCache
[free_index
];
2408 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2412 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2414 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2416 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2417 This
->blockChainCache
[i
] = NULL
;
2423 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2424 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2426 StorageImpl
*This
= (StorageImpl
*)base
;
2431 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2432 if (FAILED(hr
)) return hr
;
2434 if (data
.size
.QuadPart
== 0)
2440 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2442 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2449 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2451 SmallBlockChainStream
*stream
;
2453 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2454 if (!stream
) return E_OUTOFMEMORY
;
2456 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2458 SmallBlockChainStream_Destroy(stream
);
2464 BlockChainStream
*stream
= NULL
;
2466 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2467 if (!stream
) return E_OUTOFMEMORY
;
2469 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2475 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2476 ULARGE_INTEGER newsize
)
2478 StorageImpl
*This
= (StorageImpl
*)base
;
2481 SmallBlockChainStream
*smallblock
=NULL
;
2482 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2484 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2485 if (FAILED(hr
)) return hr
;
2487 /* In simple mode keep the stream size above the small block limit */
2488 if (This
->base
.openFlags
& STGM_SIMPLE
)
2489 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2491 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2494 /* Create a block chain object of the appropriate type */
2495 if (data
.size
.QuadPart
== 0)
2497 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2499 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2500 if (!smallblock
) return E_OUTOFMEMORY
;
2504 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2505 bigblock
= *pbigblock
;
2506 if (!bigblock
) return E_OUTOFMEMORY
;
2509 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2511 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2512 if (!smallblock
) return E_OUTOFMEMORY
;
2516 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2517 bigblock
= *pbigblock
;
2518 if (!bigblock
) return E_OUTOFMEMORY
;
2521 /* Change the block chain type if necessary. */
2522 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2524 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2527 SmallBlockChainStream_Destroy(smallblock
);
2531 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2532 *pbigblock
= bigblock
;
2534 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2536 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2541 /* Set the size of the block chain. */
2544 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2545 SmallBlockChainStream_Destroy(smallblock
);
2549 BlockChainStream_SetSize(bigblock
, newsize
);
2552 /* Set the size in the directory entry. */
2553 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2556 data
.size
= newsize
;
2558 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2563 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2564 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2566 StorageImpl
*This
= (StorageImpl
*)base
;
2569 ULARGE_INTEGER newSize
;
2571 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2572 if (FAILED(hr
)) return hr
;
2574 /* Grow the stream if necessary */
2575 newSize
.QuadPart
= 0;
2576 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2578 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2580 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2584 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2585 if (FAILED(hr
)) return hr
;
2588 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2590 SmallBlockChainStream
*stream
;
2592 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2593 if (!stream
) return E_OUTOFMEMORY
;
2595 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2597 SmallBlockChainStream_Destroy(stream
);
2603 BlockChainStream
*stream
;
2605 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2606 if (!stream
) return E_OUTOFMEMORY
;
2608 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2614 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2617 StorageImpl
*This
= (StorageImpl
*)base
;
2618 DirEntry dst_data
, src_data
;
2621 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2624 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2628 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2629 dst_data
.startingBlock
= src_data
.startingBlock
;
2630 dst_data
.size
= src_data
.size
;
2632 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2638 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2640 StorageImpl
*This
= (StorageImpl
*) iface
;
2644 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2646 *result
= statstg
.pwcsName
;
2652 * Virtual function table for the IStorage32Impl class.
2654 static const IStorageVtbl Storage32Impl_Vtbl
=
2656 StorageBaseImpl_QueryInterface
,
2657 StorageBaseImpl_AddRef
,
2658 StorageBaseImpl_Release
,
2659 StorageBaseImpl_CreateStream
,
2660 StorageBaseImpl_OpenStream
,
2661 StorageBaseImpl_CreateStorage
,
2662 StorageBaseImpl_OpenStorage
,
2663 StorageBaseImpl_CopyTo
,
2664 StorageBaseImpl_MoveElementTo
,
2667 StorageBaseImpl_EnumElements
,
2668 StorageBaseImpl_DestroyElement
,
2669 StorageBaseImpl_RenameElement
,
2670 StorageBaseImpl_SetElementTimes
,
2671 StorageBaseImpl_SetClass
,
2672 StorageBaseImpl_SetStateBits
,
2673 StorageBaseImpl_Stat
2676 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2678 StorageImpl_Destroy
,
2679 StorageImpl_Invalidate
,
2681 StorageImpl_GetFilename
,
2682 StorageImpl_CreateDirEntry
,
2683 StorageImpl_BaseWriteDirEntry
,
2684 StorageImpl_BaseReadDirEntry
,
2685 StorageImpl_DestroyDirEntry
,
2686 StorageImpl_StreamReadAt
,
2687 StorageImpl_StreamWriteAt
,
2688 StorageImpl_StreamSetSize
,
2689 StorageImpl_StreamLink
2692 static HRESULT
StorageImpl_Construct(
2700 StorageImpl
** result
)
2704 DirEntry currentEntry
;
2705 DirRef currentEntryRef
;
2707 if ( FAILED( validateSTGM(openFlags
) ))
2708 return STG_E_INVALIDFLAG
;
2710 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2712 return E_OUTOFMEMORY
;
2714 memset(This
, 0, sizeof(StorageImpl
));
2716 list_init(&This
->base
.strmHead
);
2718 list_init(&This
->base
.storageHead
);
2720 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
2721 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
2722 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2723 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2725 This
->base
.create
= create
;
2727 This
->base
.reverted
= 0;
2730 * Initialize the big block cache.
2732 This
->bigBlockSize
= sector_size
;
2733 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2735 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2738 This
->lockBytes
= pLkbyt
;
2739 ILockBytes_AddRef(pLkbyt
);
2747 ULARGE_INTEGER size
;
2748 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2750 /* Discard any existing data. */
2752 ILockBytes_SetSize(This
->lockBytes
, size
);
2755 * Initialize all header variables:
2756 * - The big block depot consists of one block and it is at block 0
2757 * - The directory table starts at block 1
2758 * - There is no small block depot
2760 memset( This
->bigBlockDepotStart
,
2762 sizeof(This
->bigBlockDepotStart
));
2764 This
->bigBlockDepotCount
= 1;
2765 This
->bigBlockDepotStart
[0] = 0;
2766 This
->rootStartBlock
= 1;
2767 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2768 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2769 if (sector_size
== 4096)
2770 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2772 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2773 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2774 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2775 This
->extBigBlockDepotCount
= 0;
2777 StorageImpl_SaveFileHeader(This
);
2780 * Add one block for the big block depot and one block for the directory table
2782 size
.u
.HighPart
= 0;
2783 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2784 ILockBytes_SetSize(This
->lockBytes
, size
);
2787 * Initialize the big block depot
2789 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2790 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2791 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2792 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2797 * Load the header for the file.
2799 hr
= StorageImpl_LoadFileHeader(This
);
2808 * There is no block depot cached yet.
2810 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2811 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
2814 * Start searching for free blocks with block 0.
2816 This
->prevFreeBlock
= 0;
2818 This
->firstFreeSmallBlock
= 0;
2820 /* Read the extended big block depot locations. */
2821 if (This
->extBigBlockDepotCount
!= 0)
2823 ULONG current_block
= This
->extBigBlockDepotStart
;
2824 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2827 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2828 if (!This
->extBigBlockDepotLocations
)
2834 This
->extBigBlockDepotLocationsSize
= cache_size
;
2836 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2838 if (current_block
== BLOCK_END_OF_CHAIN
)
2840 WARN("File has too few extended big block depot blocks.\n");
2841 hr
= STG_E_DOCFILECORRUPT
;
2844 This
->extBigBlockDepotLocations
[i
] = current_block
;
2845 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2850 This
->extBigBlockDepotLocations
= NULL
;
2851 This
->extBigBlockDepotLocationsSize
= 0;
2855 * Create the block chain abstractions.
2857 if(!(This
->rootBlockChain
=
2858 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2860 hr
= STG_E_READFAULT
;
2864 if(!(This
->smallBlockDepotChain
=
2865 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2868 hr
= STG_E_READFAULT
;
2873 * Write the root storage entry (memory only)
2877 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2880 * Initialize the directory table
2882 memset(&rootEntry
, 0, sizeof(rootEntry
));
2883 strcpyW(rootEntry
.name
, rootentryW
);
2884 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
2885 rootEntry
.stgType
= STGTY_ROOT
;
2886 rootEntry
.leftChild
= DIRENTRY_NULL
;
2887 rootEntry
.rightChild
= DIRENTRY_NULL
;
2888 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2889 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2890 rootEntry
.size
.u
.HighPart
= 0;
2891 rootEntry
.size
.u
.LowPart
= 0;
2893 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2897 * Find the ID of the root storage.
2899 currentEntryRef
= 0;
2903 hr
= StorageImpl_ReadDirEntry(
2910 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2911 (currentEntry
.stgType
== STGTY_ROOT
) )
2913 This
->base
.storageDirEntry
= currentEntryRef
;
2919 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2923 hr
= STG_E_READFAULT
;
2928 * Create the block chain abstraction for the small block root chain.
2930 if(!(This
->smallBlockRootChain
=
2931 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2933 hr
= STG_E_READFAULT
;
2939 IStorage_Release(&This
->base
.IStorage_iface
);
2944 StorageImpl_Flush((StorageBaseImpl
*)This
);
2951 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2953 StorageImpl
*This
= (StorageImpl
*) iface
;
2955 StorageBaseImpl_DeleteAll(&This
->base
);
2957 This
->base
.reverted
= 1;
2960 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2962 StorageImpl
*This
= (StorageImpl
*) iface
;
2964 TRACE("(%p)\n", This
);
2966 StorageImpl_Flush(iface
);
2968 StorageImpl_Invalidate(iface
);
2970 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
2972 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2973 BlockChainStream_Destroy(This
->rootBlockChain
);
2974 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2976 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2977 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2979 if (This
->lockBytes
)
2980 ILockBytes_Release(This
->lockBytes
);
2981 HeapFree(GetProcessHeap(), 0, This
);
2984 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
)
2986 StorageImpl
*This
= (StorageImpl
*) iface
;
2989 TRACE("(%p)\n", This
);
2991 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
2994 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
2997 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
2999 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3000 if (This
->blockChainCache
[i
])
3001 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3004 hr
= ILockBytes_Flush(This
->lockBytes
);
3009 /******************************************************************************
3010 * Storage32Impl_GetNextFreeBigBlock
3012 * Returns the index of the next free big block.
3013 * If the big block depot is filled, this method will enlarge it.
3016 static ULONG
StorageImpl_GetNextFreeBigBlock(
3019 ULONG depotBlockIndexPos
;
3020 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3022 ULONG depotBlockOffset
;
3023 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3024 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3026 ULONG freeBlock
= BLOCK_UNUSED
;
3027 ULARGE_INTEGER neededSize
;
3030 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3031 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3034 * Scan the entire big block depot until we find a block marked free
3036 while (nextBlockIndex
!= BLOCK_UNUSED
)
3038 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3040 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3043 * Grow the primary depot.
3045 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3047 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3050 * Add a block depot.
3052 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3053 This
->bigBlockDepotCount
++;
3054 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3057 * Flag it as a block depot.
3059 StorageImpl_SetNextBlockInChain(This
,
3063 /* Save new header information.
3065 StorageImpl_SaveFileHeader(This
);
3070 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3072 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3075 * Grow the extended depot.
3077 ULONG extIndex
= BLOCK_UNUSED
;
3078 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3079 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3081 if (extBlockOffset
== 0)
3083 /* We need an extended block.
3085 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3086 This
->extBigBlockDepotCount
++;
3087 depotBlockIndexPos
= extIndex
+ 1;
3090 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3093 * Add a block depot and mark it in the extended block.
3095 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3096 This
->bigBlockDepotCount
++;
3097 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3099 /* Flag the block depot.
3101 StorageImpl_SetNextBlockInChain(This
,
3105 /* If necessary, flag the extended depot block.
3107 if (extIndex
!= BLOCK_UNUSED
)
3108 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3110 /* Save header information.
3112 StorageImpl_SaveFileHeader(This
);
3116 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3120 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3121 ( nextBlockIndex
!= BLOCK_UNUSED
))
3123 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3125 if (nextBlockIndex
== BLOCK_UNUSED
)
3127 freeBlock
= (depotIndex
* blocksPerDepot
) +
3128 (depotBlockOffset
/sizeof(ULONG
));
3131 depotBlockOffset
+= sizeof(ULONG
);
3136 depotBlockOffset
= 0;
3140 * make sure that the block physically exists before using it
3142 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3144 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3146 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3147 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3149 This
->prevFreeBlock
= freeBlock
;
3154 /******************************************************************************
3155 * Storage32Impl_AddBlockDepot
3157 * This will create a depot block, essentially it is a block initialized
3160 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3162 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3165 * Initialize blocks as free
3167 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3168 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3171 /******************************************************************************
3172 * Storage32Impl_GetExtDepotBlock
3174 * Returns the index of the block that corresponds to the specified depot
3175 * index. This method is only for depot indexes equal or greater than
3176 * COUNT_BBDEPOTINHEADER.
3178 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3180 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3181 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3182 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3183 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3184 ULONG blockIndex
= BLOCK_UNUSED
;
3185 ULONG extBlockIndex
;
3186 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3187 int index
, num_blocks
;
3189 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3191 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3192 return BLOCK_UNUSED
;
3194 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3196 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3198 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
);
3200 num_blocks
= This
->bigBlockSize
/ 4;
3202 for (index
= 0; index
< num_blocks
; index
++)
3204 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3205 This
->extBlockDepotCached
[index
] = blockIndex
;
3208 This
->indexExtBlockDepotCached
= extBlockCount
;
3211 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3216 /******************************************************************************
3217 * Storage32Impl_SetExtDepotBlock
3219 * Associates the specified block index to the specified depot index.
3220 * This method is only for depot indexes equal or greater than
3221 * COUNT_BBDEPOTINHEADER.
3223 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3225 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3226 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3227 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3228 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3229 ULONG extBlockIndex
;
3231 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3233 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3235 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3237 if (extBlockIndex
!= BLOCK_UNUSED
)
3239 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3240 extBlockOffset
* sizeof(ULONG
),
3244 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3246 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3250 /******************************************************************************
3251 * Storage32Impl_AddExtBlockDepot
3253 * Creates an extended depot block.
3255 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3257 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3258 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3259 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3260 ULONG index
= BLOCK_UNUSED
;
3261 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3262 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3263 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3265 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3266 blocksPerDepotBlock
;
3268 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3271 * The first extended block.
3273 This
->extBigBlockDepotStart
= index
;
3278 * Find the last existing extended block.
3280 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3283 * Add the new extended block to the chain.
3285 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3290 * Initialize this block.
3292 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3293 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3295 /* Add the block to our cache. */
3296 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3298 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3299 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3301 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3302 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3304 This
->extBigBlockDepotLocations
= new_cache
;
3305 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3307 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3312 /******************************************************************************
3313 * Storage32Impl_FreeBigBlock
3315 * This method will flag the specified block as free in the big block depot.
3317 static void StorageImpl_FreeBigBlock(
3321 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3323 if (blockIndex
< This
->prevFreeBlock
)
3324 This
->prevFreeBlock
= blockIndex
;
3327 /************************************************************************
3328 * Storage32Impl_GetNextBlockInChain
3330 * This method will retrieve the block index of the next big block in
3333 * Params: This - Pointer to the Storage object.
3334 * blockIndex - Index of the block to retrieve the chain
3336 * nextBlockIndex - receives the return value.
3338 * Returns: This method returns the index of the next block in the chain.
3339 * It will return the constants:
3340 * BLOCK_SPECIAL - If the block given was not part of a
3342 * BLOCK_END_OF_CHAIN - If the block given was the last in
3344 * BLOCK_UNUSED - If the block given was not past of a chain
3346 * BLOCK_EXTBBDEPOT - This block is part of the extended
3349 * See Windows documentation for more details on IStorage methods.
3351 static HRESULT
StorageImpl_GetNextBlockInChain(
3354 ULONG
* nextBlockIndex
)
3356 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3357 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3358 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3359 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3361 ULONG depotBlockIndexPos
;
3362 int index
, num_blocks
;
3364 *nextBlockIndex
= BLOCK_SPECIAL
;
3366 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3368 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3369 This
->bigBlockDepotCount
);
3370 return STG_E_READFAULT
;
3374 * Cache the currently accessed depot block.
3376 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3378 This
->indexBlockDepotCached
= depotBlockCount
;
3380 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3382 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3387 * We have to look in the extended depot.
3389 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3392 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3395 return STG_E_READFAULT
;
3397 num_blocks
= This
->bigBlockSize
/ 4;
3399 for (index
= 0; index
< num_blocks
; index
++)
3401 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3402 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3406 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3411 /******************************************************************************
3412 * Storage32Impl_GetNextExtendedBlock
3414 * Given an extended block this method will return the next extended block.
3417 * The last ULONG of an extended block is the block index of the next
3418 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3422 * - The index of the next extended block
3423 * - BLOCK_UNUSED: there is no next extended block.
3424 * - Any other return values denotes failure.
3426 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3428 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3429 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3431 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3434 return nextBlockIndex
;
3437 /******************************************************************************
3438 * Storage32Impl_SetNextBlockInChain
3440 * This method will write the index of the specified block's next block
3441 * in the big block depot.
3443 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3446 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3447 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3448 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3451 static void StorageImpl_SetNextBlockInChain(
3456 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3457 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3458 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3459 ULONG depotBlockIndexPos
;
3461 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3462 assert(blockIndex
!= nextBlock
);
3464 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3466 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3471 * We have to look in the extended depot.
3473 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3476 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3479 * Update the cached block depot, if necessary.
3481 if (depotBlockCount
== This
->indexBlockDepotCached
)
3483 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3487 /******************************************************************************
3488 * Storage32Impl_LoadFileHeader
3490 * This method will read in the file header
3492 static HRESULT
StorageImpl_LoadFileHeader(
3496 BYTE headerBigBlock
[HEADER_SIZE
];
3498 ULARGE_INTEGER offset
;
3503 * Get a pointer to the big block of data containing the header.
3505 offset
.u
.HighPart
= 0;
3506 offset
.u
.LowPart
= 0;
3507 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3508 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3509 hr
= STG_E_FILENOTFOUND
;
3512 * Extract the information from the header.
3517 * Check for the "magic number" signature and return an error if it is not
3520 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3522 return STG_E_OLDFORMAT
;
3525 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3527 return STG_E_INVALIDHEADER
;
3530 StorageUtl_ReadWord(
3532 OFFSET_BIGBLOCKSIZEBITS
,
3533 &This
->bigBlockSizeBits
);
3535 StorageUtl_ReadWord(
3537 OFFSET_SMALLBLOCKSIZEBITS
,
3538 &This
->smallBlockSizeBits
);
3540 StorageUtl_ReadDWord(
3542 OFFSET_BBDEPOTCOUNT
,
3543 &This
->bigBlockDepotCount
);
3545 StorageUtl_ReadDWord(
3547 OFFSET_ROOTSTARTBLOCK
,
3548 &This
->rootStartBlock
);
3550 StorageUtl_ReadDWord(
3552 OFFSET_SMALLBLOCKLIMIT
,
3553 &This
->smallBlockLimit
);
3555 StorageUtl_ReadDWord(
3557 OFFSET_SBDEPOTSTART
,
3558 &This
->smallBlockDepotStart
);
3560 StorageUtl_ReadDWord(
3562 OFFSET_EXTBBDEPOTSTART
,
3563 &This
->extBigBlockDepotStart
);
3565 StorageUtl_ReadDWord(
3567 OFFSET_EXTBBDEPOTCOUNT
,
3568 &This
->extBigBlockDepotCount
);
3570 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3572 StorageUtl_ReadDWord(
3574 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3575 &(This
->bigBlockDepotStart
[index
]));
3579 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3581 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3582 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3585 * Right now, the code is making some assumptions about the size of the
3586 * blocks, just make sure they are what we're expecting.
3588 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3589 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3590 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3592 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3593 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3594 hr
= STG_E_INVALIDHEADER
;
3603 /******************************************************************************
3604 * Storage32Impl_SaveFileHeader
3606 * This method will save to the file the header
3608 static void StorageImpl_SaveFileHeader(
3611 BYTE headerBigBlock
[HEADER_SIZE
];
3614 ULARGE_INTEGER offset
;
3615 DWORD bytes_read
, bytes_written
;
3616 DWORD major_version
, dirsectorcount
;
3619 * Get a pointer to the big block of data containing the header.
3621 offset
.u
.HighPart
= 0;
3622 offset
.u
.LowPart
= 0;
3623 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3624 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3625 hr
= STG_E_FILENOTFOUND
;
3627 if (This
->bigBlockSizeBits
== 0x9)
3629 else if (This
->bigBlockSizeBits
== 0xc)
3633 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3638 * If the block read failed, the file is probably new.
3643 * Initialize for all unknown fields.
3645 memset(headerBigBlock
, 0, HEADER_SIZE
);
3648 * Initialize the magic number.
3650 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3654 * Write the information to the header.
3656 StorageUtl_WriteWord(
3658 OFFSET_MINORVERSION
,
3661 StorageUtl_WriteWord(
3663 OFFSET_MAJORVERSION
,
3666 StorageUtl_WriteWord(
3668 OFFSET_BYTEORDERMARKER
,
3671 StorageUtl_WriteWord(
3673 OFFSET_BIGBLOCKSIZEBITS
,
3674 This
->bigBlockSizeBits
);
3676 StorageUtl_WriteWord(
3678 OFFSET_SMALLBLOCKSIZEBITS
,
3679 This
->smallBlockSizeBits
);
3681 if (major_version
>= 4)
3683 if (This
->rootBlockChain
)
3684 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3686 /* This file is being created, and it will start out with one block. */
3690 /* This field must be 0 in versions older than 4 */
3693 StorageUtl_WriteDWord(
3695 OFFSET_DIRSECTORCOUNT
,
3698 StorageUtl_WriteDWord(
3700 OFFSET_BBDEPOTCOUNT
,
3701 This
->bigBlockDepotCount
);
3703 StorageUtl_WriteDWord(
3705 OFFSET_ROOTSTARTBLOCK
,
3706 This
->rootStartBlock
);
3708 StorageUtl_WriteDWord(
3710 OFFSET_SMALLBLOCKLIMIT
,
3711 This
->smallBlockLimit
);
3713 StorageUtl_WriteDWord(
3715 OFFSET_SBDEPOTSTART
,
3716 This
->smallBlockDepotStart
);
3718 StorageUtl_WriteDWord(
3720 OFFSET_SBDEPOTCOUNT
,
3721 This
->smallBlockDepotChain
?
3722 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3724 StorageUtl_WriteDWord(
3726 OFFSET_EXTBBDEPOTSTART
,
3727 This
->extBigBlockDepotStart
);
3729 StorageUtl_WriteDWord(
3731 OFFSET_EXTBBDEPOTCOUNT
,
3732 This
->extBigBlockDepotCount
);
3734 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3736 StorageUtl_WriteDWord(
3738 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3739 (This
->bigBlockDepotStart
[index
]));
3743 * Write the big block back to the file.
3745 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3748 /******************************************************************************
3749 * StorageImpl_ReadRawDirEntry
3751 * This method will read the raw data from a directory entry in the file.
3753 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3755 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3757 ULARGE_INTEGER offset
;
3761 offset
.u
.HighPart
= 0;
3762 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3764 hr
= BlockChainStream_ReadAt(
3765 This
->rootBlockChain
,
3771 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3772 return STG_E_READFAULT
;
3777 /******************************************************************************
3778 * StorageImpl_WriteRawDirEntry
3780 * This method will write the raw data from a directory entry in the file.
3782 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3784 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3786 ULARGE_INTEGER offset
;
3790 offset
.u
.HighPart
= 0;
3791 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3793 hr
= BlockChainStream_WriteAt(
3794 This
->rootBlockChain
,
3803 /******************************************************************************
3806 * Update raw directory entry data from the fields in newData.
3808 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3810 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3812 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3815 buffer
+ OFFSET_PS_NAME
,
3817 DIRENTRY_NAME_BUFFER_LEN
);
3819 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3821 StorageUtl_WriteWord(
3823 OFFSET_PS_NAMELENGTH
,
3824 newData
->sizeOfNameString
);
3826 StorageUtl_WriteDWord(
3828 OFFSET_PS_LEFTCHILD
,
3829 newData
->leftChild
);
3831 StorageUtl_WriteDWord(
3833 OFFSET_PS_RIGHTCHILD
,
3834 newData
->rightChild
);
3836 StorageUtl_WriteDWord(
3839 newData
->dirRootEntry
);
3841 StorageUtl_WriteGUID(
3846 StorageUtl_WriteDWord(
3849 newData
->ctime
.dwLowDateTime
);
3851 StorageUtl_WriteDWord(
3853 OFFSET_PS_CTIMEHIGH
,
3854 newData
->ctime
.dwHighDateTime
);
3856 StorageUtl_WriteDWord(
3859 newData
->mtime
.dwLowDateTime
);
3861 StorageUtl_WriteDWord(
3863 OFFSET_PS_MTIMEHIGH
,
3864 newData
->ctime
.dwHighDateTime
);
3866 StorageUtl_WriteDWord(
3868 OFFSET_PS_STARTBLOCK
,
3869 newData
->startingBlock
);
3871 StorageUtl_WriteDWord(
3874 newData
->size
.u
.LowPart
);
3877 /******************************************************************************
3878 * Storage32Impl_ReadDirEntry
3880 * This method will read the specified directory entry.
3882 HRESULT
StorageImpl_ReadDirEntry(
3887 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3890 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3892 if (SUCCEEDED(readRes
))
3894 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3897 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3898 DIRENTRY_NAME_BUFFER_LEN
);
3899 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3901 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3903 StorageUtl_ReadWord(
3905 OFFSET_PS_NAMELENGTH
,
3906 &buffer
->sizeOfNameString
);
3908 StorageUtl_ReadDWord(
3910 OFFSET_PS_LEFTCHILD
,
3911 &buffer
->leftChild
);
3913 StorageUtl_ReadDWord(
3915 OFFSET_PS_RIGHTCHILD
,
3916 &buffer
->rightChild
);
3918 StorageUtl_ReadDWord(
3921 &buffer
->dirRootEntry
);
3923 StorageUtl_ReadGUID(
3928 StorageUtl_ReadDWord(
3931 &buffer
->ctime
.dwLowDateTime
);
3933 StorageUtl_ReadDWord(
3935 OFFSET_PS_CTIMEHIGH
,
3936 &buffer
->ctime
.dwHighDateTime
);
3938 StorageUtl_ReadDWord(
3941 &buffer
->mtime
.dwLowDateTime
);
3943 StorageUtl_ReadDWord(
3945 OFFSET_PS_MTIMEHIGH
,
3946 &buffer
->mtime
.dwHighDateTime
);
3948 StorageUtl_ReadDWord(
3950 OFFSET_PS_STARTBLOCK
,
3951 &buffer
->startingBlock
);
3953 StorageUtl_ReadDWord(
3956 &buffer
->size
.u
.LowPart
);
3958 buffer
->size
.u
.HighPart
= 0;
3964 /*********************************************************************
3965 * Write the specified directory entry to the file
3967 HRESULT
StorageImpl_WriteDirEntry(
3970 const DirEntry
* buffer
)
3972 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3975 UpdateRawDirEntry(currentEntry
, buffer
);
3977 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3981 static BOOL
StorageImpl_ReadBigBlock(
3986 ULARGE_INTEGER ulOffset
;
3989 ulOffset
.u
.HighPart
= 0;
3990 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3992 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3994 if (read
&& read
< This
->bigBlockSize
)
3996 /* File ends during this block; fill the rest with 0's. */
3997 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4003 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4009 ULARGE_INTEGER ulOffset
;
4013 ulOffset
.u
.HighPart
= 0;
4014 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4015 ulOffset
.u
.LowPart
+= offset
;
4017 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4018 *value
= lendian32toh(tmp
);
4019 return (read
== sizeof(DWORD
));
4022 static BOOL
StorageImpl_WriteBigBlock(
4027 ULARGE_INTEGER ulOffset
;
4030 ulOffset
.u
.HighPart
= 0;
4031 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4033 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4034 return (wrote
== This
->bigBlockSize
);
4037 static BOOL
StorageImpl_WriteDWordToBigBlock(
4043 ULARGE_INTEGER ulOffset
;
4046 ulOffset
.u
.HighPart
= 0;
4047 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4048 ulOffset
.u
.LowPart
+= offset
;
4050 value
= htole32(value
);
4051 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4052 return (wrote
== sizeof(DWORD
));
4055 /******************************************************************************
4056 * Storage32Impl_SmallBlocksToBigBlocks
4058 * This method will convert a small block chain to a big block chain.
4059 * The small block chain will be destroyed.
4061 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4063 SmallBlockChainStream
** ppsbChain
)
4065 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4066 ULARGE_INTEGER size
, offset
;
4067 ULONG cbRead
, cbWritten
;
4068 ULARGE_INTEGER cbTotalRead
;
4069 DirRef streamEntryRef
;
4070 HRESULT resWrite
= S_OK
;
4072 DirEntry streamEntry
;
4074 BlockChainStream
*bbTempChain
= NULL
;
4075 BlockChainStream
*bigBlockChain
= NULL
;
4078 * Create a temporary big block chain that doesn't have
4079 * an associated directory entry. This temporary chain will be
4080 * used to copy data from small blocks to big blocks.
4082 bbTempChain
= BlockChainStream_Construct(This
,
4085 if(!bbTempChain
) return NULL
;
4087 * Grow the big block chain.
4089 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4090 BlockChainStream_SetSize(bbTempChain
, size
);
4093 * Copy the contents of the small block chain to the big block chain
4094 * by small block size increments.
4096 offset
.u
.LowPart
= 0;
4097 offset
.u
.HighPart
= 0;
4098 cbTotalRead
.QuadPart
= 0;
4100 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4103 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4105 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4108 if (FAILED(resRead
))
4113 cbTotalRead
.QuadPart
+= cbRead
;
4115 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4121 if (FAILED(resWrite
))
4124 offset
.u
.LowPart
+= cbRead
;
4128 resRead
= STG_E_READFAULT
;
4131 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4132 HeapFree(GetProcessHeap(),0,buffer
);
4134 size
.u
.HighPart
= 0;
4137 if (FAILED(resRead
) || FAILED(resWrite
))
4139 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4140 BlockChainStream_SetSize(bbTempChain
, size
);
4141 BlockChainStream_Destroy(bbTempChain
);
4146 * Destroy the small block chain.
4148 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4149 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4150 SmallBlockChainStream_Destroy(*ppsbChain
);
4154 * Change the directory entry. This chain is now a big block chain
4155 * and it doesn't reside in the small blocks chain anymore.
4157 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4159 streamEntry
.startingBlock
= bbHeadOfChain
;
4161 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4164 * Destroy the temporary entryless big block chain.
4165 * Create a new big block chain associated with this entry.
4167 BlockChainStream_Destroy(bbTempChain
);
4168 bigBlockChain
= BlockChainStream_Construct(This
,
4172 return bigBlockChain
;
4175 /******************************************************************************
4176 * Storage32Impl_BigBlocksToSmallBlocks
4178 * This method will convert a big block chain to a small block chain.
4179 * The big block chain will be destroyed on success.
4181 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4183 BlockChainStream
** ppbbChain
,
4184 ULARGE_INTEGER newSize
)
4186 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4187 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4188 DirRef streamEntryRef
;
4189 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4190 DirEntry streamEntry
;
4192 SmallBlockChainStream
* sbTempChain
;
4194 TRACE("%p %p\n", This
, ppbbChain
);
4196 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4202 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4203 size
= BlockChainStream_GetSize(*ppbbChain
);
4204 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4206 offset
.u
.HighPart
= 0;
4207 offset
.u
.LowPart
= 0;
4208 cbTotalRead
.QuadPart
= 0;
4209 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4210 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4212 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4213 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4221 cbTotalRead
.QuadPart
+= cbRead
;
4223 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4224 cbRead
, buffer
, &cbWritten
);
4226 if(FAILED(resWrite
))
4229 offset
.u
.LowPart
+= cbRead
;
4233 resRead
= STG_E_READFAULT
;
4237 HeapFree(GetProcessHeap(), 0, buffer
);
4239 size
.u
.HighPart
= 0;
4242 if(FAILED(resRead
) || FAILED(resWrite
))
4244 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4245 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4246 SmallBlockChainStream_Destroy(sbTempChain
);
4250 /* destroy the original big block chain */
4251 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4252 BlockChainStream_SetSize(*ppbbChain
, size
);
4253 BlockChainStream_Destroy(*ppbbChain
);
4256 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4257 streamEntry
.startingBlock
= sbHeadOfChain
;
4258 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4260 SmallBlockChainStream_Destroy(sbTempChain
);
4261 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4264 static HRESULT
StorageBaseImpl_CopyStream(
4265 StorageBaseImpl
*dst
, DirRef dst_entry
,
4266 StorageBaseImpl
*src
, DirRef src_entry
)
4271 ULARGE_INTEGER bytes_copied
;
4272 ULONG bytestocopy
, bytesread
, byteswritten
;
4274 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4278 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4280 bytes_copied
.QuadPart
= 0;
4281 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4283 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4285 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4287 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4290 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4291 data
, &byteswritten
);
4294 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4295 bytes_copied
.QuadPart
+= byteswritten
;
4303 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4305 DirRef result
=This
->firstFreeEntry
;
4307 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4310 if (result
== This
->entries_size
)
4312 ULONG new_size
= This
->entries_size
* 2;
4313 TransactedDirEntry
*new_entries
;
4315 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4316 if (!new_entries
) return DIRENTRY_NULL
;
4318 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4319 HeapFree(GetProcessHeap(), 0, This
->entries
);
4321 This
->entries
= new_entries
;
4322 This
->entries_size
= new_size
;
4325 This
->entries
[result
].inuse
= 1;
4327 This
->firstFreeEntry
= result
+1;
4332 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4333 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4335 DirRef stubEntryRef
;
4336 TransactedDirEntry
*entry
;
4338 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4340 if (stubEntryRef
!= DIRENTRY_NULL
)
4342 entry
= &This
->entries
[stubEntryRef
];
4344 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4349 return stubEntryRef
;
4352 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4353 TransactedSnapshotImpl
*This
, DirRef entry
)
4358 if (!This
->entries
[entry
].read
)
4360 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4361 This
->entries
[entry
].transactedParentEntry
,
4364 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4366 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4368 if (data
.leftChild
== DIRENTRY_NULL
)
4372 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4374 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4376 if (data
.rightChild
== DIRENTRY_NULL
)
4380 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4382 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4384 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4390 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4391 This
->entries
[entry
].read
= 1;
4398 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4399 TransactedSnapshotImpl
*This
, DirRef entry
)
4403 if (!This
->entries
[entry
].stream_dirty
)
4405 DirEntry new_entrydata
;
4407 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4408 new_entrydata
.name
[0] = 'S';
4409 new_entrydata
.sizeOfNameString
= 1;
4410 new_entrydata
.stgType
= STGTY_STREAM
;
4411 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4412 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4413 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4414 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4416 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4417 &This
->entries
[entry
].stream_entry
);
4419 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4421 hr
= StorageBaseImpl_CopyStream(
4422 This
->scratch
, This
->entries
[entry
].stream_entry
,
4423 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4426 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4430 This
->entries
[entry
].stream_dirty
= 1;
4432 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4434 /* Since this entry is modified, and we aren't using its stream data, we
4435 * no longer care about the original entry. */
4437 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4439 if (delete_ref
!= DIRENTRY_NULL
)
4440 This
->entries
[delete_ref
].deleted
= 1;
4442 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4449 /* Find the first entry in a depth-first traversal. */
4450 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4451 TransactedSnapshotImpl
* This
, DirRef parent
)
4453 DirRef cursor
, prev
;
4454 TransactedDirEntry
*entry
;
4457 entry
= &This
->entries
[cursor
];
4460 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4463 cursor
= entry
->data
.leftChild
;
4464 entry
= &This
->entries
[cursor
];
4465 entry
->parent
= prev
;
4467 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4470 cursor
= entry
->data
.rightChild
;
4471 entry
= &This
->entries
[cursor
];
4472 entry
->parent
= prev
;
4474 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4477 cursor
= entry
->data
.dirRootEntry
;
4478 entry
= &This
->entries
[cursor
];
4479 entry
->parent
= prev
;
4488 /* Find the next entry in a depth-first traversal. */
4489 static DirRef
TransactedSnapshotImpl_FindNextChild(
4490 TransactedSnapshotImpl
* This
, DirRef current
)
4493 TransactedDirEntry
*parent_entry
;
4495 parent
= This
->entries
[current
].parent
;
4496 parent_entry
= &This
->entries
[parent
];
4498 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4500 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4502 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4503 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4506 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4508 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4509 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4516 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4517 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4518 TransactedSnapshotImpl
* This
, DirRef entry
)
4520 return entry
!= DIRENTRY_NULL
&&
4521 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4524 /* Destroy the entries created by CopyTree. */
4525 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4526 TransactedSnapshotImpl
* This
, DirRef stop
)
4529 TransactedDirEntry
*entry
;
4530 ULARGE_INTEGER zero
;
4534 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4537 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4539 if (cursor
== DIRENTRY_NULL
)
4542 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4544 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4546 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4548 entry
= &This
->entries
[cursor
];
4550 if (entry
->stream_dirty
)
4551 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4552 entry
->newTransactedParentEntry
, zero
);
4554 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4555 entry
->newTransactedParentEntry
);
4557 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4560 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4564 /* Make a copy of our edited tree that we can use in the parent. */
4565 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4568 TransactedDirEntry
*entry
;
4571 cursor
= This
->base
.storageDirEntry
;
4572 entry
= &This
->entries
[cursor
];
4573 entry
->parent
= DIRENTRY_NULL
;
4574 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4576 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4579 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4581 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4582 entry
= &This
->entries
[cursor
];
4584 while (cursor
!= DIRENTRY_NULL
)
4586 /* Make a copy of this entry in the transacted parent. */
4588 (!entry
->dirty
&& !entry
->stream_dirty
&&
4589 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4590 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4591 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4592 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4597 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4599 newData
.size
.QuadPart
= 0;
4600 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4602 if (newData
.leftChild
!= DIRENTRY_NULL
)
4603 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4605 if (newData
.rightChild
!= DIRENTRY_NULL
)
4606 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4608 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4609 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4611 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4612 &entry
->newTransactedParentEntry
);
4615 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4619 if (entry
->stream_dirty
)
4621 hr
= StorageBaseImpl_CopyStream(
4622 This
->transactedParent
, entry
->newTransactedParentEntry
,
4623 This
->scratch
, entry
->stream_entry
);
4625 else if (entry
->data
.size
.QuadPart
)
4627 hr
= StorageBaseImpl_StreamLink(
4628 This
->transactedParent
, entry
->newTransactedParentEntry
,
4629 entry
->transactedParentEntry
);
4634 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4635 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4640 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4641 entry
= &This
->entries
[cursor
];
4647 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4649 DWORD grfCommitFlags
) /* [in] */
4651 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4652 TransactedDirEntry
*root_entry
;
4653 DirRef i
, dir_root_ref
;
4655 ULARGE_INTEGER zero
;
4660 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4662 /* Cannot commit a read-only transacted storage */
4663 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4664 return STG_E_ACCESSDENIED
;
4666 /* To prevent data loss, we create the new structure in the file before we
4667 * delete the old one, so that in case of errors the old data is intact. We
4668 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4669 * needed in the rare situation where we have just enough free disk space to
4670 * overwrite the existing data. */
4672 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4674 if (!root_entry
->read
)
4677 hr
= TransactedSnapshotImpl_CopyTree(This
);
4678 if (FAILED(hr
)) return hr
;
4680 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4681 dir_root_ref
= DIRENTRY_NULL
;
4683 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4685 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4687 /* Update the storage to use the new data in one step. */
4689 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4690 root_entry
->transactedParentEntry
, &data
);
4694 data
.dirRootEntry
= dir_root_ref
;
4695 data
.clsid
= root_entry
->data
.clsid
;
4696 data
.ctime
= root_entry
->data
.ctime
;
4697 data
.mtime
= root_entry
->data
.mtime
;
4699 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4700 root_entry
->transactedParentEntry
, &data
);
4703 /* Try to flush after updating the root storage, but if the flush fails, keep
4704 * going, on the theory that it'll either succeed later or the subsequent
4705 * writes will fail. */
4706 StorageBaseImpl_Flush(This
->transactedParent
);
4710 /* Destroy the old now-orphaned data. */
4711 for (i
=0; i
<This
->entries_size
; i
++)
4713 TransactedDirEntry
*entry
= &This
->entries
[i
];
4718 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4719 entry
->transactedParentEntry
, zero
);
4720 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4721 entry
->transactedParentEntry
);
4722 memset(entry
, 0, sizeof(TransactedDirEntry
));
4723 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4725 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4727 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4728 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4729 entry
->transactedParentEntry
);
4730 if (entry
->stream_dirty
)
4732 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4733 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4734 entry
->stream_dirty
= 0;
4737 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4744 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4748 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4753 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4756 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
4757 ULARGE_INTEGER zero
;
4760 TRACE("(%p)\n", iface
);
4762 /* Destroy the open objects. */
4763 StorageBaseImpl_DeleteAll(&This
->base
);
4765 /* Clear out the scratch file. */
4767 for (i
=0; i
<This
->entries_size
; i
++)
4769 if (This
->entries
[i
].stream_dirty
)
4771 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4774 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4778 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4780 This
->firstFreeEntry
= 0;
4781 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4786 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4788 if (!This
->reverted
)
4790 TRACE("Storage invalidated (stg=%p)\n", This
);
4794 StorageBaseImpl_DeleteAll(This
);
4798 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4800 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4802 IStorage_Revert(&This
->base
.IStorage_iface
);
4803 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
4804 IStorage_Release(&This
->scratch
->IStorage_iface
);
4805 HeapFree(GetProcessHeap(), 0, This
->entries
);
4806 HeapFree(GetProcessHeap(), 0, This
);
4809 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4811 /* We only need to flush when committing. */
4815 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4817 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4819 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4822 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4823 const DirEntry
*newData
, DirRef
*index
)
4825 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4827 TransactedDirEntry
*new_entry
;
4829 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4830 if (new_ref
== DIRENTRY_NULL
)
4831 return E_OUTOFMEMORY
;
4833 new_entry
= &This
->entries
[new_ref
];
4835 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4836 new_entry
->read
= 1;
4837 new_entry
->dirty
= 1;
4838 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4842 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4847 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4848 DirRef index
, const DirEntry
*data
)
4850 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4853 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4855 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4856 if (FAILED(hr
)) return hr
;
4858 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4860 if (index
!= This
->base
.storageDirEntry
)
4862 This
->entries
[index
].dirty
= 1;
4864 if (data
->size
.QuadPart
== 0 &&
4865 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4867 /* Since this entry is modified, and we aren't using its stream data, we
4868 * no longer care about the original entry. */
4870 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4872 if (delete_ref
!= DIRENTRY_NULL
)
4873 This
->entries
[delete_ref
].deleted
= 1;
4875 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4882 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4883 DirRef index
, DirEntry
*data
)
4885 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4888 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4889 if (FAILED(hr
)) return hr
;
4891 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4893 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4898 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4901 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4903 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4904 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4906 /* If we deleted this entry while it has stream data. We must have left the
4907 * data because some other entry is using it, and we need to leave the
4908 * original entry alone. */
4909 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4910 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4914 This
->entries
[index
].deleted
= 1;
4920 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4921 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4923 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4925 if (This
->entries
[index
].stream_dirty
)
4927 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4928 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4930 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4932 /* This stream doesn't live in the parent, and we haven't allocated storage
4939 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4940 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4944 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4945 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4947 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4950 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4951 if (FAILED(hr
)) return hr
;
4953 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4954 if (FAILED(hr
)) return hr
;
4956 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4957 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4959 if (SUCCEEDED(hr
) && size
!= 0)
4960 This
->entries
[index
].data
.size
.QuadPart
= max(
4961 This
->entries
[index
].data
.size
.QuadPart
,
4962 offset
.QuadPart
+ size
);
4967 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4968 DirRef index
, ULARGE_INTEGER newsize
)
4970 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4973 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4974 if (FAILED(hr
)) return hr
;
4976 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4979 if (newsize
.QuadPart
== 0)
4981 /* Destroy any parent references or entries in the scratch file. */
4982 if (This
->entries
[index
].stream_dirty
)
4984 ULARGE_INTEGER zero
;
4986 StorageBaseImpl_StreamSetSize(This
->scratch
,
4987 This
->entries
[index
].stream_entry
, zero
);
4988 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4989 This
->entries
[index
].stream_entry
);
4990 This
->entries
[index
].stream_dirty
= 0;
4992 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4995 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4997 if (delete_ref
!= DIRENTRY_NULL
)
4998 This
->entries
[delete_ref
].deleted
= 1;
5000 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5005 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5006 if (FAILED(hr
)) return hr
;
5008 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5009 This
->entries
[index
].stream_entry
, newsize
);
5013 This
->entries
[index
].data
.size
= newsize
;
5018 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5019 DirRef dst
, DirRef src
)
5021 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5023 TransactedDirEntry
*dst_entry
, *src_entry
;
5025 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5026 if (FAILED(hr
)) return hr
;
5028 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5029 if (FAILED(hr
)) return hr
;
5031 dst_entry
= &This
->entries
[dst
];
5032 src_entry
= &This
->entries
[src
];
5034 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5035 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5036 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5037 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5038 dst_entry
->data
.size
= src_entry
->data
.size
;
5043 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5045 StorageBaseImpl_QueryInterface
,
5046 StorageBaseImpl_AddRef
,
5047 StorageBaseImpl_Release
,
5048 StorageBaseImpl_CreateStream
,
5049 StorageBaseImpl_OpenStream
,
5050 StorageBaseImpl_CreateStorage
,
5051 StorageBaseImpl_OpenStorage
,
5052 StorageBaseImpl_CopyTo
,
5053 StorageBaseImpl_MoveElementTo
,
5054 TransactedSnapshotImpl_Commit
,
5055 TransactedSnapshotImpl_Revert
,
5056 StorageBaseImpl_EnumElements
,
5057 StorageBaseImpl_DestroyElement
,
5058 StorageBaseImpl_RenameElement
,
5059 StorageBaseImpl_SetElementTimes
,
5060 StorageBaseImpl_SetClass
,
5061 StorageBaseImpl_SetStateBits
,
5062 StorageBaseImpl_Stat
5065 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5067 TransactedSnapshotImpl_Destroy
,
5068 TransactedSnapshotImpl_Invalidate
,
5069 TransactedSnapshotImpl_Flush
,
5070 TransactedSnapshotImpl_GetFilename
,
5071 TransactedSnapshotImpl_CreateDirEntry
,
5072 TransactedSnapshotImpl_WriteDirEntry
,
5073 TransactedSnapshotImpl_ReadDirEntry
,
5074 TransactedSnapshotImpl_DestroyDirEntry
,
5075 TransactedSnapshotImpl_StreamReadAt
,
5076 TransactedSnapshotImpl_StreamWriteAt
,
5077 TransactedSnapshotImpl_StreamSetSize
,
5078 TransactedSnapshotImpl_StreamLink
5081 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5082 TransactedSnapshotImpl
** result
)
5086 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5091 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5093 /* This is OK because the property set storage functions use the IStorage functions. */
5094 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5095 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5097 list_init(&(*result
)->base
.strmHead
);
5099 list_init(&(*result
)->base
.storageHead
);
5101 (*result
)->base
.ref
= 1;
5103 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5105 /* Create a new temporary storage to act as the scratch file. */
5106 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5108 (*result
)->scratch
= impl_from_IStorage(scratch
);
5112 ULONG num_entries
= 20;
5114 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5115 (*result
)->entries_size
= num_entries
;
5116 (*result
)->firstFreeEntry
= 0;
5118 if ((*result
)->entries
)
5120 /* parentStorage already has 1 reference, which we take over here. */
5121 (*result
)->transactedParent
= parentStorage
;
5123 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5125 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5129 IStorage_Release(scratch
);
5135 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5140 return E_OUTOFMEMORY
;
5143 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5144 StorageBaseImpl
** result
)
5148 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5150 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5153 return TransactedSnapshotImpl_Construct(parentStorage
,
5154 (TransactedSnapshotImpl
**)result
);
5157 static HRESULT
Storage_Construct(
5165 StorageBaseImpl
** result
)
5167 StorageImpl
*newStorage
;
5168 StorageBaseImpl
*newTransactedStorage
;
5171 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5172 if (FAILED(hr
)) goto end
;
5174 if (openFlags
& STGM_TRANSACTED
)
5176 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5178 IStorage_Release(&newStorage
->base
.IStorage_iface
);
5180 *result
= newTransactedStorage
;
5183 *result
= &newStorage
->base
;
5189 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5191 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5193 if (!This
->base
.reverted
)
5195 TRACE("Storage invalidated (stg=%p)\n", This
);
5197 This
->base
.reverted
= 1;
5199 This
->parentStorage
= NULL
;
5201 StorageBaseImpl_DeleteAll(&This
->base
);
5203 list_remove(&This
->ParentListEntry
);
5207 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5209 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5211 StorageInternalImpl_Invalidate(&This
->base
);
5213 HeapFree(GetProcessHeap(), 0, This
);
5216 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5218 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5220 return StorageBaseImpl_Flush(This
->parentStorage
);
5223 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5225 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5227 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5230 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5231 const DirEntry
*newData
, DirRef
*index
)
5233 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5235 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5239 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5240 DirRef index
, const DirEntry
*data
)
5242 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5244 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5248 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5249 DirRef index
, DirEntry
*data
)
5251 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5253 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5257 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5260 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5262 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5266 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5267 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5269 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5271 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5272 index
, offset
, size
, buffer
, bytesRead
);
5275 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5276 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5278 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5280 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5281 index
, offset
, size
, buffer
, bytesWritten
);
5284 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5285 DirRef index
, ULARGE_INTEGER newsize
)
5287 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5289 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5293 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5294 DirRef dst
, DirRef src
)
5296 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5298 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5302 /******************************************************************************
5304 ** Storage32InternalImpl_Commit
5307 static HRESULT WINAPI
StorageInternalImpl_Commit(
5309 DWORD grfCommitFlags
) /* [in] */
5311 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
5312 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5313 return StorageBaseImpl_Flush(This
);
5316 /******************************************************************************
5318 ** Storage32InternalImpl_Revert
5321 static HRESULT WINAPI
StorageInternalImpl_Revert(
5324 FIXME("(%p): stub\n", iface
);
5328 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5330 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
5331 HeapFree(GetProcessHeap(), 0, This
);
5334 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5335 IEnumSTATSTG
* iface
,
5339 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5342 return E_INVALIDARG
;
5346 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5347 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5350 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5354 return E_NOINTERFACE
;
5357 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5358 IEnumSTATSTG
* iface
)
5360 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5361 return InterlockedIncrement(&This
->ref
);
5364 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5365 IEnumSTATSTG
* iface
)
5367 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5371 newRef
= InterlockedDecrement(&This
->ref
);
5375 IEnumSTATSTGImpl_Destroy(This
);
5381 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5382 IEnumSTATSTGImpl
* This
,
5385 DirRef result
= DIRENTRY_NULL
;
5389 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5391 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5392 This
->parentStorage
->storageDirEntry
, &entry
);
5393 searchNode
= entry
.dirRootEntry
;
5395 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5397 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5401 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5405 searchNode
= entry
.rightChild
;
5409 result
= searchNode
;
5410 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5411 searchNode
= entry
.leftChild
;
5419 if (result
!= DIRENTRY_NULL
)
5420 memcpy(This
->name
, result_name
, sizeof(result_name
));
5426 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5427 IEnumSTATSTG
* iface
,
5430 ULONG
* pceltFetched
)
5432 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5434 DirEntry currentEntry
;
5435 STATSTG
* currentReturnStruct
= rgelt
;
5436 ULONG objectFetched
= 0;
5437 DirRef currentSearchNode
;
5440 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5441 return E_INVALIDARG
;
5443 if (This
->parentStorage
->reverted
)
5444 return STG_E_REVERTED
;
5447 * To avoid the special case, get another pointer to a ULONG value if
5448 * the caller didn't supply one.
5450 if (pceltFetched
==0)
5451 pceltFetched
= &objectFetched
;
5454 * Start the iteration, we will iterate until we hit the end of the
5455 * linked list or until we hit the number of items to iterate through
5459 while ( *pceltFetched
< celt
)
5461 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5463 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5467 * Read the entry from the storage.
5469 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5474 * Copy the information to the return buffer.
5476 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5477 currentReturnStruct
,
5482 * Step to the next item in the iteration
5485 currentReturnStruct
++;
5488 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5495 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5496 IEnumSTATSTG
* iface
,
5499 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5501 ULONG objectFetched
= 0;
5502 DirRef currentSearchNode
;
5505 if (This
->parentStorage
->reverted
)
5506 return STG_E_REVERTED
;
5508 while ( (objectFetched
< celt
) )
5510 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5512 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5518 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5524 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5525 IEnumSTATSTG
* iface
)
5527 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5529 if (This
->parentStorage
->reverted
)
5530 return STG_E_REVERTED
;
5537 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5538 IEnumSTATSTG
* iface
,
5539 IEnumSTATSTG
** ppenum
)
5541 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5543 IEnumSTATSTGImpl
* newClone
;
5545 if (This
->parentStorage
->reverted
)
5546 return STG_E_REVERTED
;
5549 * Perform a sanity check on the parameters.
5552 return E_INVALIDARG
;
5554 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5555 This
->storageDirEntry
);
5559 * The new clone enumeration must point to the same current node as
5562 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5564 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5567 * Don't forget to nail down a reference to the clone before
5570 IEnumSTATSTGImpl_AddRef(*ppenum
);
5576 * Virtual function table for the IEnumSTATSTGImpl class.
5578 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5580 IEnumSTATSTGImpl_QueryInterface
,
5581 IEnumSTATSTGImpl_AddRef
,
5582 IEnumSTATSTGImpl_Release
,
5583 IEnumSTATSTGImpl_Next
,
5584 IEnumSTATSTGImpl_Skip
,
5585 IEnumSTATSTGImpl_Reset
,
5586 IEnumSTATSTGImpl_Clone
5589 /******************************************************************************
5590 ** IEnumSTATSTGImpl implementation
5593 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5594 StorageBaseImpl
* parentStorage
,
5595 DirRef storageDirEntry
)
5597 IEnumSTATSTGImpl
* newEnumeration
;
5599 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5601 if (newEnumeration
!=0)
5604 * Set-up the virtual function table and reference count.
5606 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5607 newEnumeration
->ref
= 0;
5610 * We want to nail-down the reference to the storage in case the
5611 * enumeration out-lives the storage in the client application.
5613 newEnumeration
->parentStorage
= parentStorage
;
5614 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
5616 newEnumeration
->storageDirEntry
= storageDirEntry
;
5619 * Make sure the current node of the iterator is the first one.
5621 IEnumSTATSTGImpl_Reset(&newEnumeration
->IEnumSTATSTG_iface
);
5624 return newEnumeration
;
5628 * Virtual function table for the Storage32InternalImpl class.
5630 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5632 StorageBaseImpl_QueryInterface
,
5633 StorageBaseImpl_AddRef
,
5634 StorageBaseImpl_Release
,
5635 StorageBaseImpl_CreateStream
,
5636 StorageBaseImpl_OpenStream
,
5637 StorageBaseImpl_CreateStorage
,
5638 StorageBaseImpl_OpenStorage
,
5639 StorageBaseImpl_CopyTo
,
5640 StorageBaseImpl_MoveElementTo
,
5641 StorageInternalImpl_Commit
,
5642 StorageInternalImpl_Revert
,
5643 StorageBaseImpl_EnumElements
,
5644 StorageBaseImpl_DestroyElement
,
5645 StorageBaseImpl_RenameElement
,
5646 StorageBaseImpl_SetElementTimes
,
5647 StorageBaseImpl_SetClass
,
5648 StorageBaseImpl_SetStateBits
,
5649 StorageBaseImpl_Stat
5652 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5654 StorageInternalImpl_Destroy
,
5655 StorageInternalImpl_Invalidate
,
5656 StorageInternalImpl_Flush
,
5657 StorageInternalImpl_GetFilename
,
5658 StorageInternalImpl_CreateDirEntry
,
5659 StorageInternalImpl_WriteDirEntry
,
5660 StorageInternalImpl_ReadDirEntry
,
5661 StorageInternalImpl_DestroyDirEntry
,
5662 StorageInternalImpl_StreamReadAt
,
5663 StorageInternalImpl_StreamWriteAt
,
5664 StorageInternalImpl_StreamSetSize
,
5665 StorageInternalImpl_StreamLink
5668 /******************************************************************************
5669 ** Storage32InternalImpl implementation
5672 static StorageInternalImpl
* StorageInternalImpl_Construct(
5673 StorageBaseImpl
* parentStorage
,
5675 DirRef storageDirEntry
)
5677 StorageInternalImpl
* newStorage
;
5679 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5683 list_init(&newStorage
->base
.strmHead
);
5685 list_init(&newStorage
->base
.storageHead
);
5688 * Initialize the virtual function table.
5690 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5691 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
5692 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5693 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5695 newStorage
->base
.reverted
= 0;
5697 newStorage
->base
.ref
= 1;
5699 newStorage
->parentStorage
= parentStorage
;
5702 * Keep a reference to the directory entry of this storage
5704 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5706 newStorage
->base
.create
= 0;
5714 /******************************************************************************
5715 ** StorageUtl implementation
5718 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5722 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5723 *value
= lendian16toh(tmp
);
5726 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5728 value
= htole16(value
);
5729 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5732 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5736 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5737 *value
= lendian32toh(tmp
);
5740 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5742 value
= htole32(value
);
5743 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5746 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5747 ULARGE_INTEGER
* value
)
5749 #ifdef WORDS_BIGENDIAN
5752 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5753 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5754 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5756 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5760 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5761 const ULARGE_INTEGER
*value
)
5763 #ifdef WORDS_BIGENDIAN
5766 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5767 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5768 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5770 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5774 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5776 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5777 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5778 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5780 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5783 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5785 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5786 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5787 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5789 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5792 void StorageUtl_CopyDirEntryToSTATSTG(
5793 StorageBaseImpl
* storage
,
5794 STATSTG
* destination
,
5795 const DirEntry
* source
,
5799 * The copy of the string occurs only when the flag is not set
5801 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5803 /* Use the filename for the root storage. */
5804 destination
->pwcsName
= 0;
5805 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5807 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5808 (source
->name
[0] == 0) )
5810 destination
->pwcsName
= 0;
5814 destination
->pwcsName
=
5815 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5817 strcpyW(destination
->pwcsName
, source
->name
);
5820 switch (source
->stgType
)
5824 destination
->type
= STGTY_STORAGE
;
5827 destination
->type
= STGTY_STREAM
;
5830 destination
->type
= STGTY_STREAM
;
5834 destination
->cbSize
= source
->size
;
5836 currentReturnStruct->mtime = {0}; TODO
5837 currentReturnStruct->ctime = {0};
5838 currentReturnStruct->atime = {0};
5840 destination
->grfMode
= 0;
5841 destination
->grfLocksSupported
= 0;
5842 destination
->clsid
= source
->clsid
;
5843 destination
->grfStateBits
= 0;
5844 destination
->reserved
= 0;
5847 /******************************************************************************
5848 ** BlockChainStream implementation
5851 /* Read and save the index of all blocks in this stream. */
5852 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5854 ULONG next_sector
, next_offset
;
5856 struct BlockChainRun
*last_run
;
5858 if (This
->indexCacheLen
== 0)
5862 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5866 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5867 next_offset
= last_run
->lastOffset
+1;
5868 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5869 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5871 if (FAILED(hr
)) return hr
;
5874 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5876 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5878 /* Add the current block to the cache. */
5879 if (This
->indexCacheSize
== 0)
5881 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5882 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5883 This
->indexCacheSize
= 16;
5885 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5887 struct BlockChainRun
*new_cache
;
5890 new_size
= This
->indexCacheSize
* 2;
5891 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5892 if (!new_cache
) return E_OUTOFMEMORY
;
5893 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5895 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5896 This
->indexCache
= new_cache
;
5897 This
->indexCacheSize
= new_size
;
5900 This
->indexCacheLen
++;
5901 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5902 last_run
->firstSector
= next_sector
;
5903 last_run
->firstOffset
= next_offset
;
5906 last_run
->lastOffset
= next_offset
;
5908 /* Find the next block. */
5910 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5911 if (FAILED(hr
)) return hr
;
5914 if (This
->indexCacheLen
)
5916 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5917 This
->numBlocks
= last_run
->lastOffset
+1;
5921 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5922 This
->numBlocks
= 0;
5928 /* Locate the nth block in this stream. */
5929 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5931 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5932 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5934 if (offset
>= This
->numBlocks
)
5935 return BLOCK_END_OF_CHAIN
;
5937 while (min_run
< max_run
)
5939 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5940 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5942 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5943 max_run
= run_to_check
-1;
5945 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5947 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5948 min_run
= run_to_check
+1;
5951 /* Block is in this run. */
5952 min_run
= max_run
= run_to_check
;
5955 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5958 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5959 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5961 BlockChainBlock
*result
=NULL
;
5965 if (This
->cachedBlocks
[i
].index
== index
)
5967 *sector
= This
->cachedBlocks
[i
].sector
;
5968 *block
= &This
->cachedBlocks
[i
];
5972 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5973 if (*sector
== BLOCK_END_OF_CHAIN
)
5974 return STG_E_DOCFILECORRUPT
;
5978 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5979 result
= &This
->cachedBlocks
[0];
5980 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5981 result
= &This
->cachedBlocks
[1];
5984 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5985 if (This
->blockToEvict
== 2)
5986 This
->blockToEvict
= 0;
5991 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5992 return STG_E_WRITEFAULT
;
5997 result
->index
= index
;
5998 result
->sector
= *sector
;
6005 BlockChainStream
* BlockChainStream_Construct(
6006 StorageImpl
* parentStorage
,
6007 ULONG
* headOfStreamPlaceHolder
,
6010 BlockChainStream
* newStream
;
6012 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6014 newStream
->parentStorage
= parentStorage
;
6015 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6016 newStream
->ownerDirEntry
= dirEntry
;
6017 newStream
->indexCache
= NULL
;
6018 newStream
->indexCacheLen
= 0;
6019 newStream
->indexCacheSize
= 0;
6020 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6021 newStream
->cachedBlocks
[0].dirty
= 0;
6022 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6023 newStream
->cachedBlocks
[1].dirty
= 0;
6024 newStream
->blockToEvict
= 0;
6026 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6028 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6029 HeapFree(GetProcessHeap(), 0, newStream
);
6036 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6039 if (!This
) return S_OK
;
6042 if (This
->cachedBlocks
[i
].dirty
)
6044 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6045 This
->cachedBlocks
[i
].dirty
= 0;
6047 return STG_E_WRITEFAULT
;
6053 void BlockChainStream_Destroy(BlockChainStream
* This
)
6057 BlockChainStream_Flush(This
);
6058 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6060 HeapFree(GetProcessHeap(), 0, This
);
6063 /******************************************************************************
6064 * BlockChainStream_GetHeadOfChain
6066 * Returns the head of this stream chain.
6067 * Some special chains don't have directory entries, their heads are kept in
6068 * This->headOfStreamPlaceHolder.
6071 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6073 DirEntry chainEntry
;
6076 if (This
->headOfStreamPlaceHolder
!= 0)
6077 return *(This
->headOfStreamPlaceHolder
);
6079 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6081 hr
= StorageImpl_ReadDirEntry(
6082 This
->parentStorage
,
6083 This
->ownerDirEntry
,
6088 return chainEntry
.startingBlock
;
6092 return BLOCK_END_OF_CHAIN
;
6095 /******************************************************************************
6096 * BlockChainStream_GetCount
6098 * Returns the number of blocks that comprises this chain.
6099 * This is not the size of the stream as the last block may not be full!
6101 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6103 return This
->numBlocks
;
6106 /******************************************************************************
6107 * BlockChainStream_ReadAt
6109 * Reads a specified number of bytes from this chain at the specified offset.
6110 * bytesRead may be NULL.
6111 * Failure will be returned if the specified number of bytes has not been read.
6113 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6114 ULARGE_INTEGER offset
,
6119 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6120 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6121 ULONG bytesToReadInBuffer
;
6124 ULARGE_INTEGER stream_size
;
6126 BlockChainBlock
*cachedBlock
;
6128 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6131 * Find the first block in the stream that contains part of the buffer.
6133 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6137 stream_size
= BlockChainStream_GetSize(This
);
6138 if (stream_size
.QuadPart
> offset
.QuadPart
)
6139 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6144 * Start reading the buffer.
6146 bufferWalker
= buffer
;
6150 ULARGE_INTEGER ulOffset
;
6154 * Calculate how many bytes we can copy from this big block.
6156 bytesToReadInBuffer
=
6157 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6159 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6166 /* Not in cache, and we're going to read past the end of the block. */
6167 ulOffset
.u
.HighPart
= 0;
6168 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6171 StorageImpl_ReadAt(This
->parentStorage
,
6174 bytesToReadInBuffer
,
6179 if (!cachedBlock
->read
)
6181 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6182 return STG_E_READFAULT
;
6184 cachedBlock
->read
= 1;
6187 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6188 bytesReadAt
= bytesToReadInBuffer
;
6191 blockNoInSequence
++;
6192 bufferWalker
+= bytesReadAt
;
6193 size
-= bytesReadAt
;
6194 *bytesRead
+= bytesReadAt
;
6195 offsetInBlock
= 0; /* There is no offset on the next block */
6197 if (bytesToReadInBuffer
!= bytesReadAt
)
6204 /******************************************************************************
6205 * BlockChainStream_WriteAt
6207 * Writes the specified number of bytes to this chain at the specified offset.
6208 * Will fail if not all specified number of bytes have been written.
6210 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6211 ULARGE_INTEGER offset
,
6214 ULONG
* bytesWritten
)
6216 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6217 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6220 const BYTE
* bufferWalker
;
6222 BlockChainBlock
*cachedBlock
;
6225 bufferWalker
= buffer
;
6229 ULARGE_INTEGER ulOffset
;
6230 DWORD bytesWrittenAt
;
6233 * Calculate how many bytes we can copy to this big block.
6236 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6238 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6240 /* BlockChainStream_SetSize should have already been called to ensure we have
6241 * enough blocks in the chain to write into */
6244 ERR("not enough blocks in chain to write data\n");
6250 /* Not in cache, and we're going to write past the end of the block. */
6251 ulOffset
.u
.HighPart
= 0;
6252 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6255 StorageImpl_WriteAt(This
->parentStorage
,
6263 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6265 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6266 return STG_E_READFAULT
;
6269 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6270 bytesWrittenAt
= bytesToWrite
;
6271 cachedBlock
->read
= 1;
6272 cachedBlock
->dirty
= 1;
6275 blockNoInSequence
++;
6276 bufferWalker
+= bytesWrittenAt
;
6277 size
-= bytesWrittenAt
;
6278 *bytesWritten
+= bytesWrittenAt
;
6279 offsetInBlock
= 0; /* There is no offset on the next block */
6281 if (bytesWrittenAt
!= bytesToWrite
)
6285 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6288 /******************************************************************************
6289 * BlockChainStream_Shrink
6291 * Shrinks this chain in the big block depot.
6293 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6294 ULARGE_INTEGER newSize
)
6301 * Figure out how many blocks are needed to contain the new size
6303 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6305 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6311 * Go to the new end of chain
6313 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6315 /* Mark the new end of chain */
6316 StorageImpl_SetNextBlockInChain(
6317 This
->parentStorage
,
6319 BLOCK_END_OF_CHAIN
);
6321 This
->tailIndex
= blockIndex
;
6325 if (This
->headOfStreamPlaceHolder
!= 0)
6327 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6331 DirEntry chainEntry
;
6332 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6334 StorageImpl_ReadDirEntry(
6335 This
->parentStorage
,
6336 This
->ownerDirEntry
,
6339 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6341 StorageImpl_WriteDirEntry(
6342 This
->parentStorage
,
6343 This
->ownerDirEntry
,
6347 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6350 This
->numBlocks
= numBlocks
;
6353 * Mark the extra blocks as free
6355 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6357 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6358 StorageImpl_FreeBigBlock(This
->parentStorage
,
6359 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6360 if (last_run
->lastOffset
== last_run
->firstOffset
)
6361 This
->indexCacheLen
--;
6363 last_run
->lastOffset
--;
6367 * Reset the last accessed block cache.
6371 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6373 This
->cachedBlocks
[i
].index
= 0xffffffff;
6374 This
->cachedBlocks
[i
].dirty
= 0;
6381 /******************************************************************************
6382 * BlockChainStream_Enlarge
6384 * Grows this chain in the big block depot.
6386 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6387 ULARGE_INTEGER newSize
)
6389 ULONG blockIndex
, currentBlock
;
6391 ULONG oldNumBlocks
= 0;
6393 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6396 * Empty chain. Create the head.
6398 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6400 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6401 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6403 BLOCK_END_OF_CHAIN
);
6405 if (This
->headOfStreamPlaceHolder
!= 0)
6407 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6411 DirEntry chainEntry
;
6412 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6414 StorageImpl_ReadDirEntry(
6415 This
->parentStorage
,
6416 This
->ownerDirEntry
,
6419 chainEntry
.startingBlock
= blockIndex
;
6421 StorageImpl_WriteDirEntry(
6422 This
->parentStorage
,
6423 This
->ownerDirEntry
,
6427 This
->tailIndex
= blockIndex
;
6428 This
->numBlocks
= 1;
6432 * Figure out how many blocks are needed to contain this stream
6434 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6436 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6440 * Go to the current end of chain
6442 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6444 currentBlock
= blockIndex
;
6446 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6449 currentBlock
= blockIndex
;
6451 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6456 This
->tailIndex
= currentBlock
;
6459 currentBlock
= This
->tailIndex
;
6460 oldNumBlocks
= This
->numBlocks
;
6463 * Add new blocks to the chain
6465 if (oldNumBlocks
< newNumBlocks
)
6467 while (oldNumBlocks
< newNumBlocks
)
6469 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6471 StorageImpl_SetNextBlockInChain(
6472 This
->parentStorage
,
6476 StorageImpl_SetNextBlockInChain(
6477 This
->parentStorage
,
6479 BLOCK_END_OF_CHAIN
);
6481 currentBlock
= blockIndex
;
6485 This
->tailIndex
= blockIndex
;
6486 This
->numBlocks
= newNumBlocks
;
6489 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6495 /******************************************************************************
6496 * BlockChainStream_SetSize
6498 * Sets the size of this stream. The big block depot will be updated.
6499 * The file will grow if we grow the chain.
6501 * TODO: Free the actual blocks in the file when we shrink the chain.
6502 * Currently, the blocks are still in the file. So the file size
6503 * doesn't shrink even if we shrink streams.
6505 BOOL
BlockChainStream_SetSize(
6506 BlockChainStream
* This
,
6507 ULARGE_INTEGER newSize
)
6509 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6511 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6514 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6516 BlockChainStream_Shrink(This
, newSize
);
6520 BlockChainStream_Enlarge(This
, newSize
);
6526 /******************************************************************************
6527 * BlockChainStream_GetSize
6529 * Returns the size of this chain.
6530 * Will return the block count if this chain doesn't have a directory entry.
6532 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6534 DirEntry chainEntry
;
6536 if(This
->headOfStreamPlaceHolder
== NULL
)
6539 * This chain has a directory entry so use the size value from there.
6541 StorageImpl_ReadDirEntry(
6542 This
->parentStorage
,
6543 This
->ownerDirEntry
,
6546 return chainEntry
.size
;
6551 * this chain is a chain that does not have a directory entry, figure out the
6552 * size by making the product number of used blocks times the
6555 ULARGE_INTEGER result
;
6556 result
.u
.HighPart
= 0;
6559 BlockChainStream_GetCount(This
) *
6560 This
->parentStorage
->bigBlockSize
;
6566 /******************************************************************************
6567 ** SmallBlockChainStream implementation
6570 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6571 StorageImpl
* parentStorage
,
6572 ULONG
* headOfStreamPlaceHolder
,
6575 SmallBlockChainStream
* newStream
;
6577 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6579 newStream
->parentStorage
= parentStorage
;
6580 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6581 newStream
->ownerDirEntry
= dirEntry
;
6586 void SmallBlockChainStream_Destroy(
6587 SmallBlockChainStream
* This
)
6589 HeapFree(GetProcessHeap(), 0, This
);
6592 /******************************************************************************
6593 * SmallBlockChainStream_GetHeadOfChain
6595 * Returns the head of this chain of small blocks.
6597 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6598 SmallBlockChainStream
* This
)
6600 DirEntry chainEntry
;
6603 if (This
->headOfStreamPlaceHolder
!= NULL
)
6604 return *(This
->headOfStreamPlaceHolder
);
6606 if (This
->ownerDirEntry
)
6608 hr
= StorageImpl_ReadDirEntry(
6609 This
->parentStorage
,
6610 This
->ownerDirEntry
,
6615 return chainEntry
.startingBlock
;
6620 return BLOCK_END_OF_CHAIN
;
6623 /******************************************************************************
6624 * SmallBlockChainStream_GetNextBlockInChain
6626 * Returns the index of the next small block in this chain.
6629 * - BLOCK_END_OF_CHAIN: end of this chain
6630 * - BLOCK_UNUSED: small block 'blockIndex' is free
6632 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6633 SmallBlockChainStream
* This
,
6635 ULONG
* nextBlockInChain
)
6637 ULARGE_INTEGER offsetOfBlockInDepot
;
6642 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6644 offsetOfBlockInDepot
.u
.HighPart
= 0;
6645 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6648 * Read those bytes in the buffer from the small block file.
6650 res
= BlockChainStream_ReadAt(
6651 This
->parentStorage
->smallBlockDepotChain
,
6652 offsetOfBlockInDepot
,
6657 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6658 res
= STG_E_READFAULT
;
6662 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6669 /******************************************************************************
6670 * SmallBlockChainStream_SetNextBlockInChain
6672 * Writes the index of the next block of the specified block in the small
6674 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6675 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6677 static void SmallBlockChainStream_SetNextBlockInChain(
6678 SmallBlockChainStream
* This
,
6682 ULARGE_INTEGER offsetOfBlockInDepot
;
6686 offsetOfBlockInDepot
.u
.HighPart
= 0;
6687 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6689 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6692 * Read those bytes in the buffer from the small block file.
6694 BlockChainStream_WriteAt(
6695 This
->parentStorage
->smallBlockDepotChain
,
6696 offsetOfBlockInDepot
,
6702 /******************************************************************************
6703 * SmallBlockChainStream_FreeBlock
6705 * Flag small block 'blockIndex' as free in the small block depot.
6707 static void SmallBlockChainStream_FreeBlock(
6708 SmallBlockChainStream
* This
,
6711 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6714 /******************************************************************************
6715 * SmallBlockChainStream_GetNextFreeBlock
6717 * Returns the index of a free small block. The small block depot will be
6718 * enlarged if necessary. The small block chain will also be enlarged if
6721 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6722 SmallBlockChainStream
* This
)
6724 ULARGE_INTEGER offsetOfBlockInDepot
;
6727 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6728 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6730 ULONG smallBlocksPerBigBlock
;
6732 ULONG blocksRequired
;
6733 ULARGE_INTEGER old_size
, size_required
;
6735 offsetOfBlockInDepot
.u
.HighPart
= 0;
6738 * Scan the small block depot for a free block
6740 while (nextBlockIndex
!= BLOCK_UNUSED
)
6742 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6744 res
= BlockChainStream_ReadAt(
6745 This
->parentStorage
->smallBlockDepotChain
,
6746 offsetOfBlockInDepot
,
6752 * If we run out of space for the small block depot, enlarge it
6754 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6756 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6758 if (nextBlockIndex
!= BLOCK_UNUSED
)
6764 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6766 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6767 ULARGE_INTEGER newSize
, offset
;
6770 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6771 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6774 * Initialize all the small blocks to free
6776 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6777 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6778 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6779 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6781 StorageImpl_SaveFileHeader(This
->parentStorage
);
6785 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6787 smallBlocksPerBigBlock
=
6788 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6791 * Verify if we have to allocate big blocks to contain small blocks
6793 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6795 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6797 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6799 if (size_required
.QuadPart
> old_size
.QuadPart
)
6801 BlockChainStream_SetSize(
6802 This
->parentStorage
->smallBlockRootChain
,
6805 StorageImpl_ReadDirEntry(
6806 This
->parentStorage
,
6807 This
->parentStorage
->base
.storageDirEntry
,
6810 rootEntry
.size
= size_required
;
6812 StorageImpl_WriteDirEntry(
6813 This
->parentStorage
,
6814 This
->parentStorage
->base
.storageDirEntry
,
6821 /******************************************************************************
6822 * SmallBlockChainStream_ReadAt
6824 * Reads a specified number of bytes from this chain at the specified offset.
6825 * bytesRead may be NULL.
6826 * Failure will be returned if the specified number of bytes has not been read.
6828 HRESULT
SmallBlockChainStream_ReadAt(
6829 SmallBlockChainStream
* This
,
6830 ULARGE_INTEGER offset
,
6836 ULARGE_INTEGER offsetInBigBlockFile
;
6837 ULONG blockNoInSequence
=
6838 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6840 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6841 ULONG bytesToReadInBuffer
;
6843 ULONG bytesReadFromBigBlockFile
;
6845 ULARGE_INTEGER stream_size
;
6848 * This should never happen on a small block file.
6850 assert(offset
.u
.HighPart
==0);
6854 stream_size
= SmallBlockChainStream_GetSize(This
);
6855 if (stream_size
.QuadPart
> offset
.QuadPart
)
6856 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6861 * Find the first block in the stream that contains part of the buffer.
6863 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6865 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6867 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6870 blockNoInSequence
--;
6874 * Start reading the buffer.
6876 bufferWalker
= buffer
;
6878 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6881 * Calculate how many bytes we can copy from this small block.
6883 bytesToReadInBuffer
=
6884 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6887 * Calculate the offset of the small block in the small block file.
6889 offsetInBigBlockFile
.u
.HighPart
= 0;
6890 offsetInBigBlockFile
.u
.LowPart
=
6891 blockIndex
* This
->parentStorage
->smallBlockSize
;
6893 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6896 * Read those bytes in the buffer from the small block file.
6897 * The small block has already been identified so it shouldn't fail
6898 * unless the file is corrupt.
6900 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6901 offsetInBigBlockFile
,
6902 bytesToReadInBuffer
,
6904 &bytesReadFromBigBlockFile
);
6909 if (!bytesReadFromBigBlockFile
)
6910 return STG_E_DOCFILECORRUPT
;
6913 * Step to the next big block.
6915 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6917 return STG_E_DOCFILECORRUPT
;
6919 bufferWalker
+= bytesReadFromBigBlockFile
;
6920 size
-= bytesReadFromBigBlockFile
;
6921 *bytesRead
+= bytesReadFromBigBlockFile
;
6922 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6928 /******************************************************************************
6929 * SmallBlockChainStream_WriteAt
6931 * Writes the specified number of bytes to this chain at the specified offset.
6932 * Will fail if not all specified number of bytes have been written.
6934 HRESULT
SmallBlockChainStream_WriteAt(
6935 SmallBlockChainStream
* This
,
6936 ULARGE_INTEGER offset
,
6939 ULONG
* bytesWritten
)
6941 ULARGE_INTEGER offsetInBigBlockFile
;
6942 ULONG blockNoInSequence
=
6943 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6945 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6946 ULONG bytesToWriteInBuffer
;
6948 ULONG bytesWrittenToBigBlockFile
;
6949 const BYTE
* bufferWalker
;
6953 * This should never happen on a small block file.
6955 assert(offset
.u
.HighPart
==0);
6958 * Find the first block in the stream that contains part of the buffer.
6960 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6962 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6964 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6965 return STG_E_DOCFILECORRUPT
;
6966 blockNoInSequence
--;
6970 * Start writing the buffer.
6973 bufferWalker
= buffer
;
6974 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6977 * Calculate how many bytes we can copy to this small block.
6979 bytesToWriteInBuffer
=
6980 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6983 * Calculate the offset of the small block in the small block file.
6985 offsetInBigBlockFile
.u
.HighPart
= 0;
6986 offsetInBigBlockFile
.u
.LowPart
=
6987 blockIndex
* This
->parentStorage
->smallBlockSize
;
6989 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6992 * Write those bytes in the buffer to the small block file.
6994 res
= BlockChainStream_WriteAt(
6995 This
->parentStorage
->smallBlockRootChain
,
6996 offsetInBigBlockFile
,
6997 bytesToWriteInBuffer
,
6999 &bytesWrittenToBigBlockFile
);
7004 * Step to the next big block.
7006 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7009 bufferWalker
+= bytesWrittenToBigBlockFile
;
7010 size
-= bytesWrittenToBigBlockFile
;
7011 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7012 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7015 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7018 /******************************************************************************
7019 * SmallBlockChainStream_Shrink
7021 * Shrinks this chain in the small block depot.
7023 static BOOL
SmallBlockChainStream_Shrink(
7024 SmallBlockChainStream
* This
,
7025 ULARGE_INTEGER newSize
)
7027 ULONG blockIndex
, extraBlock
;
7031 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7033 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7036 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7039 * Go to the new end of chain
7041 while (count
< numBlocks
)
7043 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7050 * If the count is 0, we have a special case, the head of the chain was
7055 DirEntry chainEntry
;
7057 StorageImpl_ReadDirEntry(This
->parentStorage
,
7058 This
->ownerDirEntry
,
7061 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7063 StorageImpl_WriteDirEntry(This
->parentStorage
,
7064 This
->ownerDirEntry
,
7068 * We start freeing the chain at the head block.
7070 extraBlock
= blockIndex
;
7074 /* Get the next block before marking the new end */
7075 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7079 /* Mark the new end of chain */
7080 SmallBlockChainStream_SetNextBlockInChain(
7083 BLOCK_END_OF_CHAIN
);
7087 * Mark the extra blocks as free
7089 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7091 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7094 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7095 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7096 extraBlock
= blockIndex
;
7102 /******************************************************************************
7103 * SmallBlockChainStream_Enlarge
7105 * Grows this chain in the small block depot.
7107 static BOOL
SmallBlockChainStream_Enlarge(
7108 SmallBlockChainStream
* This
,
7109 ULARGE_INTEGER newSize
)
7111 ULONG blockIndex
, currentBlock
;
7113 ULONG oldNumBlocks
= 0;
7115 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7118 * Empty chain. Create the head.
7120 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7122 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7123 SmallBlockChainStream_SetNextBlockInChain(
7126 BLOCK_END_OF_CHAIN
);
7128 if (This
->headOfStreamPlaceHolder
!= NULL
)
7130 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7134 DirEntry chainEntry
;
7136 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7139 chainEntry
.startingBlock
= blockIndex
;
7141 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7146 currentBlock
= blockIndex
;
7149 * Figure out how many blocks are needed to contain this stream
7151 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7153 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7157 * Go to the current end of chain
7159 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7162 currentBlock
= blockIndex
;
7163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7168 * Add new blocks to the chain
7170 while (oldNumBlocks
< newNumBlocks
)
7172 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7173 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7175 SmallBlockChainStream_SetNextBlockInChain(
7178 BLOCK_END_OF_CHAIN
);
7180 currentBlock
= blockIndex
;
7187 /******************************************************************************
7188 * SmallBlockChainStream_SetSize
7190 * Sets the size of this stream.
7191 * The file will grow if we grow the chain.
7193 * TODO: Free the actual blocks in the file when we shrink the chain.
7194 * Currently, the blocks are still in the file. So the file size
7195 * doesn't shrink even if we shrink streams.
7197 BOOL
SmallBlockChainStream_SetSize(
7198 SmallBlockChainStream
* This
,
7199 ULARGE_INTEGER newSize
)
7201 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7203 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7206 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7208 SmallBlockChainStream_Shrink(This
, newSize
);
7212 SmallBlockChainStream_Enlarge(This
, newSize
);
7218 /******************************************************************************
7219 * SmallBlockChainStream_GetCount
7221 * Returns the number of small blocks that comprises this chain.
7222 * This is not the size of the stream as the last block may not be full!
7225 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7230 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7232 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7236 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7237 blockIndex
, &blockIndex
)))
7244 /******************************************************************************
7245 * SmallBlockChainStream_GetSize
7247 * Returns the size of this chain.
7249 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7251 DirEntry chainEntry
;
7253 if(This
->headOfStreamPlaceHolder
!= NULL
)
7255 ULARGE_INTEGER result
;
7256 result
.u
.HighPart
= 0;
7258 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7259 This
->parentStorage
->smallBlockSize
;
7264 StorageImpl_ReadDirEntry(
7265 This
->parentStorage
,
7266 This
->ownerDirEntry
,
7269 return chainEntry
.size
;
7272 static HRESULT
create_storagefile(
7276 STGOPTIONS
* pStgOptions
,
7280 StorageBaseImpl
* newStorage
= 0;
7281 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7282 HRESULT hr
= STG_E_INVALIDFLAG
;
7286 DWORD fileAttributes
;
7287 WCHAR tempFileName
[MAX_PATH
];
7290 return STG_E_INVALIDPOINTER
;
7292 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7293 return STG_E_INVALIDPARAMETER
;
7295 /* if no share mode given then DENY_NONE is the default */
7296 if (STGM_SHARE_MODE(grfMode
) == 0)
7297 grfMode
|= STGM_SHARE_DENY_NONE
;
7299 if ( FAILED( validateSTGM(grfMode
) ))
7302 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7303 switch(STGM_ACCESS_MODE(grfMode
))
7306 case STGM_READWRITE
:
7312 /* in direct mode, can only use SHARE_EXCLUSIVE */
7313 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7316 /* but in transacted mode, any share mode is valid */
7319 * Generate a unique name.
7323 WCHAR tempPath
[MAX_PATH
];
7324 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7326 memset(tempPath
, 0, sizeof(tempPath
));
7327 memset(tempFileName
, 0, sizeof(tempFileName
));
7329 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7332 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7333 pwcsName
= tempFileName
;
7336 hr
= STG_E_INSUFFICIENTMEMORY
;
7340 creationMode
= TRUNCATE_EXISTING
;
7344 creationMode
= GetCreationModeFromSTGM(grfMode
);
7348 * Interpret the STGM value grfMode
7350 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7351 accessMode
= GetAccessModeFromSTGM(grfMode
);
7353 if (grfMode
& STGM_DELETEONRELEASE
)
7354 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7356 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7358 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7362 FIXME("Storage share mode not implemented.\n");
7367 hFile
= CreateFileW(pwcsName
,
7375 if (hFile
== INVALID_HANDLE_VALUE
)
7377 if(GetLastError() == ERROR_FILE_EXISTS
)
7378 hr
= STG_E_FILEALREADYEXISTS
;
7385 * Allocate and initialize the new IStorage32object.
7387 hr
= Storage_Construct(
7394 pStgOptions
->ulSectorSize
,
7402 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
7403 IStorage_Release(&newStorage
->IStorage_iface
);
7406 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7411 /******************************************************************************
7412 * StgCreateDocfile [OLE32.@]
7413 * Creates a new compound file storage object
7416 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7417 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7418 * reserved [ ?] unused?, usually 0
7419 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7422 * S_OK if the file was successfully created
7423 * some STG_E_ value if error
7425 * if pwcsName is NULL, create file with new unique name
7426 * the function can returns
7427 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7430 HRESULT WINAPI
StgCreateDocfile(
7434 IStorage
**ppstgOpen
)
7436 STGOPTIONS stgoptions
= {1, 0, 512};
7438 TRACE("(%s, %x, %d, %p)\n",
7439 debugstr_w(pwcsName
), grfMode
,
7440 reserved
, ppstgOpen
);
7443 return STG_E_INVALIDPOINTER
;
7445 return STG_E_INVALIDPARAMETER
;
7447 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7450 /******************************************************************************
7451 * StgCreateStorageEx [OLE32.@]
7453 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7455 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7456 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7458 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7460 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7461 return STG_E_INVALIDPARAMETER
;
7464 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7466 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7467 return STG_E_INVALIDPARAMETER
;
7470 if (stgfmt
== STGFMT_FILE
)
7472 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7473 return STG_E_INVALIDPARAMETER
;
7476 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7478 STGOPTIONS defaultOptions
= {1, 0, 512};
7480 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7481 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7485 ERR("Invalid stgfmt argument\n");
7486 return STG_E_INVALIDPARAMETER
;
7489 /******************************************************************************
7490 * StgCreatePropSetStg [OLE32.@]
7492 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7493 IPropertySetStorage
**propset
)
7495 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
7497 return STG_E_INVALIDPARAMETER
;
7499 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
7502 /******************************************************************************
7503 * StgOpenStorageEx [OLE32.@]
7505 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7507 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7508 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7510 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7512 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7513 return STG_E_INVALIDPARAMETER
;
7519 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7520 return STG_E_INVALIDPARAMETER
;
7522 case STGFMT_STORAGE
:
7525 case STGFMT_DOCFILE
:
7526 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7528 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7529 return STG_E_INVALIDPARAMETER
;
7531 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7535 WARN("STGFMT_ANY assuming storage\n");
7539 return STG_E_INVALIDPARAMETER
;
7542 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7546 /******************************************************************************
7547 * StgOpenStorage [OLE32.@]
7549 HRESULT WINAPI
StgOpenStorage(
7550 const OLECHAR
*pwcsName
,
7551 IStorage
*pstgPriority
,
7555 IStorage
**ppstgOpen
)
7557 StorageBaseImpl
* newStorage
= 0;
7563 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7564 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7565 snbExclude
, reserved
, ppstgOpen
);
7569 hr
= STG_E_INVALIDNAME
;
7575 hr
= STG_E_INVALIDPOINTER
;
7581 hr
= STG_E_INVALIDPARAMETER
;
7585 if (grfMode
& STGM_PRIORITY
)
7587 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7588 return STG_E_INVALIDFLAG
;
7589 if (grfMode
& STGM_DELETEONRELEASE
)
7590 return STG_E_INVALIDFUNCTION
;
7591 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7592 return STG_E_INVALIDFLAG
;
7593 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7594 grfMode
|= STGM_SHARE_DENY_NONE
;
7596 /* STGM_PRIORITY stops other IStorage objects on the same file from
7597 * committing until the STGM_PRIORITY IStorage is closed. it also
7598 * stops non-transacted mode StgOpenStorage calls with write access from
7599 * succeeding. obviously, both of these cannot be achieved through just
7600 * file share flags */
7601 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7605 * Validate the sharing mode
7607 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7608 switch(STGM_SHARE_MODE(grfMode
))
7610 case STGM_SHARE_EXCLUSIVE
:
7611 case STGM_SHARE_DENY_WRITE
:
7614 hr
= STG_E_INVALIDFLAG
;
7618 if ( FAILED( validateSTGM(grfMode
) ) ||
7619 (grfMode
&STGM_CREATE
))
7621 hr
= STG_E_INVALIDFLAG
;
7625 /* shared reading requires transacted mode */
7626 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7627 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7628 !(grfMode
&STGM_TRANSACTED
) )
7630 hr
= STG_E_INVALIDFLAG
;
7635 * Interpret the STGM value grfMode
7637 shareMode
= GetShareModeFromSTGM(grfMode
);
7638 accessMode
= GetAccessModeFromSTGM(grfMode
);
7642 hFile
= CreateFileW( pwcsName
,
7647 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7650 if (hFile
==INVALID_HANDLE_VALUE
)
7652 DWORD last_error
= GetLastError();
7658 case ERROR_FILE_NOT_FOUND
:
7659 hr
= STG_E_FILENOTFOUND
;
7662 case ERROR_PATH_NOT_FOUND
:
7663 hr
= STG_E_PATHNOTFOUND
;
7666 case ERROR_ACCESS_DENIED
:
7667 case ERROR_WRITE_PROTECT
:
7668 hr
= STG_E_ACCESSDENIED
;
7671 case ERROR_SHARING_VIOLATION
:
7672 hr
= STG_E_SHAREVIOLATION
;
7683 * Refuse to open the file if it's too small to be a structured storage file
7684 * FIXME: verify the file when reading instead of here
7686 if (GetFileSize(hFile
, NULL
) < 0x100)
7689 hr
= STG_E_FILEALREADYEXISTS
;
7694 * Allocate and initialize the new IStorage32object.
7696 hr
= Storage_Construct(
7709 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7711 if(hr
== STG_E_INVALIDHEADER
)
7712 hr
= STG_E_FILEALREADYEXISTS
;
7716 *ppstgOpen
= &newStorage
->IStorage_iface
;
7719 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7723 /******************************************************************************
7724 * StgCreateDocfileOnILockBytes [OLE32.@]
7726 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7730 IStorage
** ppstgOpen
)
7732 StorageBaseImpl
* newStorage
= 0;
7735 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7736 return STG_E_INVALIDPOINTER
;
7739 * Allocate and initialize the new IStorage object.
7741 hr
= Storage_Construct(
7756 *ppstgOpen
= &newStorage
->IStorage_iface
;
7761 /******************************************************************************
7762 * StgOpenStorageOnILockBytes [OLE32.@]
7764 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7766 IStorage
*pstgPriority
,
7770 IStorage
**ppstgOpen
)
7772 StorageBaseImpl
* newStorage
= 0;
7775 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7776 return STG_E_INVALIDPOINTER
;
7778 if ( FAILED( validateSTGM(grfMode
) ))
7779 return STG_E_INVALIDFLAG
;
7784 * Allocate and initialize the new IStorage object.
7786 hr
= Storage_Construct(
7801 *ppstgOpen
= &newStorage
->IStorage_iface
;
7806 /******************************************************************************
7807 * StgSetTimes [ole32.@]
7808 * StgSetTimes [OLE32.@]
7812 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7813 FILETIME
const *patime
, FILETIME
const *pmtime
)
7815 IStorage
*stg
= NULL
;
7818 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7820 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7824 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7825 IStorage_Release(stg
);
7831 /******************************************************************************
7832 * StgIsStorageILockBytes [OLE32.@]
7834 * Determines if the ILockBytes contains a storage object.
7836 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7838 BYTE sig
[sizeof(STORAGE_magic
)];
7839 ULARGE_INTEGER offset
;
7842 offset
.u
.HighPart
= 0;
7843 offset
.u
.LowPart
= 0;
7845 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7847 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7853 /******************************************************************************
7854 * WriteClassStg [OLE32.@]
7856 * This method will store the specified CLSID in the specified storage object
7858 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7861 return E_INVALIDARG
;
7864 return STG_E_INVALIDPOINTER
;
7866 return IStorage_SetClass(pStg
, rclsid
);
7869 /***********************************************************************
7870 * ReadClassStg (OLE32.@)
7872 * This method reads the CLSID previously written to a storage object with
7873 * the WriteClassStg.
7876 * pstg [I] IStorage pointer
7877 * pclsid [O] Pointer to where the CLSID is written
7881 * Failure: HRESULT code.
7883 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7888 TRACE("(%p, %p)\n", pstg
, pclsid
);
7890 if(!pstg
|| !pclsid
)
7891 return E_INVALIDARG
;
7894 * read a STATSTG structure (contains the clsid) from the storage
7896 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7899 *pclsid
=pstatstg
.clsid
;
7904 /***********************************************************************
7905 * OleLoadFromStream (OLE32.@)
7907 * This function loads an object from stream
7909 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7913 LPPERSISTSTREAM xstm
;
7915 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7917 res
=ReadClassStm(pStm
,&clsid
);
7920 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7923 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7925 IUnknown_Release((IUnknown
*)*ppvObj
);
7928 res
=IPersistStream_Load(xstm
,pStm
);
7929 IPersistStream_Release(xstm
);
7930 /* FIXME: all refcounts ok at this point? I think they should be:
7933 * xstm : 0 (released)
7938 /***********************************************************************
7939 * OleSaveToStream (OLE32.@)
7941 * This function saves an object with the IPersistStream interface on it
7942 * to the specified stream.
7944 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7950 TRACE("(%p,%p)\n",pPStm
,pStm
);
7952 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7954 if (SUCCEEDED(res
)){
7956 res
=WriteClassStm(pStm
,&clsid
);
7960 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7963 TRACE("Finished Save\n");
7967 /****************************************************************************
7968 * This method validate a STGM parameter that can contain the values below
7970 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7971 * The stgm values contained in 0xffff0000 are bitmasks.
7973 * STGM_DIRECT 0x00000000
7974 * STGM_TRANSACTED 0x00010000
7975 * STGM_SIMPLE 0x08000000
7977 * STGM_READ 0x00000000
7978 * STGM_WRITE 0x00000001
7979 * STGM_READWRITE 0x00000002
7981 * STGM_SHARE_DENY_NONE 0x00000040
7982 * STGM_SHARE_DENY_READ 0x00000030
7983 * STGM_SHARE_DENY_WRITE 0x00000020
7984 * STGM_SHARE_EXCLUSIVE 0x00000010
7986 * STGM_PRIORITY 0x00040000
7987 * STGM_DELETEONRELEASE 0x04000000
7989 * STGM_CREATE 0x00001000
7990 * STGM_CONVERT 0x00020000
7991 * STGM_FAILIFTHERE 0x00000000
7993 * STGM_NOSCRATCH 0x00100000
7994 * STGM_NOSNAPSHOT 0x00200000
7996 static HRESULT
validateSTGM(DWORD stgm
)
7998 DWORD access
= STGM_ACCESS_MODE(stgm
);
7999 DWORD share
= STGM_SHARE_MODE(stgm
);
8000 DWORD create
= STGM_CREATE_MODE(stgm
);
8002 if (stgm
&~STGM_KNOWN_FLAGS
)
8004 ERR("unknown flags %08x\n", stgm
);
8012 case STGM_READWRITE
:
8020 case STGM_SHARE_DENY_NONE
:
8021 case STGM_SHARE_DENY_READ
:
8022 case STGM_SHARE_DENY_WRITE
:
8023 case STGM_SHARE_EXCLUSIVE
:
8032 case STGM_FAILIFTHERE
:
8039 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8041 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8045 * STGM_CREATE | STGM_CONVERT
8046 * if both are false, STGM_FAILIFTHERE is set to TRUE
8048 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8052 * STGM_NOSCRATCH requires STGM_TRANSACTED
8054 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8058 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8059 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8061 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8062 (!(stgm
& STGM_TRANSACTED
) ||
8063 share
== STGM_SHARE_EXCLUSIVE
||
8064 share
== STGM_SHARE_DENY_WRITE
) )
8070 /****************************************************************************
8071 * GetShareModeFromSTGM
8073 * This method will return a share mode flag from a STGM value.
8074 * The STGM value is assumed valid.
8076 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8078 switch (STGM_SHARE_MODE(stgm
))
8080 case STGM_SHARE_DENY_NONE
:
8081 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8082 case STGM_SHARE_DENY_READ
:
8083 return FILE_SHARE_WRITE
;
8084 case STGM_SHARE_DENY_WRITE
:
8085 return FILE_SHARE_READ
;
8086 case STGM_SHARE_EXCLUSIVE
:
8089 ERR("Invalid share mode!\n");
8094 /****************************************************************************
8095 * GetAccessModeFromSTGM
8097 * This method will return an access mode flag from a STGM value.
8098 * The STGM value is assumed valid.
8100 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8102 switch (STGM_ACCESS_MODE(stgm
))
8105 return GENERIC_READ
;
8107 case STGM_READWRITE
:
8108 return GENERIC_READ
| GENERIC_WRITE
;
8110 ERR("Invalid access mode!\n");
8115 /****************************************************************************
8116 * GetCreationModeFromSTGM
8118 * This method will return a creation mode flag from a STGM value.
8119 * The STGM value is assumed valid.
8121 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8123 switch(STGM_CREATE_MODE(stgm
))
8126 return CREATE_ALWAYS
;
8128 FIXME("STGM_CONVERT not implemented!\n");
8130 case STGM_FAILIFTHERE
:
8133 ERR("Invalid create mode!\n");
8139 /*************************************************************************
8140 * OLECONVERT_LoadOLE10 [Internal]
8142 * Loads the OLE10 STREAM to memory
8145 * pOleStream [I] The OLESTREAM
8146 * pData [I] Data Structure for the OLESTREAM Data
8150 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8151 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8154 * This function is used by OleConvertOLESTREAMToIStorage only.
8156 * Memory allocated for pData must be freed by the caller
8158 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8161 HRESULT hRes
= S_OK
;
8165 pData
->pData
= NULL
;
8166 pData
->pstrOleObjFileName
= NULL
;
8168 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8171 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8172 if(dwSize
!= sizeof(pData
->dwOleID
))
8174 hRes
= CONVERT10_E_OLESTREAM_GET
;
8176 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8178 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8189 /* Get the TypeID... more info needed for this field */
8190 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8191 if(dwSize
!= sizeof(pData
->dwTypeID
))
8193 hRes
= CONVERT10_E_OLESTREAM_GET
;
8198 if(pData
->dwTypeID
!= 0)
8200 /* Get the length of the OleTypeName */
8201 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8202 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8204 hRes
= CONVERT10_E_OLESTREAM_GET
;
8209 if(pData
->dwOleTypeNameLength
> 0)
8211 /* Get the OleTypeName */
8212 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8213 if(dwSize
!= pData
->dwOleTypeNameLength
)
8215 hRes
= CONVERT10_E_OLESTREAM_GET
;
8221 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8222 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8224 hRes
= CONVERT10_E_OLESTREAM_GET
;
8228 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8229 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8230 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8231 if(pData
->pstrOleObjFileName
)
8233 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8234 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8236 hRes
= CONVERT10_E_OLESTREAM_GET
;
8240 hRes
= CONVERT10_E_OLESTREAM_GET
;
8245 /* Get the Width of the Metafile */
8246 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8247 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8249 hRes
= CONVERT10_E_OLESTREAM_GET
;
8253 /* Get the Height of the Metafile */
8254 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8255 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8257 hRes
= CONVERT10_E_OLESTREAM_GET
;
8263 /* Get the Length of the Data */
8264 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8265 if(dwSize
!= sizeof(pData
->dwDataLength
))
8267 hRes
= CONVERT10_E_OLESTREAM_GET
;
8271 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8273 if(!bStrem1
) /* if it is a second OLE stream data */
8275 pData
->dwDataLength
-= 8;
8276 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8277 if(dwSize
!= sizeof(pData
->strUnknown
))
8279 hRes
= CONVERT10_E_OLESTREAM_GET
;
8285 if(pData
->dwDataLength
> 0)
8287 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8289 /* Get Data (ex. IStorage, Metafile, or BMP) */
8292 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8293 if(dwSize
!= pData
->dwDataLength
)
8295 hRes
= CONVERT10_E_OLESTREAM_GET
;
8300 hRes
= CONVERT10_E_OLESTREAM_GET
;
8309 /*************************************************************************
8310 * OLECONVERT_SaveOLE10 [Internal]
8312 * Saves the OLE10 STREAM From memory
8315 * pData [I] Data Structure for the OLESTREAM Data
8316 * pOleStream [I] The OLESTREAM to save
8320 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8323 * This function is used by OleConvertIStorageToOLESTREAM only.
8326 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8329 HRESULT hRes
= S_OK
;
8333 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8334 if(dwSize
!= sizeof(pData
->dwOleID
))
8336 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8341 /* Set the TypeID */
8342 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8343 if(dwSize
!= sizeof(pData
->dwTypeID
))
8345 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8349 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8351 /* Set the Length of the OleTypeName */
8352 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8353 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8355 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8360 if(pData
->dwOleTypeNameLength
> 0)
8362 /* Set the OleTypeName */
8363 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8364 if(dwSize
!= pData
->dwOleTypeNameLength
)
8366 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8373 /* Set the width of the Metafile */
8374 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8375 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8377 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8383 /* Set the height of the Metafile */
8384 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8385 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8387 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8393 /* Set the length of the Data */
8394 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8395 if(dwSize
!= sizeof(pData
->dwDataLength
))
8397 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8403 if(pData
->dwDataLength
> 0)
8405 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8406 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8407 if(dwSize
!= pData
->dwDataLength
)
8409 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8417 /*************************************************************************
8418 * OLECONVERT_GetOLE20FromOLE10[Internal]
8420 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8421 * opens it, and copies the content to the dest IStorage for
8422 * OleConvertOLESTREAMToIStorage
8426 * pDestStorage [I] The IStorage to copy the data to
8427 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8428 * nBufferLength [I] The size of the buffer
8437 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8441 IStorage
*pTempStorage
;
8442 DWORD dwNumOfBytesWritten
;
8443 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8444 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8446 /* Create a temp File */
8447 GetTempPathW(MAX_PATH
, wstrTempDir
);
8448 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8449 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8451 if(hFile
!= INVALID_HANDLE_VALUE
)
8453 /* Write IStorage Data to File */
8454 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8457 /* Open and copy temp storage to the Dest Storage */
8458 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8461 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8462 IStorage_Release(pTempStorage
);
8464 DeleteFileW(wstrTempFile
);
8469 /*************************************************************************
8470 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8472 * Saves the OLE10 STREAM From memory
8475 * pStorage [I] The Src IStorage to copy
8476 * pData [I] The Dest Memory to write to.
8479 * The size in bytes allocated for pData
8482 * Memory allocated for pData must be freed by the caller
8484 * Used by OleConvertIStorageToOLESTREAM only.
8487 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8491 DWORD nDataLength
= 0;
8492 IStorage
*pTempStorage
;
8493 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8494 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8498 /* Create temp Storage */
8499 GetTempPathW(MAX_PATH
, wstrTempDir
);
8500 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8501 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8505 /* Copy Src Storage to the Temp Storage */
8506 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8507 IStorage_Release(pTempStorage
);
8509 /* Open Temp Storage as a file and copy to memory */
8510 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8511 if(hFile
!= INVALID_HANDLE_VALUE
)
8513 nDataLength
= GetFileSize(hFile
, NULL
);
8514 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8515 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8518 DeleteFileW(wstrTempFile
);
8523 /*************************************************************************
8524 * STORAGE_CreateOleStream [Internal]
8526 * Creates the "\001OLE" stream in the IStorage if necessary.
8529 * storage [I] Dest storage to create the stream in
8530 * flags [I] flags to be set for newly created stream
8533 * HRESULT return value
8537 * This stream is still unknown, MS Word seems to have extra data
8538 * but since the data is stored in the OLESTREAM there should be
8539 * no need to recreate the stream. If the stream is manually
8540 * deleted it will create it with this default data.
8543 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
8545 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
8546 static const DWORD version_magic
= 0x02000001;
8550 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
8553 struct empty_1ole_stream
{
8554 DWORD version_magic
;
8556 DWORD update_options
;
8558 DWORD mon_stream_size
;
8560 struct empty_1ole_stream stream_data
;
8562 stream_data
.version_magic
= version_magic
;
8563 stream_data
.flags
= flags
;
8564 stream_data
.update_options
= 0;
8565 stream_data
.reserved
= 0;
8566 stream_data
.mon_stream_size
= 0;
8568 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
8569 IStream_Release(stream
);
8575 /* write a string to a stream, preceded by its length */
8576 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8583 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8584 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8589 str
= CoTaskMemAlloc( len
);
8590 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8591 r
= IStream_Write( stm
, str
, len
, NULL
);
8592 CoTaskMemFree( str
);
8596 /* read a string preceded by its length from a stream */
8597 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8600 DWORD len
, count
= 0;
8604 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8607 if( count
!= sizeof(len
) )
8608 return E_OUTOFMEMORY
;
8610 TRACE("%d bytes\n",len
);
8612 str
= CoTaskMemAlloc( len
);
8614 return E_OUTOFMEMORY
;
8616 r
= IStream_Read( stm
, str
, len
, &count
);
8621 CoTaskMemFree( str
);
8622 return E_OUTOFMEMORY
;
8625 TRACE("Read string %s\n",debugstr_an(str
,len
));
8627 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8628 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8630 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8631 CoTaskMemFree( str
);
8639 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8640 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8644 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8646 static const BYTE unknown1
[12] =
8647 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8648 0xFF, 0xFF, 0xFF, 0xFF};
8649 static const BYTE unknown2
[16] =
8650 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8651 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8653 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8654 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8655 debugstr_w(szProgIDName
));
8657 /* Create a CompObj stream */
8658 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8659 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8663 /* Write CompObj Structure to stream */
8664 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8666 if( SUCCEEDED( r
) )
8667 r
= WriteClassStm( pstm
, clsid
);
8669 if( SUCCEEDED( r
) )
8670 r
= STREAM_WriteString( pstm
, lpszUserType
);
8671 if( SUCCEEDED( r
) )
8672 r
= STREAM_WriteString( pstm
, szClipName
);
8673 if( SUCCEEDED( r
) )
8674 r
= STREAM_WriteString( pstm
, szProgIDName
);
8675 if( SUCCEEDED( r
) )
8676 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8678 IStream_Release( pstm
);
8683 /***********************************************************************
8684 * WriteFmtUserTypeStg (OLE32.@)
8686 HRESULT WINAPI
WriteFmtUserTypeStg(
8687 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8690 WCHAR szwClipName
[0x40];
8691 CLSID clsid
= CLSID_NULL
;
8692 LPWSTR wstrProgID
= NULL
;
8695 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8697 /* get the clipboard format name */
8698 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8701 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8703 /* FIXME: There's room to save a CLSID and its ProgID, but
8704 the CLSID is not looked up in the registry and in all the
8705 tests I wrote it was CLSID_NULL. Where does it come from?
8708 /* get the real program ID. This may fail, but that's fine */
8709 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8711 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8713 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8714 lpszUserType
, szwClipName
, wstrProgID
);
8716 CoTaskMemFree(wstrProgID
);
8722 /******************************************************************************
8723 * ReadFmtUserTypeStg [OLE32.@]
8725 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8729 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8730 unsigned char unknown1
[12];
8731 unsigned char unknown2
[16];
8733 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8736 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8738 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8739 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8742 WARN("Failed to open stream r = %08x\n", r
);
8746 /* read the various parts of the structure */
8747 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8748 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8750 r
= ReadClassStm( stm
, &clsid
);
8754 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8758 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8762 r
= STREAM_ReadString( stm
, &szProgIDName
);
8766 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8767 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8770 /* ok, success... now we just need to store what we found */
8772 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8774 if( lplpszUserType
)
8776 *lplpszUserType
= szCLSIDName
;
8781 CoTaskMemFree( szCLSIDName
);
8782 CoTaskMemFree( szOleTypeName
);
8783 CoTaskMemFree( szProgIDName
);
8784 IStream_Release( stm
);
8790 /*************************************************************************
8791 * OLECONVERT_CreateCompObjStream [Internal]
8793 * Creates a "\001CompObj" is the destination IStorage if necessary.
8796 * pStorage [I] The dest IStorage to create the CompObj Stream
8798 * strOleTypeName [I] The ProgID
8802 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8805 * This function is used by OleConvertOLESTREAMToIStorage only.
8807 * The stream data is stored in the OLESTREAM and there should be
8808 * no need to recreate the stream. If the stream is manually
8809 * deleted it will attempt to create it by querying the registry.
8813 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8816 HRESULT hStorageRes
, hRes
= S_OK
;
8817 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8818 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8819 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8821 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8822 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8824 /* Initialize the CompObj structure */
8825 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8826 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8827 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8830 /* Create a CompObj stream if it doesn't exist */
8831 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8832 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8833 if(hStorageRes
== S_OK
)
8835 /* copy the OleTypeName to the compobj struct */
8836 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8837 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8839 /* copy the OleTypeName to the compobj struct */
8840 /* Note: in the test made, these were Identical */
8841 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8842 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8845 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8846 bufferW
, OLESTREAM_MAX_STR_LEN
);
8847 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8853 /* Get the CLSID Default Name from the Registry */
8854 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
8855 if(hErr
== ERROR_SUCCESS
)
8857 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8858 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8859 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8860 if(hErr
== ERROR_SUCCESS
)
8862 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8868 /* Write CompObj Structure to stream */
8869 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8871 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8873 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8874 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8876 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8878 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8879 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8881 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8883 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8884 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8886 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8888 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8889 IStream_Release(pStream
);
8895 /*************************************************************************
8896 * OLECONVERT_CreateOlePresStream[Internal]
8898 * Creates the "\002OlePres000" Stream with the Metafile data
8901 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8902 * dwExtentX [I] Width of the Metafile
8903 * dwExtentY [I] Height of the Metafile
8904 * pData [I] Metafile data
8905 * dwDataLength [I] Size of the Metafile data
8909 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8912 * This function is used by OleConvertOLESTREAMToIStorage only.
8915 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8919 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8920 BYTE pOlePresStreamHeader
[] =
8922 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8923 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8924 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8925 0x00, 0x00, 0x00, 0x00
8928 BYTE pOlePresStreamHeaderEmpty
[] =
8930 0x00, 0x00, 0x00, 0x00,
8931 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8932 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8933 0x00, 0x00, 0x00, 0x00
8936 /* Create the OlePres000 Stream */
8937 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8938 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8943 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8945 memset(&OlePres
, 0, sizeof(OlePres
));
8946 /* Do we have any metafile data to save */
8947 if(dwDataLength
> 0)
8949 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8950 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8954 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8955 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8957 /* Set width and height of the metafile */
8958 OlePres
.dwExtentX
= dwExtentX
;
8959 OlePres
.dwExtentY
= -dwExtentY
;
8961 /* Set Data and Length */
8962 if(dwDataLength
> sizeof(METAFILEPICT16
))
8964 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8965 OlePres
.pData
= &(pData
[8]);
8967 /* Save OlePres000 Data to Stream */
8968 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8969 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8970 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8971 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8972 if(OlePres
.dwSize
> 0)
8974 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8976 IStream_Release(pStream
);
8980 /*************************************************************************
8981 * OLECONVERT_CreateOle10NativeStream [Internal]
8983 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8986 * pStorage [I] Dest storage to create the stream in
8987 * pData [I] Ole10 Native Data (ex. bmp)
8988 * dwDataLength [I] Size of the Ole10 Native Data
8994 * This function is used by OleConvertOLESTREAMToIStorage only.
8996 * Might need to verify the data and return appropriate error message
8999 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9003 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9005 /* Create the Ole10Native Stream */
9006 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9007 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9011 /* Write info to stream */
9012 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9013 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9014 IStream_Release(pStream
);
9019 /*************************************************************************
9020 * OLECONVERT_GetOLE10ProgID [Internal]
9022 * Finds the ProgID (or OleTypeID) from the IStorage
9025 * pStorage [I] The Src IStorage to get the ProgID
9026 * strProgID [I] the ProgID string to get
9027 * dwSize [I] the size of the string
9031 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9034 * This function is used by OleConvertIStorageToOLESTREAM only.
9038 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9042 LARGE_INTEGER iSeekPos
;
9043 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9044 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9046 /* Open the CompObj Stream */
9047 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9048 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9052 /*Get the OleType from the CompObj Stream */
9053 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9054 iSeekPos
.u
.HighPart
= 0;
9056 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9057 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9058 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9059 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9060 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9061 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9062 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9064 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9067 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9069 IStream_Release(pStream
);
9074 LPOLESTR wstrProgID
;
9076 /* Get the OleType from the registry */
9077 REFCLSID clsid
= &(stat
.clsid
);
9078 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9079 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9082 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9083 CoTaskMemFree(wstrProgID
);
9090 /*************************************************************************
9091 * OLECONVERT_GetOle10PresData [Internal]
9093 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9096 * pStorage [I] Src IStroage
9097 * pOleStream [I] Dest OleStream Mem Struct
9103 * This function is used by OleConvertIStorageToOLESTREAM only.
9105 * Memory allocated for pData must be freed by the caller
9109 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9114 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9116 /* Initialize Default data for OLESTREAM */
9117 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9118 pOleStreamData
[0].dwTypeID
= 2;
9119 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9120 pOleStreamData
[1].dwTypeID
= 0;
9121 pOleStreamData
[0].dwMetaFileWidth
= 0;
9122 pOleStreamData
[0].dwMetaFileHeight
= 0;
9123 pOleStreamData
[0].pData
= NULL
;
9124 pOleStreamData
[1].pData
= NULL
;
9126 /* Open Ole10Native Stream */
9127 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9128 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9132 /* Read Size and Data */
9133 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9134 if(pOleStreamData
->dwDataLength
> 0)
9136 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9137 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9139 IStream_Release(pStream
);
9145 /*************************************************************************
9146 * OLECONVERT_GetOle20PresData[Internal]
9148 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9151 * pStorage [I] Src IStroage
9152 * pOleStreamData [I] Dest OleStream Mem Struct
9158 * This function is used by OleConvertIStorageToOLESTREAM only.
9160 * Memory allocated for pData must be freed by the caller
9162 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9166 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9167 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9169 /* Initialize Default data for OLESTREAM */
9170 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9171 pOleStreamData
[0].dwTypeID
= 2;
9172 pOleStreamData
[0].dwMetaFileWidth
= 0;
9173 pOleStreamData
[0].dwMetaFileHeight
= 0;
9174 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9175 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9176 pOleStreamData
[1].dwTypeID
= 0;
9177 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9178 pOleStreamData
[1].strOleTypeName
[0] = 0;
9179 pOleStreamData
[1].dwMetaFileWidth
= 0;
9180 pOleStreamData
[1].dwMetaFileHeight
= 0;
9181 pOleStreamData
[1].pData
= NULL
;
9182 pOleStreamData
[1].dwDataLength
= 0;
9185 /* Open OlePress000 stream */
9186 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9187 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9190 LARGE_INTEGER iSeekPos
;
9191 METAFILEPICT16 MetaFilePict
;
9192 static const char strMetafilePictName
[] = "METAFILEPICT";
9194 /* Set the TypeID for a Metafile */
9195 pOleStreamData
[1].dwTypeID
= 5;
9197 /* Set the OleTypeName to Metafile */
9198 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9199 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9201 iSeekPos
.u
.HighPart
= 0;
9202 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9204 /* Get Presentation Data */
9205 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9206 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9207 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9208 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9210 /*Set width and Height */
9211 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9212 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9213 if(olePress
.dwSize
> 0)
9216 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9218 /* Set MetaFilePict struct */
9219 MetaFilePict
.mm
= 8;
9220 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9221 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9222 MetaFilePict
.hMF
= 0;
9224 /* Get Metafile Data */
9225 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9226 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9227 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9229 IStream_Release(pStream
);
9233 /*************************************************************************
9234 * OleConvertOLESTREAMToIStorage [OLE32.@]
9239 * DVTARGETDEVICE parameter is not handled
9240 * Still unsure of some mem fields for OLE 10 Stream
9241 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9242 * and "\001OLE" streams
9245 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9246 LPOLESTREAM pOleStream
,
9248 const DVTARGETDEVICE
* ptd
)
9252 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9254 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9256 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9260 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9263 if(pstg
== NULL
|| pOleStream
== NULL
)
9265 hRes
= E_INVALIDARG
;
9270 /* Load the OLESTREAM to Memory */
9271 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9276 /* Load the OLESTREAM to Memory (part 2)*/
9277 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9283 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9285 /* Do we have the IStorage Data in the OLESTREAM */
9286 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9288 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9289 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9293 /* It must be an original OLE 1.0 source */
9294 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9299 /* It must be an original OLE 1.0 source */
9300 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9303 /* Create CompObj Stream if necessary */
9304 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9307 /*Create the Ole Stream if necessary */
9308 STORAGE_CreateOleStream(pstg
, 0);
9313 /* Free allocated memory */
9314 for(i
=0; i
< 2; i
++)
9316 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9317 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9318 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9323 /*************************************************************************
9324 * OleConvertIStorageToOLESTREAM [OLE32.@]
9331 * Still unsure of some mem fields for OLE 10 Stream
9332 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9333 * and "\001OLE" streams.
9336 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9338 LPOLESTREAM pOleStream
)
9341 HRESULT hRes
= S_OK
;
9343 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9344 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9346 TRACE("%p %p\n", pstg
, pOleStream
);
9348 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9350 if(pstg
== NULL
|| pOleStream
== NULL
)
9352 hRes
= E_INVALIDARG
;
9356 /* Get the ProgID */
9357 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9358 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9362 /* Was it originally Ole10 */
9363 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9366 IStream_Release(pStream
);
9367 /* Get Presentation Data for Ole10Native */
9368 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9372 /* Get Presentation Data (OLE20) */
9373 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9376 /* Save OLESTREAM */
9377 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9380 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9385 /* Free allocated memory */
9386 for(i
=0; i
< 2; i
++)
9388 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9394 enum stream_1ole_flags
{
9395 OleStream_LinkedObject
= 0x00000001,
9396 OleStream_Convert
= 0x00000004
9399 /***********************************************************************
9400 * GetConvertStg (OLE32.@)
9402 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
9404 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9405 static const DWORD version_magic
= 0x02000001;
9412 if (!stg
) return E_INVALIDARG
;
9414 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9415 if (FAILED(hr
)) return hr
;
9417 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9418 IStream_Release(stream
);
9419 if (FAILED(hr
)) return hr
;
9421 if (header
[0] != version_magic
)
9423 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
9427 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
9430 /***********************************************************************
9431 * SetConvertStg (OLE32.@)
9433 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
9435 DWORD flags
= convert
? OleStream_Convert
: 0;
9438 TRACE("(%p, %d)\n", storage
, convert
);
9440 hr
= STORAGE_CreateOleStream(storage
, flags
);
9441 if (hr
== STG_E_FILEALREADYEXISTS
)
9443 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9447 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
9448 if (FAILED(hr
)) return hr
;
9450 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
9453 IStream_Release(stream
);
9457 /* update flag if differs */
9458 if ((header
[1] ^ flags
) & OleStream_Convert
)
9462 if (header
[1] & OleStream_Convert
)
9463 flags
= header
[1] & ~OleStream_Convert
;
9465 flags
= header
[1] | OleStream_Convert
;
9467 pos
.QuadPart
= sizeof(DWORD
);
9468 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
9471 IStream_Release(stream
);
9475 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
9477 IStream_Release(stream
);
9483 /******************************************************************************
9484 * StgIsStorageFile [OLE32.@]
9485 * Verify if the file contains a storage object
9491 * S_OK if file has magic bytes as a storage object
9492 * S_FALSE if file is not storage
9495 StgIsStorageFile(LPCOLESTR fn
)
9501 TRACE("%s\n", debugstr_w(fn
));
9502 hf
= CreateFileW(fn
, GENERIC_READ
,
9503 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9504 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9506 if (hf
== INVALID_HANDLE_VALUE
)
9507 return STG_E_FILENOTFOUND
;
9509 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9511 WARN(" unable to read file\n");
9518 if (bytes_read
!= 8) {
9519 TRACE(" too short\n");
9523 if (!memcmp(magic
,STORAGE_magic
,8)) {
9528 TRACE(" -> Invalid header.\n");
9532 /***********************************************************************
9533 * WriteClassStm (OLE32.@)
9535 * Writes a CLSID to a stream.
9538 * pStm [I] Stream to write to.
9539 * rclsid [I] CLSID to write.
9543 * Failure: HRESULT code.
9545 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9547 TRACE("(%p,%p)\n",pStm
,rclsid
);
9549 if (!pStm
|| !rclsid
)
9550 return E_INVALIDARG
;
9552 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9555 /***********************************************************************
9556 * ReadClassStm (OLE32.@)
9558 * Reads a CLSID from a stream.
9561 * pStm [I] Stream to read from.
9562 * rclsid [O] CLSID to read.
9566 * Failure: HRESULT code.
9568 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9573 TRACE("(%p,%p)\n",pStm
,pclsid
);
9575 if (!pStm
|| !pclsid
)
9576 return E_INVALIDARG
;
9578 /* clear the output args */
9579 *pclsid
= CLSID_NULL
;
9581 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
9586 if (nbByte
!= sizeof(CLSID
))
9587 return STG_E_READFAULT
;