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