2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry
;
86 StorageBaseImpl
*parentStorage
;
88 typedef struct StorageInternalImpl StorageInternalImpl
;
90 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
91 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
93 /* Method definitions for the Storage32InternalImpl class. */
94 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
95 DWORD openFlags
, DirRef storageDirEntry
);
96 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
97 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
98 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
99 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
100 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
101 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
102 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
103 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
105 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
106 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
107 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
108 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
109 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
111 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
112 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
113 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
115 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
116 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
117 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
118 ULONG blockIndex
, ULONG offset
, DWORD value
);
119 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
120 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
122 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
123 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
125 typedef struct TransactedDirEntry
127 /* If applicable, a reference to the original DirEntry in the transacted
128 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
129 DirRef transactedParentEntry
;
131 /* True if this entry is being used. */
134 /* True if data is up to date. */
137 /* True if this entry has been modified. */
140 /* True if this entry's stream has been modified. */
143 /* True if this entry has been deleted in the transacted storage, but the
144 * delete has not yet been committed. */
147 /* If this entry's stream has been modified, a reference to where the stream
148 * is stored in the snapshot file. */
151 /* This directory entry's data, including any changes that have been made. */
154 /* A reference to the parent of this node. This is only valid while we are
155 * committing changes. */
158 /* A reference to a newly-created entry in the transacted parent. This is
159 * always equal to transactedParentEntry except when committing changes. */
160 DirRef newTransactedParentEntry
;
161 } TransactedDirEntry
;
163 /****************************************************************************
164 * Transacted storage object.
166 typedef struct TransactedSnapshotImpl
168 struct StorageBaseImpl base
;
171 * Modified streams are temporarily saved to the scratch file.
173 StorageBaseImpl
*scratch
;
175 /* The directory structure is kept here, so that we can track how these
176 * entries relate to those in the parent storage. */
177 TransactedDirEntry
*entries
;
179 ULONG firstFreeEntry
;
182 * Changes are committed to the transacted parent.
184 StorageBaseImpl
*transactedParent
;
185 } TransactedSnapshotImpl
;
187 /* Generic function to create a transacted wrapper for a direct storage object. */
188 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
190 /* OLESTREAM memory structure to use for Get and Put Routines */
191 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
196 DWORD dwOleTypeNameLength
;
197 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
198 CHAR
*pstrOleObjFileName
;
199 DWORD dwOleObjFileNameLength
;
200 DWORD dwMetaFileWidth
;
201 DWORD dwMetaFileHeight
;
202 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
205 }OLECONVERT_OLESTREAM_DATA
;
207 /* CompObj Stream structure */
208 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
213 DWORD dwCLSIDNameLength
;
214 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
215 DWORD dwOleTypeNameLength
;
216 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
217 DWORD dwProgIDNameLength
;
218 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
220 }OLECONVERT_ISTORAGE_COMPOBJ
;
223 /* Ole Presentation Stream structure */
224 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
232 }OLECONVERT_ISTORAGE_OLEPRES
;
236 /***********************************************************************
237 * Forward declaration of internal functions used by the method DestroyElement
239 static HRESULT
deleteStorageContents(
240 StorageBaseImpl
*parentStorage
,
241 DirRef indexToDelete
,
242 DirEntry entryDataToDelete
);
244 static HRESULT
deleteStreamContents(
245 StorageBaseImpl
*parentStorage
,
246 DirRef indexToDelete
,
247 DirEntry entryDataToDelete
);
249 static HRESULT
removeFromTree(
250 StorageBaseImpl
*This
,
251 DirRef parentStorageIndex
,
252 DirRef deletedIndex
);
254 /***********************************************************************
255 * Declaration of the functions used to manipulate DirEntry
258 static HRESULT
insertIntoTree(
259 StorageBaseImpl
*This
,
260 DirRef parentStorageIndex
,
261 DirRef newEntryIndex
);
263 static LONG
entryNameCmp(
264 const OLECHAR
*name1
,
265 const OLECHAR
*name2
);
267 static DirRef
findElement(
268 StorageBaseImpl
*storage
,
273 static HRESULT
findTreeParent(
274 StorageBaseImpl
*storage
,
276 const OLECHAR
*childName
,
277 DirEntry
*parentData
,
281 /***********************************************************************
282 * Declaration of miscellaneous functions...
284 static HRESULT
validateSTGM(DWORD stgmValue
);
286 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
287 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
288 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
290 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
293 /****************************************************************************
294 * IEnumSTATSTGImpl definitions.
296 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
297 * This class allows iterating through the content of a storage and to find
298 * specific items inside it.
300 struct IEnumSTATSTGImpl
302 IEnumSTATSTG IEnumSTATSTG_iface
;
304 LONG ref
; /* Reference count */
305 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
306 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
308 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
311 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
313 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
317 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
318 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
320 /************************************************************************
324 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
326 return (index
+1) * This
->bigBlockSize
;
329 /************************************************************************
330 ** Storage32BaseImpl implementation
332 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
333 ULARGE_INTEGER offset
,
338 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
341 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
342 ULARGE_INTEGER offset
,
347 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
350 /************************************************************************
351 * Storage32BaseImpl_QueryInterface (IUnknown)
353 * This method implements the common QueryInterface for all IStorage32
354 * implementations contained in this file.
356 * See Windows documentation for more details on IUnknown methods.
358 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
363 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
365 if ( (This
==0) || (ppvObject
==0) )
370 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
371 IsEqualGUID(&IID_IStorage
, riid
))
375 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
377 *ppvObject
= &This
->pssVtbl
;
381 return E_NOINTERFACE
;
383 IStorage_AddRef(iface
);
388 /************************************************************************
389 * Storage32BaseImpl_AddRef (IUnknown)
391 * This method implements the common AddRef for all IStorage32
392 * implementations contained in this file.
394 * See Windows documentation for more details on IUnknown methods.
396 static ULONG WINAPI
StorageBaseImpl_AddRef(
399 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
400 ULONG ref
= InterlockedIncrement(&This
->ref
);
402 TRACE("(%p) AddRef to %d\n", This
, ref
);
407 /************************************************************************
408 * Storage32BaseImpl_Release (IUnknown)
410 * This method implements the common Release for all IStorage32
411 * implementations contained in this file.
413 * See Windows documentation for more details on IUnknown methods.
415 static ULONG WINAPI
StorageBaseImpl_Release(
418 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
420 ULONG ref
= InterlockedDecrement(&This
->ref
);
422 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
427 * Since we are using a system of base-classes, we want to call the
428 * destructor of the appropriate derived class. To do this, we are
429 * using virtual functions to implement the destructor.
431 StorageBaseImpl_Destroy(This
);
437 /************************************************************************
438 * Storage32BaseImpl_OpenStream (IStorage)
440 * This method will open the specified stream object from the current storage.
442 * See Windows documentation for more details on IStorage methods.
444 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
446 const OLECHAR
* pwcsName
, /* [string][in] */
447 void* reserved1
, /* [unique][in] */
448 DWORD grfMode
, /* [in] */
449 DWORD reserved2
, /* [in] */
450 IStream
** ppstm
) /* [out] */
452 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
453 StgStreamImpl
* newStream
;
454 DirEntry currentEntry
;
455 DirRef streamEntryRef
;
456 HRESULT res
= STG_E_UNKNOWN
;
458 TRACE("(%p, %s, %p, %x, %d, %p)\n",
459 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
461 if ( (pwcsName
==NULL
) || (ppstm
==0) )
469 if ( FAILED( validateSTGM(grfMode
) ) ||
470 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
472 res
= STG_E_INVALIDFLAG
;
479 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
481 res
= STG_E_INVALIDFUNCTION
;
487 res
= STG_E_REVERTED
;
492 * Check that we're compatible with the parent's storage mode, but
493 * only if we are not in transacted mode
495 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
496 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
498 res
= STG_E_INVALIDFLAG
;
504 * Search for the element with the given name
506 streamEntryRef
= findElement(
508 This
->storageDirEntry
,
513 * If it was found, construct the stream object and return a pointer to it.
515 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
516 (currentEntry
.stgType
==STGTY_STREAM
) )
518 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
520 /* A single stream cannot be opened a second time. */
521 res
= STG_E_ACCESSDENIED
;
525 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
529 newStream
->grfMode
= grfMode
;
530 *ppstm
= (IStream
*)newStream
;
532 IStream_AddRef(*ppstm
);
542 res
= STG_E_FILENOTFOUND
;
546 TRACE("<-- IStream %p\n", *ppstm
);
547 TRACE("<-- %08x\n", res
);
551 /************************************************************************
552 * Storage32BaseImpl_OpenStorage (IStorage)
554 * This method will open a new storage object from the current storage.
556 * See Windows documentation for more details on IStorage methods.
558 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
560 const OLECHAR
* pwcsName
, /* [string][unique][in] */
561 IStorage
* pstgPriority
, /* [unique][in] */
562 DWORD grfMode
, /* [in] */
563 SNB snbExclude
, /* [unique][in] */
564 DWORD reserved
, /* [in] */
565 IStorage
** ppstg
) /* [out] */
567 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
568 StorageInternalImpl
* newStorage
;
569 StorageBaseImpl
* newTransactedStorage
;
570 DirEntry currentEntry
;
571 DirRef storageEntryRef
;
572 HRESULT res
= STG_E_UNKNOWN
;
574 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
575 iface
, debugstr_w(pwcsName
), pstgPriority
,
576 grfMode
, snbExclude
, reserved
, ppstg
);
578 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
584 if (This
->openFlags
& STGM_SIMPLE
)
586 res
= STG_E_INVALIDFUNCTION
;
591 if (snbExclude
!= NULL
)
593 res
= STG_E_INVALIDPARAMETER
;
597 if ( FAILED( validateSTGM(grfMode
) ))
599 res
= STG_E_INVALIDFLAG
;
606 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
607 (grfMode
& STGM_DELETEONRELEASE
) ||
608 (grfMode
& STGM_PRIORITY
) )
610 res
= STG_E_INVALIDFUNCTION
;
615 return STG_E_REVERTED
;
618 * Check that we're compatible with the parent's storage mode,
619 * but only if we are not transacted
621 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
622 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
624 res
= STG_E_ACCESSDENIED
;
631 storageEntryRef
= findElement(
633 This
->storageDirEntry
,
637 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
638 (currentEntry
.stgType
==STGTY_STORAGE
) )
640 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
642 /* A single storage cannot be opened a second time. */
643 res
= STG_E_ACCESSDENIED
;
647 newStorage
= StorageInternalImpl_Construct(
654 if (grfMode
& STGM_TRANSACTED
)
656 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
660 HeapFree(GetProcessHeap(), 0, newStorage
);
664 *ppstg
= (IStorage
*)newTransactedStorage
;
668 *ppstg
= (IStorage
*)newStorage
;
671 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
677 res
= STG_E_INSUFFICIENTMEMORY
;
681 res
= STG_E_FILENOTFOUND
;
684 TRACE("<-- %08x\n", res
);
688 /************************************************************************
689 * Storage32BaseImpl_EnumElements (IStorage)
691 * This method will create an enumerator object that can be used to
692 * retrieve information about all the elements in the storage object.
694 * See Windows documentation for more details on IStorage methods.
696 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
698 DWORD reserved1
, /* [in] */
699 void* reserved2
, /* [size_is][unique][in] */
700 DWORD reserved3
, /* [in] */
701 IEnumSTATSTG
** ppenum
) /* [out] */
703 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
704 IEnumSTATSTGImpl
* newEnum
;
706 TRACE("(%p, %d, %p, %d, %p)\n",
707 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
709 if ( (This
==0) || (ppenum
==0))
713 return STG_E_REVERTED
;
715 newEnum
= IEnumSTATSTGImpl_Construct(
717 This
->storageDirEntry
);
721 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
723 IEnumSTATSTG_AddRef(*ppenum
);
728 return E_OUTOFMEMORY
;
731 /************************************************************************
732 * Storage32BaseImpl_Stat (IStorage)
734 * This method will retrieve information about this storage object.
736 * See Windows documentation for more details on IStorage methods.
738 static HRESULT WINAPI
StorageBaseImpl_Stat(
740 STATSTG
* pstatstg
, /* [out] */
741 DWORD grfStatFlag
) /* [in] */
743 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
744 DirEntry currentEntry
;
745 HRESULT res
= STG_E_UNKNOWN
;
747 TRACE("(%p, %p, %x)\n",
748 iface
, pstatstg
, grfStatFlag
);
750 if ( (This
==0) || (pstatstg
==0))
758 res
= STG_E_REVERTED
;
762 res
= StorageBaseImpl_ReadDirEntry(
764 This
->storageDirEntry
,
769 StorageUtl_CopyDirEntryToSTATSTG(
775 pstatstg
->grfMode
= This
->openFlags
;
776 pstatstg
->grfStateBits
= This
->stateBits
;
782 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
);
784 TRACE("<-- %08x\n", res
);
788 /************************************************************************
789 * Storage32BaseImpl_RenameElement (IStorage)
791 * This method will rename the specified element.
793 * See Windows documentation for more details on IStorage methods.
795 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
797 const OLECHAR
* pwcsOldName
, /* [in] */
798 const OLECHAR
* pwcsNewName
) /* [in] */
800 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
801 DirEntry currentEntry
;
802 DirRef currentEntryRef
;
804 TRACE("(%p, %s, %s)\n",
805 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
808 return STG_E_REVERTED
;
810 currentEntryRef
= findElement(This
,
811 This
->storageDirEntry
,
815 if (currentEntryRef
!= DIRENTRY_NULL
)
818 * There is already an element with the new name
820 return STG_E_FILEALREADYEXISTS
;
824 * Search for the old element name
826 currentEntryRef
= findElement(This
,
827 This
->storageDirEntry
,
831 if (currentEntryRef
!= DIRENTRY_NULL
)
833 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
834 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
836 WARN("Element is already open; cannot rename.\n");
837 return STG_E_ACCESSDENIED
;
840 /* Remove the element from its current position in the tree */
841 removeFromTree(This
, This
->storageDirEntry
,
844 /* Change the name of the element */
845 strcpyW(currentEntry
.name
, pwcsNewName
);
847 /* Delete any sibling links */
848 currentEntry
.leftChild
= DIRENTRY_NULL
;
849 currentEntry
.rightChild
= DIRENTRY_NULL
;
851 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
854 /* Insert the element in a new position in the tree */
855 insertIntoTree(This
, This
->storageDirEntry
,
861 * There is no element with the old name
863 return STG_E_FILENOTFOUND
;
866 return StorageBaseImpl_Flush(This
);
869 /************************************************************************
870 * Storage32BaseImpl_CreateStream (IStorage)
872 * This method will create a stream object within this storage
874 * See Windows documentation for more details on IStorage methods.
876 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
878 const OLECHAR
* pwcsName
, /* [string][in] */
879 DWORD grfMode
, /* [in] */
880 DWORD reserved1
, /* [in] */
881 DWORD reserved2
, /* [in] */
882 IStream
** ppstm
) /* [out] */
884 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
885 StgStreamImpl
* newStream
;
886 DirEntry currentEntry
, newStreamEntry
;
887 DirRef currentEntryRef
, newStreamEntryRef
;
890 TRACE("(%p, %s, %x, %d, %d, %p)\n",
891 iface
, debugstr_w(pwcsName
), grfMode
,
892 reserved1
, reserved2
, ppstm
);
895 return STG_E_INVALIDPOINTER
;
898 return STG_E_INVALIDNAME
;
900 if (reserved1
|| reserved2
)
901 return STG_E_INVALIDPARAMETER
;
903 if ( FAILED( validateSTGM(grfMode
) ))
904 return STG_E_INVALIDFLAG
;
906 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
907 return STG_E_INVALIDFLAG
;
910 return STG_E_REVERTED
;
915 if ((grfMode
& STGM_DELETEONRELEASE
) ||
916 (grfMode
& STGM_TRANSACTED
))
917 return STG_E_INVALIDFUNCTION
;
920 * Don't worry about permissions in transacted mode, as we can always write
921 * changes; we just can't always commit them.
923 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
924 /* Can't create a stream on read-only storage */
925 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
926 return STG_E_ACCESSDENIED
;
928 /* Can't create a stream with greater access than the parent. */
929 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
930 return STG_E_ACCESSDENIED
;
933 if(This
->openFlags
& STGM_SIMPLE
)
934 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
938 currentEntryRef
= findElement(This
,
939 This
->storageDirEntry
,
943 if (currentEntryRef
!= DIRENTRY_NULL
)
946 * An element with this name already exists
948 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
950 IStorage_DestroyElement(iface
, pwcsName
);
953 return STG_E_FILEALREADYEXISTS
;
957 * memset the empty entry
959 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
961 newStreamEntry
.sizeOfNameString
=
962 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
964 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
965 return STG_E_INVALIDNAME
;
967 strcpyW(newStreamEntry
.name
, pwcsName
);
969 newStreamEntry
.stgType
= STGTY_STREAM
;
970 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
971 newStreamEntry
.size
.u
.LowPart
= 0;
972 newStreamEntry
.size
.u
.HighPart
= 0;
974 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
975 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
976 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
978 /* call CoFileTime to get the current time
983 /* newStreamEntry.clsid */
986 * Create an entry with the new data
988 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
993 * Insert the new entry in the parent storage's tree.
997 This
->storageDirEntry
,
1001 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1006 * Open the stream to return it.
1008 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1012 *ppstm
= (IStream
*)newStream
;
1014 IStream_AddRef(*ppstm
);
1018 return STG_E_INSUFFICIENTMEMORY
;
1021 return StorageBaseImpl_Flush(This
);
1024 /************************************************************************
1025 * Storage32BaseImpl_SetClass (IStorage)
1027 * This method will write the specified CLSID in the directory entry of this
1030 * See Windows documentation for more details on IStorage methods.
1032 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1034 REFCLSID clsid
) /* [in] */
1036 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1038 DirEntry currentEntry
;
1040 TRACE("(%p, %p)\n", iface
, clsid
);
1043 return STG_E_REVERTED
;
1045 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1046 This
->storageDirEntry
,
1048 if (SUCCEEDED(hRes
))
1050 currentEntry
.clsid
= *clsid
;
1052 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1053 This
->storageDirEntry
,
1057 if (SUCCEEDED(hRes
))
1058 hRes
= StorageBaseImpl_Flush(This
);
1063 /************************************************************************
1064 ** Storage32Impl implementation
1067 /************************************************************************
1068 * Storage32BaseImpl_CreateStorage (IStorage)
1070 * This method will create the storage object within the provided storage.
1072 * See Windows documentation for more details on IStorage methods.
1074 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1076 const OLECHAR
*pwcsName
, /* [string][in] */
1077 DWORD grfMode
, /* [in] */
1078 DWORD reserved1
, /* [in] */
1079 DWORD reserved2
, /* [in] */
1080 IStorage
**ppstg
) /* [out] */
1082 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1084 DirEntry currentEntry
;
1086 DirRef currentEntryRef
;
1090 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1091 iface
, debugstr_w(pwcsName
), grfMode
,
1092 reserved1
, reserved2
, ppstg
);
1095 return STG_E_INVALIDPOINTER
;
1097 if (This
->openFlags
& STGM_SIMPLE
)
1099 return STG_E_INVALIDFUNCTION
;
1103 return STG_E_INVALIDNAME
;
1107 if ( FAILED( validateSTGM(grfMode
) ) ||
1108 (grfMode
& STGM_DELETEONRELEASE
) )
1110 WARN("bad grfMode: 0x%x\n", grfMode
);
1111 return STG_E_INVALIDFLAG
;
1115 return STG_E_REVERTED
;
1118 * Check that we're compatible with the parent's storage mode
1120 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1121 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1123 WARN("access denied\n");
1124 return STG_E_ACCESSDENIED
;
1127 currentEntryRef
= findElement(This
,
1128 This
->storageDirEntry
,
1132 if (currentEntryRef
!= DIRENTRY_NULL
)
1135 * An element with this name already exists
1137 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1138 ((This
->openFlags
& STGM_TRANSACTED
) ||
1139 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1141 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1147 WARN("file already exists\n");
1148 return STG_E_FILEALREADYEXISTS
;
1151 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1152 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1154 WARN("read-only storage\n");
1155 return STG_E_ACCESSDENIED
;
1158 memset(&newEntry
, 0, sizeof(DirEntry
));
1160 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1162 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1164 FIXME("name too long\n");
1165 return STG_E_INVALIDNAME
;
1168 strcpyW(newEntry
.name
, pwcsName
);
1170 newEntry
.stgType
= STGTY_STORAGE
;
1171 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1172 newEntry
.size
.u
.LowPart
= 0;
1173 newEntry
.size
.u
.HighPart
= 0;
1175 newEntry
.leftChild
= DIRENTRY_NULL
;
1176 newEntry
.rightChild
= DIRENTRY_NULL
;
1177 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1179 /* call CoFileTime to get the current time
1184 /* newEntry.clsid */
1187 * Create a new directory entry for the storage
1189 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1194 * Insert the new directory entry into the parent storage's tree
1196 hr
= insertIntoTree(
1198 This
->storageDirEntry
,
1202 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1207 * Open it to get a pointer to return.
1209 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1211 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1217 hr
= StorageBaseImpl_Flush(This
);
1223 /***************************************************************************
1227 * Reserve a directory entry in the file and initialize it.
1229 static HRESULT
StorageImpl_CreateDirEntry(
1230 StorageBaseImpl
*base
,
1231 const DirEntry
*newData
,
1234 StorageImpl
*storage
= (StorageImpl
*)base
;
1235 ULONG currentEntryIndex
= 0;
1236 ULONG newEntryIndex
= DIRENTRY_NULL
;
1238 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1239 WORD sizeOfNameString
;
1243 hr
= StorageImpl_ReadRawDirEntry(storage
,
1249 StorageUtl_ReadWord(
1251 OFFSET_PS_NAMELENGTH
,
1254 if (sizeOfNameString
== 0)
1257 * The entry exists and is available, we found it.
1259 newEntryIndex
= currentEntryIndex
;
1265 * We exhausted the directory entries, we will create more space below
1267 newEntryIndex
= currentEntryIndex
;
1269 currentEntryIndex
++;
1271 } while (newEntryIndex
== DIRENTRY_NULL
);
1274 * grow the directory stream
1278 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1279 ULARGE_INTEGER newSize
;
1281 ULONG lastEntry
= 0;
1282 ULONG blockCount
= 0;
1285 * obtain the new count of blocks in the directory stream
1287 blockCount
= BlockChainStream_GetCount(
1288 storage
->rootBlockChain
)+1;
1291 * initialize the size used by the directory stream
1293 newSize
.u
.HighPart
= 0;
1294 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1297 * add a block to the directory stream
1299 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1302 * memset the empty entry in order to initialize the unused newly
1305 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1310 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1313 entryIndex
= newEntryIndex
+ 1;
1314 entryIndex
< lastEntry
;
1317 StorageImpl_WriteRawDirEntry(
1323 StorageImpl_SaveFileHeader(storage
);
1326 UpdateRawDirEntry(currentData
, newData
);
1328 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1331 *index
= newEntryIndex
;
1336 /***************************************************************************
1340 * Mark a directory entry in the file as free.
1342 static HRESULT
StorageImpl_DestroyDirEntry(
1343 StorageBaseImpl
*base
,
1347 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1348 StorageImpl
*storage
= (StorageImpl
*)base
;
1350 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1352 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1358 /****************************************************************************
1362 * Case insensitive comparison of DirEntry.name by first considering
1365 * Returns <0 when name1 < name2
1366 * >0 when name1 > name2
1367 * 0 when name1 == name2
1369 static LONG
entryNameCmp(
1370 const OLECHAR
*name1
,
1371 const OLECHAR
*name2
)
1373 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1375 while (diff
== 0 && *name1
!= 0)
1378 * We compare the string themselves only when they are of the same length
1380 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1386 /****************************************************************************
1390 * Add a directory entry to a storage
1392 static HRESULT
insertIntoTree(
1393 StorageBaseImpl
*This
,
1394 DirRef parentStorageIndex
,
1395 DirRef newEntryIndex
)
1397 DirEntry currentEntry
;
1401 * Read the inserted entry
1403 StorageBaseImpl_ReadDirEntry(This
,
1408 * Read the storage entry
1410 StorageBaseImpl_ReadDirEntry(This
,
1414 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1417 * The root storage contains some element, therefore, start the research
1418 * for the appropriate location.
1421 DirRef current
, next
, previous
, currentEntryId
;
1424 * Keep a reference to the root of the storage's element tree
1426 currentEntryId
= currentEntry
.dirRootEntry
;
1431 StorageBaseImpl_ReadDirEntry(This
,
1432 currentEntry
.dirRootEntry
,
1435 previous
= currentEntry
.leftChild
;
1436 next
= currentEntry
.rightChild
;
1437 current
= currentEntryId
;
1441 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1445 if (previous
!= DIRENTRY_NULL
)
1447 StorageBaseImpl_ReadDirEntry(This
,
1454 currentEntry
.leftChild
= newEntryIndex
;
1455 StorageBaseImpl_WriteDirEntry(This
,
1463 if (next
!= DIRENTRY_NULL
)
1465 StorageBaseImpl_ReadDirEntry(This
,
1472 currentEntry
.rightChild
= newEntryIndex
;
1473 StorageBaseImpl_WriteDirEntry(This
,
1482 * Trying to insert an item with the same name in the
1483 * subtree structure.
1485 return STG_E_FILEALREADYEXISTS
;
1488 previous
= currentEntry
.leftChild
;
1489 next
= currentEntry
.rightChild
;
1495 * The storage is empty, make the new entry the root of its element tree
1497 currentEntry
.dirRootEntry
= newEntryIndex
;
1498 StorageBaseImpl_WriteDirEntry(This
,
1506 /****************************************************************************
1510 * Find and read the element of a storage with the given name.
1512 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1513 const OLECHAR
*name
, DirEntry
*data
)
1515 DirRef currentEntry
;
1517 /* Read the storage entry to find the root of the tree. */
1518 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1520 currentEntry
= data
->dirRootEntry
;
1522 while (currentEntry
!= DIRENTRY_NULL
)
1526 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1528 cmp
= entryNameCmp(name
, data
->name
);
1535 currentEntry
= data
->leftChild
;
1538 currentEntry
= data
->rightChild
;
1541 return currentEntry
;
1544 /****************************************************************************
1548 * Find and read the binary tree parent of the element with the given name.
1550 * If there is no such element, find a place where it could be inserted and
1551 * return STG_E_FILENOTFOUND.
1553 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1554 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1560 /* Read the storage entry to find the root of the tree. */
1561 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1563 *parentEntry
= storageEntry
;
1564 *relation
= DIRENTRY_RELATION_DIR
;
1566 childEntry
= parentData
->dirRootEntry
;
1568 while (childEntry
!= DIRENTRY_NULL
)
1572 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1574 cmp
= entryNameCmp(childName
, childData
.name
);
1582 *parentData
= childData
;
1583 *parentEntry
= childEntry
;
1584 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1586 childEntry
= parentData
->leftChild
;
1591 *parentData
= childData
;
1592 *parentEntry
= childEntry
;
1593 *relation
= DIRENTRY_RELATION_NEXT
;
1595 childEntry
= parentData
->rightChild
;
1599 if (childEntry
== DIRENTRY_NULL
)
1600 return STG_E_FILENOTFOUND
;
1606 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1607 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1608 SNB snbExclude
, IStorage
*pstgDest
);
1610 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1611 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1612 SNB snbExclude
, IStorage
*pstgDest
)
1618 IStream
*pstrChild
, *pstrTmp
;
1621 if (srcEntry
== DIRENTRY_NULL
)
1624 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1631 WCHAR
**snb
= snbExclude
;
1633 while ( *snb
!= NULL
&& !skip
)
1635 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1643 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1646 * create a new storage in destination storage
1648 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1649 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1654 * if it already exist, don't create a new one use this one
1656 if (hr
== STG_E_FILEALREADYEXISTS
)
1658 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1659 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1660 NULL
, 0, &pstgTmp
);
1665 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1666 skip_stream
, NULL
, pstgTmp
);
1668 IStorage_Release(pstgTmp
);
1671 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1674 * create a new stream in destination storage. If the stream already
1675 * exist, it will be deleted and a new one will be created.
1677 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1678 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1682 * open child stream storage. This operation must succeed even if the
1683 * stream is already open, so we use internal functions to do it.
1687 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1689 IStream_AddRef(pstrChild
);
1697 * Get the size of the source stream
1699 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1702 * Set the size of the destination stream.
1704 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1709 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1712 IStream_Release( pstrChild
);
1715 IStream_Release( pstrTmp
);
1721 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1722 skip_stream
, snbExclude
, pstgDest
);
1725 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1726 skip_stream
, snbExclude
, pstgDest
);
1731 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1732 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1733 SNB snbExclude
, IStorage
*pstgDest
)
1738 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1741 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1744 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1745 skip_stream
, snbExclude
, pstgDest
);
1750 /*************************************************************************
1753 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1755 DWORD ciidExclude
, /* [in] */
1756 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1757 SNB snbExclude
, /* [unique][in] */
1758 IStorage
* pstgDest
) /* [unique][in] */
1760 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1762 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1765 TRACE("(%p, %d, %p, %p, %p)\n",
1766 iface
, ciidExclude
, rgiidExclude
,
1767 snbExclude
, pstgDest
);
1769 if ( pstgDest
== 0 )
1770 return STG_E_INVALIDPOINTER
;
1772 for(i
= 0; i
< ciidExclude
; ++i
)
1774 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1775 skip_storage
= TRUE
;
1776 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1779 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1784 /* Give up early if it looks like this would be infinitely recursive.
1785 * Oddly enough, this includes some cases that aren't really recursive, like
1786 * copying to a transacted child. */
1787 IStorage
*pstgDestAncestor
= pstgDest
;
1788 IStorage
*pstgDestAncestorChild
= NULL
;
1790 /* Go up the chain from the destination until we find the source storage. */
1791 while (pstgDestAncestor
!= iface
) {
1792 pstgDestAncestorChild
= pstgDest
;
1794 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1796 TransactedSnapshotImpl
*impl
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1798 pstgDestAncestor
= (IStorage
*)impl
->transactedParent
;
1800 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1802 StorageInternalImpl
*impl
= (StorageInternalImpl
*) pstgDestAncestor
;
1804 pstgDestAncestor
= (IStorage
*)impl
->parentStorage
;
1810 if (pstgDestAncestor
== iface
)
1814 if (pstgDestAncestorChild
&& snbExclude
)
1816 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1818 WCHAR
**snb
= snbExclude
;
1820 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1822 while ( *snb
!= NULL
&& fail
)
1824 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1831 return STG_E_ACCESSDENIED
;
1835 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1836 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1839 /*************************************************************************
1840 * MoveElementTo (IStorage)
1842 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1844 const OLECHAR
*pwcsName
, /* [string][in] */
1845 IStorage
*pstgDest
, /* [unique][in] */
1846 const OLECHAR
*pwcsNewName
,/* [string][in] */
1847 DWORD grfFlags
) /* [in] */
1849 FIXME("(%p %s %p %s %u): stub\n", iface
,
1850 debugstr_w(pwcsName
), pstgDest
,
1851 debugstr_w(pwcsNewName
), grfFlags
);
1855 /*************************************************************************
1858 * Ensures that any changes made to a storage object open in transacted mode
1859 * are reflected in the parent storage
1861 * In a non-transacted mode, this ensures all cached writes are completed.
1863 static HRESULT WINAPI
StorageImpl_Commit(
1865 DWORD grfCommitFlags
)/* [in] */
1867 StorageBaseImpl
* const base
=(StorageBaseImpl
*)iface
;
1868 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1869 return StorageBaseImpl_Flush(base
);
1872 /*************************************************************************
1875 * Discard all changes that have been made since the last commit operation
1877 static HRESULT WINAPI
StorageImpl_Revert(
1880 TRACE("(%p)\n", iface
);
1884 /*************************************************************************
1885 * DestroyElement (IStorage)
1887 * Strategy: This implementation is built this way for simplicity not for speed.
1888 * I always delete the topmost element of the enumeration and adjust
1889 * the deleted element pointer all the time. This takes longer to
1890 * do but allow to reinvoke DestroyElement whenever we encounter a
1891 * storage object. The optimisation resides in the usage of another
1892 * enumeration strategy that would give all the leaves of a storage
1893 * first. (postfix order)
1895 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1897 const OLECHAR
*pwcsName
)/* [string][in] */
1899 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1902 DirEntry entryToDelete
;
1903 DirRef entryToDeleteRef
;
1906 iface
, debugstr_w(pwcsName
));
1909 return STG_E_INVALIDPOINTER
;
1912 return STG_E_REVERTED
;
1914 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1915 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1916 return STG_E_ACCESSDENIED
;
1918 entryToDeleteRef
= findElement(
1920 This
->storageDirEntry
,
1924 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1926 return STG_E_FILENOTFOUND
;
1929 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1931 hr
= deleteStorageContents(
1936 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1938 hr
= deleteStreamContents(
1948 * Remove the entry from its parent storage
1950 hr
= removeFromTree(
1952 This
->storageDirEntry
,
1956 * Invalidate the entry
1959 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1962 hr
= StorageBaseImpl_Flush(This
);
1968 /******************************************************************************
1969 * Internal stream list handlers
1972 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1974 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1975 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1978 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1980 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1981 list_remove(&(strm
->StrmListEntry
));
1984 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1986 StgStreamImpl
*strm
;
1988 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1990 if (strm
->dirEntry
== streamEntry
)
1999 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2001 StorageInternalImpl
*childstg
;
2003 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2005 if (childstg
->base
.storageDirEntry
== storageEntry
)
2014 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2016 struct list
*cur
, *cur2
;
2017 StgStreamImpl
*strm
=NULL
;
2018 StorageInternalImpl
*childstg
=NULL
;
2020 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2021 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2022 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2023 strm
->parentStorage
= NULL
;
2027 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2028 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2029 StorageBaseImpl_Invalidate( &childstg
->base
);
2032 if (stg
->transactedChild
)
2034 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2036 stg
->transactedChild
= NULL
;
2041 /*********************************************************************
2045 * Delete the contents of a storage entry.
2048 static HRESULT
deleteStorageContents(
2049 StorageBaseImpl
*parentStorage
,
2050 DirRef indexToDelete
,
2051 DirEntry entryDataToDelete
)
2053 IEnumSTATSTG
*elements
= 0;
2054 IStorage
*childStorage
= 0;
2055 STATSTG currentElement
;
2057 HRESULT destroyHr
= S_OK
;
2058 StorageInternalImpl
*stg
, *stg2
;
2060 /* Invalidate any open storage objects. */
2061 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2063 if (stg
->base
.storageDirEntry
== indexToDelete
)
2065 StorageBaseImpl_Invalidate(&stg
->base
);
2070 * Open the storage and enumerate it
2072 hr
= StorageBaseImpl_OpenStorage(
2073 (IStorage
*)parentStorage
,
2074 entryDataToDelete
.name
,
2076 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2087 * Enumerate the elements
2089 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2094 * Obtain the next element
2096 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2099 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2101 CoTaskMemFree(currentElement
.pwcsName
);
2105 * We need to Reset the enumeration every time because we delete elements
2106 * and the enumeration could be invalid
2108 IEnumSTATSTG_Reset(elements
);
2110 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2112 IStorage_Release(childStorage
);
2113 IEnumSTATSTG_Release(elements
);
2118 /*********************************************************************
2122 * Perform the deletion of a stream's data
2125 static HRESULT
deleteStreamContents(
2126 StorageBaseImpl
*parentStorage
,
2127 DirRef indexToDelete
,
2128 DirEntry entryDataToDelete
)
2132 ULARGE_INTEGER size
;
2133 StgStreamImpl
*strm
, *strm2
;
2135 /* Invalidate any open stream objects. */
2136 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2138 if (strm
->dirEntry
== indexToDelete
)
2140 TRACE("Stream deleted %p\n", strm
);
2141 strm
->parentStorage
= NULL
;
2142 list_remove(&strm
->StrmListEntry
);
2146 size
.u
.HighPart
= 0;
2149 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2150 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2160 hr
= IStream_SetSize(pis
, size
);
2168 * Release the stream object.
2170 IStream_Release(pis
);
2175 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2179 case DIRENTRY_RELATION_PREVIOUS
:
2180 entry
->leftChild
= new_target
;
2182 case DIRENTRY_RELATION_NEXT
:
2183 entry
->rightChild
= new_target
;
2185 case DIRENTRY_RELATION_DIR
:
2186 entry
->dirRootEntry
= new_target
;
2193 /*************************************************************************
2197 * This method removes a directory entry from its parent storage tree without
2198 * freeing any resources attached to it.
2200 static HRESULT
removeFromTree(
2201 StorageBaseImpl
*This
,
2202 DirRef parentStorageIndex
,
2203 DirRef deletedIndex
)
2206 DirEntry entryToDelete
;
2207 DirEntry parentEntry
;
2208 DirRef parentEntryRef
;
2209 ULONG typeOfRelation
;
2211 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2217 * Find the element that links to the one we want to delete.
2219 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2220 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2225 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2228 * Replace the deleted entry with its left child
2230 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2232 hr
= StorageBaseImpl_WriteDirEntry(
2241 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2244 * We need to reinsert the right child somewhere. We already know it and
2245 * its children are greater than everything in the left tree, so we
2246 * insert it at the rightmost point in the left tree.
2248 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2249 DirEntry newRightChildParentEntry
;
2253 hr
= StorageBaseImpl_ReadDirEntry(
2255 newRightChildParent
,
2256 &newRightChildParentEntry
);
2262 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2263 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2264 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2266 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2268 hr
= StorageBaseImpl_WriteDirEntry(
2270 newRightChildParent
,
2271 &newRightChildParentEntry
);
2281 * Replace the deleted entry with its right child
2283 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2285 hr
= StorageBaseImpl_WriteDirEntry(
2299 /******************************************************************************
2300 * SetElementTimes (IStorage)
2302 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2304 const OLECHAR
*pwcsName
,/* [string][in] */
2305 const FILETIME
*pctime
, /* [in] */
2306 const FILETIME
*patime
, /* [in] */
2307 const FILETIME
*pmtime
) /* [in] */
2309 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2313 /******************************************************************************
2314 * SetStateBits (IStorage)
2316 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2318 DWORD grfStateBits
,/* [in] */
2319 DWORD grfMask
) /* [in] */
2321 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2324 return STG_E_REVERTED
;
2326 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2330 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2331 DirRef index
, const DirEntry
*data
)
2333 StorageImpl
*This
= (StorageImpl
*)base
;
2334 return StorageImpl_WriteDirEntry(This
, index
, data
);
2337 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2338 DirRef index
, DirEntry
*data
)
2340 StorageImpl
*This
= (StorageImpl
*)base
;
2341 return StorageImpl_ReadDirEntry(This
, index
, data
);
2344 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2348 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2350 if (!This
->blockChainCache
[i
])
2352 return &This
->blockChainCache
[i
];
2356 i
= This
->blockChainToEvict
;
2358 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2359 This
->blockChainCache
[i
] = NULL
;
2361 This
->blockChainToEvict
++;
2362 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2363 This
->blockChainToEvict
= 0;
2365 return &This
->blockChainCache
[i
];
2368 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2371 int i
, free_index
=-1;
2373 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2375 if (!This
->blockChainCache
[i
])
2377 if (free_index
== -1) free_index
= i
;
2379 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2381 return &This
->blockChainCache
[i
];
2385 if (free_index
== -1)
2387 free_index
= This
->blockChainToEvict
;
2389 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2390 This
->blockChainCache
[free_index
] = NULL
;
2392 This
->blockChainToEvict
++;
2393 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2394 This
->blockChainToEvict
= 0;
2397 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2398 return &This
->blockChainCache
[free_index
];
2401 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2405 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2407 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2409 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2410 This
->blockChainCache
[i
] = NULL
;
2416 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2417 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2419 StorageImpl
*This
= (StorageImpl
*)base
;
2424 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2425 if (FAILED(hr
)) return hr
;
2427 if (data
.size
.QuadPart
== 0)
2433 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2435 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2442 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2444 SmallBlockChainStream
*stream
;
2446 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2447 if (!stream
) return E_OUTOFMEMORY
;
2449 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2451 SmallBlockChainStream_Destroy(stream
);
2457 BlockChainStream
*stream
= NULL
;
2459 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2460 if (!stream
) return E_OUTOFMEMORY
;
2462 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2468 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2469 ULARGE_INTEGER newsize
)
2471 StorageImpl
*This
= (StorageImpl
*)base
;
2474 SmallBlockChainStream
*smallblock
=NULL
;
2475 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2477 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2478 if (FAILED(hr
)) return hr
;
2480 /* In simple mode keep the stream size above the small block limit */
2481 if (This
->base
.openFlags
& STGM_SIMPLE
)
2482 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2484 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2487 /* Create a block chain object of the appropriate type */
2488 if (data
.size
.QuadPart
== 0)
2490 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2492 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2493 if (!smallblock
) return E_OUTOFMEMORY
;
2497 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2498 bigblock
= *pbigblock
;
2499 if (!bigblock
) return E_OUTOFMEMORY
;
2502 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2504 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2505 if (!smallblock
) return E_OUTOFMEMORY
;
2509 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2510 bigblock
= *pbigblock
;
2511 if (!bigblock
) return E_OUTOFMEMORY
;
2514 /* Change the block chain type if necessary. */
2515 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2517 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2520 SmallBlockChainStream_Destroy(smallblock
);
2524 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2525 *pbigblock
= bigblock
;
2527 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2529 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2534 /* Set the size of the block chain. */
2537 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2538 SmallBlockChainStream_Destroy(smallblock
);
2542 BlockChainStream_SetSize(bigblock
, newsize
);
2545 /* Set the size in the directory entry. */
2546 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2549 data
.size
= newsize
;
2551 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2556 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2557 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2559 StorageImpl
*This
= (StorageImpl
*)base
;
2562 ULARGE_INTEGER newSize
;
2564 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2565 if (FAILED(hr
)) return hr
;
2567 /* Grow the stream if necessary */
2568 newSize
.QuadPart
= 0;
2569 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2571 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2573 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2577 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2578 if (FAILED(hr
)) return hr
;
2581 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2583 SmallBlockChainStream
*stream
;
2585 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2586 if (!stream
) return E_OUTOFMEMORY
;
2588 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2590 SmallBlockChainStream_Destroy(stream
);
2596 BlockChainStream
*stream
;
2598 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2599 if (!stream
) return E_OUTOFMEMORY
;
2601 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2607 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2610 StorageImpl
*This
= (StorageImpl
*)base
;
2611 DirEntry dst_data
, src_data
;
2614 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2617 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2621 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2622 dst_data
.startingBlock
= src_data
.startingBlock
;
2623 dst_data
.size
= src_data
.size
;
2625 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2631 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2633 StorageImpl
*This
= (StorageImpl
*) iface
;
2637 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2639 *result
= statstg
.pwcsName
;
2645 * Virtual function table for the IStorage32Impl class.
2647 static const IStorageVtbl Storage32Impl_Vtbl
=
2649 StorageBaseImpl_QueryInterface
,
2650 StorageBaseImpl_AddRef
,
2651 StorageBaseImpl_Release
,
2652 StorageBaseImpl_CreateStream
,
2653 StorageBaseImpl_OpenStream
,
2654 StorageBaseImpl_CreateStorage
,
2655 StorageBaseImpl_OpenStorage
,
2656 StorageBaseImpl_CopyTo
,
2657 StorageBaseImpl_MoveElementTo
,
2660 StorageBaseImpl_EnumElements
,
2661 StorageBaseImpl_DestroyElement
,
2662 StorageBaseImpl_RenameElement
,
2663 StorageBaseImpl_SetElementTimes
,
2664 StorageBaseImpl_SetClass
,
2665 StorageBaseImpl_SetStateBits
,
2666 StorageBaseImpl_Stat
2669 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2671 StorageImpl_Destroy
,
2672 StorageImpl_Invalidate
,
2674 StorageImpl_GetFilename
,
2675 StorageImpl_CreateDirEntry
,
2676 StorageImpl_BaseWriteDirEntry
,
2677 StorageImpl_BaseReadDirEntry
,
2678 StorageImpl_DestroyDirEntry
,
2679 StorageImpl_StreamReadAt
,
2680 StorageImpl_StreamWriteAt
,
2681 StorageImpl_StreamSetSize
,
2682 StorageImpl_StreamLink
2685 static HRESULT
StorageImpl_Construct(
2693 StorageImpl
** result
)
2697 DirEntry currentEntry
;
2698 DirRef currentEntryRef
;
2700 if ( FAILED( validateSTGM(openFlags
) ))
2701 return STG_E_INVALIDFLAG
;
2703 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2705 return E_OUTOFMEMORY
;
2707 memset(This
, 0, sizeof(StorageImpl
));
2709 list_init(&This
->base
.strmHead
);
2711 list_init(&This
->base
.storageHead
);
2713 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2714 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2715 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2716 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2718 This
->base
.create
= create
;
2720 This
->base
.reverted
= 0;
2723 * Initialize the big block cache.
2725 This
->bigBlockSize
= sector_size
;
2726 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2728 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2731 This
->lockBytes
= pLkbyt
;
2732 ILockBytes_AddRef(pLkbyt
);
2740 ULARGE_INTEGER size
;
2741 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2743 /* Discard any existing data. */
2745 ILockBytes_SetSize(This
->lockBytes
, size
);
2748 * Initialize all header variables:
2749 * - The big block depot consists of one block and it is at block 0
2750 * - The directory table starts at block 1
2751 * - There is no small block depot
2753 memset( This
->bigBlockDepotStart
,
2755 sizeof(This
->bigBlockDepotStart
));
2757 This
->bigBlockDepotCount
= 1;
2758 This
->bigBlockDepotStart
[0] = 0;
2759 This
->rootStartBlock
= 1;
2760 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2761 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2762 if (sector_size
== 4096)
2763 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2765 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2766 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2767 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2768 This
->extBigBlockDepotCount
= 0;
2770 StorageImpl_SaveFileHeader(This
);
2773 * Add one block for the big block depot and one block for the directory table
2775 size
.u
.HighPart
= 0;
2776 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2777 ILockBytes_SetSize(This
->lockBytes
, size
);
2780 * Initialize the big block depot
2782 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2783 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2784 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2785 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2790 * Load the header for the file.
2792 hr
= StorageImpl_LoadFileHeader(This
);
2801 * There is no block depot cached yet.
2803 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2804 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
2807 * Start searching for free blocks with block 0.
2809 This
->prevFreeBlock
= 0;
2811 This
->firstFreeSmallBlock
= 0;
2813 /* Read the extended big block depot locations. */
2814 if (This
->extBigBlockDepotCount
!= 0)
2816 ULONG current_block
= This
->extBigBlockDepotStart
;
2817 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2820 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2821 if (!This
->extBigBlockDepotLocations
)
2827 This
->extBigBlockDepotLocationsSize
= cache_size
;
2829 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2831 if (current_block
== BLOCK_END_OF_CHAIN
)
2833 WARN("File has too few extended big block depot blocks.\n");
2834 hr
= STG_E_DOCFILECORRUPT
;
2837 This
->extBigBlockDepotLocations
[i
] = current_block
;
2838 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2843 This
->extBigBlockDepotLocations
= NULL
;
2844 This
->extBigBlockDepotLocationsSize
= 0;
2848 * Create the block chain abstractions.
2850 if(!(This
->rootBlockChain
=
2851 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2853 hr
= STG_E_READFAULT
;
2857 if(!(This
->smallBlockDepotChain
=
2858 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2861 hr
= STG_E_READFAULT
;
2866 * Write the root storage entry (memory only)
2872 * Initialize the directory table
2874 memset(&rootEntry
, 0, sizeof(rootEntry
));
2875 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2876 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2877 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2878 rootEntry
.stgType
= STGTY_ROOT
;
2879 rootEntry
.leftChild
= DIRENTRY_NULL
;
2880 rootEntry
.rightChild
= DIRENTRY_NULL
;
2881 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2882 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2883 rootEntry
.size
.u
.HighPart
= 0;
2884 rootEntry
.size
.u
.LowPart
= 0;
2886 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2890 * Find the ID of the root storage.
2892 currentEntryRef
= 0;
2896 hr
= StorageImpl_ReadDirEntry(
2903 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2904 (currentEntry
.stgType
== STGTY_ROOT
) )
2906 This
->base
.storageDirEntry
= currentEntryRef
;
2912 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2916 hr
= STG_E_READFAULT
;
2921 * Create the block chain abstraction for the small block root chain.
2923 if(!(This
->smallBlockRootChain
=
2924 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2926 hr
= STG_E_READFAULT
;
2932 IStorage_Release((IStorage
*)This
);
2937 StorageImpl_Flush((StorageBaseImpl
*)This
);
2944 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2946 StorageImpl
*This
= (StorageImpl
*) iface
;
2948 StorageBaseImpl_DeleteAll(&This
->base
);
2950 This
->base
.reverted
= 1;
2953 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2955 StorageImpl
*This
= (StorageImpl
*) iface
;
2957 TRACE("(%p)\n", This
);
2959 StorageImpl_Flush(iface
);
2961 StorageImpl_Invalidate(iface
);
2963 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
2965 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2966 BlockChainStream_Destroy(This
->rootBlockChain
);
2967 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2969 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2970 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2972 if (This
->lockBytes
)
2973 ILockBytes_Release(This
->lockBytes
);
2974 HeapFree(GetProcessHeap(), 0, This
);
2977 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
)
2979 StorageImpl
*This
= (StorageImpl
*) iface
;
2982 TRACE("(%p)\n", This
);
2984 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
2987 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
2990 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
2992 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2993 if (This
->blockChainCache
[i
])
2994 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
2997 hr
= ILockBytes_Flush(This
->lockBytes
);
3002 /******************************************************************************
3003 * Storage32Impl_GetNextFreeBigBlock
3005 * Returns the index of the next free big block.
3006 * If the big block depot is filled, this method will enlarge it.
3009 static ULONG
StorageImpl_GetNextFreeBigBlock(
3012 ULONG depotBlockIndexPos
;
3013 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3015 ULONG depotBlockOffset
;
3016 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3017 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3019 ULONG freeBlock
= BLOCK_UNUSED
;
3020 ULARGE_INTEGER neededSize
;
3023 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3024 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3027 * Scan the entire big block depot until we find a block marked free
3029 while (nextBlockIndex
!= BLOCK_UNUSED
)
3031 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3033 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3036 * Grow the primary depot.
3038 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3040 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3043 * Add a block depot.
3045 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3046 This
->bigBlockDepotCount
++;
3047 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3050 * Flag it as a block depot.
3052 StorageImpl_SetNextBlockInChain(This
,
3056 /* Save new header information.
3058 StorageImpl_SaveFileHeader(This
);
3063 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3065 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3068 * Grow the extended depot.
3070 ULONG extIndex
= BLOCK_UNUSED
;
3071 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3072 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3074 if (extBlockOffset
== 0)
3076 /* We need an extended block.
3078 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3079 This
->extBigBlockDepotCount
++;
3080 depotBlockIndexPos
= extIndex
+ 1;
3083 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3086 * Add a block depot and mark it in the extended block.
3088 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3089 This
->bigBlockDepotCount
++;
3090 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3092 /* Flag the block depot.
3094 StorageImpl_SetNextBlockInChain(This
,
3098 /* If necessary, flag the extended depot block.
3100 if (extIndex
!= BLOCK_UNUSED
)
3101 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3103 /* Save header information.
3105 StorageImpl_SaveFileHeader(This
);
3109 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3113 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3114 ( nextBlockIndex
!= BLOCK_UNUSED
))
3116 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3118 if (nextBlockIndex
== BLOCK_UNUSED
)
3120 freeBlock
= (depotIndex
* blocksPerDepot
) +
3121 (depotBlockOffset
/sizeof(ULONG
));
3124 depotBlockOffset
+= sizeof(ULONG
);
3129 depotBlockOffset
= 0;
3133 * make sure that the block physically exists before using it
3135 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3137 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3139 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3140 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3142 This
->prevFreeBlock
= freeBlock
;
3147 /******************************************************************************
3148 * Storage32Impl_AddBlockDepot
3150 * This will create a depot block, essentially it is a block initialized
3153 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3155 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3158 * Initialize blocks as free
3160 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3161 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3164 /******************************************************************************
3165 * Storage32Impl_GetExtDepotBlock
3167 * Returns the index of the block that corresponds to the specified depot
3168 * index. This method is only for depot indexes equal or greater than
3169 * COUNT_BBDEPOTINHEADER.
3171 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3173 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3174 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3175 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3176 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3177 ULONG blockIndex
= BLOCK_UNUSED
;
3178 ULONG extBlockIndex
;
3179 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3180 int index
, num_blocks
;
3182 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3184 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3185 return BLOCK_UNUSED
;
3187 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3189 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3191 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
);
3193 num_blocks
= This
->bigBlockSize
/ 4;
3195 for (index
= 0; index
< num_blocks
; index
++)
3197 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3198 This
->extBlockDepotCached
[index
] = blockIndex
;
3201 This
->indexExtBlockDepotCached
= extBlockCount
;
3204 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3209 /******************************************************************************
3210 * Storage32Impl_SetExtDepotBlock
3212 * Associates the specified block index to the specified depot index.
3213 * This method is only for depot indexes equal or greater than
3214 * COUNT_BBDEPOTINHEADER.
3216 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3218 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3219 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3220 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3221 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3222 ULONG extBlockIndex
;
3224 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3226 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3228 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3230 if (extBlockIndex
!= BLOCK_UNUSED
)
3232 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3233 extBlockOffset
* sizeof(ULONG
),
3237 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3239 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3243 /******************************************************************************
3244 * Storage32Impl_AddExtBlockDepot
3246 * Creates an extended depot block.
3248 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3250 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3251 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3252 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3253 ULONG index
= BLOCK_UNUSED
;
3254 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3255 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3256 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3258 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3259 blocksPerDepotBlock
;
3261 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3264 * The first extended block.
3266 This
->extBigBlockDepotStart
= index
;
3271 * Find the last existing extended block.
3273 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3276 * Add the new extended block to the chain.
3278 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3283 * Initialize this block.
3285 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3286 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3288 /* Add the block to our cache. */
3289 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3291 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3292 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3294 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3295 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3297 This
->extBigBlockDepotLocations
= new_cache
;
3298 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3300 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3305 /******************************************************************************
3306 * Storage32Impl_FreeBigBlock
3308 * This method will flag the specified block as free in the big block depot.
3310 static void StorageImpl_FreeBigBlock(
3314 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3316 if (blockIndex
< This
->prevFreeBlock
)
3317 This
->prevFreeBlock
= blockIndex
;
3320 /************************************************************************
3321 * Storage32Impl_GetNextBlockInChain
3323 * This method will retrieve the block index of the next big block in
3326 * Params: This - Pointer to the Storage object.
3327 * blockIndex - Index of the block to retrieve the chain
3329 * nextBlockIndex - receives the return value.
3331 * Returns: This method returns the index of the next block in the chain.
3332 * It will return the constants:
3333 * BLOCK_SPECIAL - If the block given was not part of a
3335 * BLOCK_END_OF_CHAIN - If the block given was the last in
3337 * BLOCK_UNUSED - If the block given was not past of a chain
3339 * BLOCK_EXTBBDEPOT - This block is part of the extended
3342 * See Windows documentation for more details on IStorage methods.
3344 static HRESULT
StorageImpl_GetNextBlockInChain(
3347 ULONG
* nextBlockIndex
)
3349 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3350 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3351 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3352 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3354 ULONG depotBlockIndexPos
;
3355 int index
, num_blocks
;
3357 *nextBlockIndex
= BLOCK_SPECIAL
;
3359 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3361 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3362 This
->bigBlockDepotCount
);
3363 return STG_E_READFAULT
;
3367 * Cache the currently accessed depot block.
3369 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3371 This
->indexBlockDepotCached
= depotBlockCount
;
3373 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3375 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3380 * We have to look in the extended depot.
3382 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3385 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3388 return STG_E_READFAULT
;
3390 num_blocks
= This
->bigBlockSize
/ 4;
3392 for (index
= 0; index
< num_blocks
; index
++)
3394 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3395 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3399 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3404 /******************************************************************************
3405 * Storage32Impl_GetNextExtendedBlock
3407 * Given an extended block this method will return the next extended block.
3410 * The last ULONG of an extended block is the block index of the next
3411 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3415 * - The index of the next extended block
3416 * - BLOCK_UNUSED: there is no next extended block.
3417 * - Any other return values denotes failure.
3419 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3421 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3422 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3424 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3427 return nextBlockIndex
;
3430 /******************************************************************************
3431 * Storage32Impl_SetNextBlockInChain
3433 * This method will write the index of the specified block's next block
3434 * in the big block depot.
3436 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3439 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3440 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3441 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3444 static void StorageImpl_SetNextBlockInChain(
3449 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3450 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3451 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3452 ULONG depotBlockIndexPos
;
3454 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3455 assert(blockIndex
!= nextBlock
);
3457 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3459 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3464 * We have to look in the extended depot.
3466 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3469 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3472 * Update the cached block depot, if necessary.
3474 if (depotBlockCount
== This
->indexBlockDepotCached
)
3476 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3480 /******************************************************************************
3481 * Storage32Impl_LoadFileHeader
3483 * This method will read in the file header
3485 static HRESULT
StorageImpl_LoadFileHeader(
3489 BYTE headerBigBlock
[HEADER_SIZE
];
3491 ULARGE_INTEGER offset
;
3496 * Get a pointer to the big block of data containing the header.
3498 offset
.u
.HighPart
= 0;
3499 offset
.u
.LowPart
= 0;
3500 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3501 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3502 hr
= STG_E_FILENOTFOUND
;
3505 * Extract the information from the header.
3510 * Check for the "magic number" signature and return an error if it is not
3513 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3515 return STG_E_OLDFORMAT
;
3518 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3520 return STG_E_INVALIDHEADER
;
3523 StorageUtl_ReadWord(
3525 OFFSET_BIGBLOCKSIZEBITS
,
3526 &This
->bigBlockSizeBits
);
3528 StorageUtl_ReadWord(
3530 OFFSET_SMALLBLOCKSIZEBITS
,
3531 &This
->smallBlockSizeBits
);
3533 StorageUtl_ReadDWord(
3535 OFFSET_BBDEPOTCOUNT
,
3536 &This
->bigBlockDepotCount
);
3538 StorageUtl_ReadDWord(
3540 OFFSET_ROOTSTARTBLOCK
,
3541 &This
->rootStartBlock
);
3543 StorageUtl_ReadDWord(
3545 OFFSET_SMALLBLOCKLIMIT
,
3546 &This
->smallBlockLimit
);
3548 StorageUtl_ReadDWord(
3550 OFFSET_SBDEPOTSTART
,
3551 &This
->smallBlockDepotStart
);
3553 StorageUtl_ReadDWord(
3555 OFFSET_EXTBBDEPOTSTART
,
3556 &This
->extBigBlockDepotStart
);
3558 StorageUtl_ReadDWord(
3560 OFFSET_EXTBBDEPOTCOUNT
,
3561 &This
->extBigBlockDepotCount
);
3563 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3565 StorageUtl_ReadDWord(
3567 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3568 &(This
->bigBlockDepotStart
[index
]));
3572 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3574 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3575 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3578 * Right now, the code is making some assumptions about the size of the
3579 * blocks, just make sure they are what we're expecting.
3581 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3582 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3583 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3585 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3586 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3587 hr
= STG_E_INVALIDHEADER
;
3596 /******************************************************************************
3597 * Storage32Impl_SaveFileHeader
3599 * This method will save to the file the header
3601 static void StorageImpl_SaveFileHeader(
3604 BYTE headerBigBlock
[HEADER_SIZE
];
3607 ULARGE_INTEGER offset
;
3608 DWORD bytes_read
, bytes_written
;
3609 DWORD major_version
, dirsectorcount
;
3612 * Get a pointer to the big block of data containing the header.
3614 offset
.u
.HighPart
= 0;
3615 offset
.u
.LowPart
= 0;
3616 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3617 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3618 hr
= STG_E_FILENOTFOUND
;
3620 if (This
->bigBlockSizeBits
== 0x9)
3622 else if (This
->bigBlockSizeBits
== 0xc)
3626 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3631 * If the block read failed, the file is probably new.
3636 * Initialize for all unknown fields.
3638 memset(headerBigBlock
, 0, HEADER_SIZE
);
3641 * Initialize the magic number.
3643 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3647 * Write the information to the header.
3649 StorageUtl_WriteWord(
3651 OFFSET_MINORVERSION
,
3654 StorageUtl_WriteWord(
3656 OFFSET_MAJORVERSION
,
3659 StorageUtl_WriteWord(
3661 OFFSET_BYTEORDERMARKER
,
3664 StorageUtl_WriteWord(
3666 OFFSET_BIGBLOCKSIZEBITS
,
3667 This
->bigBlockSizeBits
);
3669 StorageUtl_WriteWord(
3671 OFFSET_SMALLBLOCKSIZEBITS
,
3672 This
->smallBlockSizeBits
);
3674 if (major_version
>= 4)
3676 if (This
->rootBlockChain
)
3677 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3679 /* This file is being created, and it will start out with one block. */
3683 /* This field must be 0 in versions older than 4 */
3686 StorageUtl_WriteDWord(
3688 OFFSET_DIRSECTORCOUNT
,
3691 StorageUtl_WriteDWord(
3693 OFFSET_BBDEPOTCOUNT
,
3694 This
->bigBlockDepotCount
);
3696 StorageUtl_WriteDWord(
3698 OFFSET_ROOTSTARTBLOCK
,
3699 This
->rootStartBlock
);
3701 StorageUtl_WriteDWord(
3703 OFFSET_SMALLBLOCKLIMIT
,
3704 This
->smallBlockLimit
);
3706 StorageUtl_WriteDWord(
3708 OFFSET_SBDEPOTSTART
,
3709 This
->smallBlockDepotStart
);
3711 StorageUtl_WriteDWord(
3713 OFFSET_SBDEPOTCOUNT
,
3714 This
->smallBlockDepotChain
?
3715 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3717 StorageUtl_WriteDWord(
3719 OFFSET_EXTBBDEPOTSTART
,
3720 This
->extBigBlockDepotStart
);
3722 StorageUtl_WriteDWord(
3724 OFFSET_EXTBBDEPOTCOUNT
,
3725 This
->extBigBlockDepotCount
);
3727 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3729 StorageUtl_WriteDWord(
3731 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3732 (This
->bigBlockDepotStart
[index
]));
3736 * Write the big block back to the file.
3738 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3741 /******************************************************************************
3742 * StorageImpl_ReadRawDirEntry
3744 * This method will read the raw data from a directory entry in the file.
3746 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3748 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3750 ULARGE_INTEGER offset
;
3754 offset
.u
.HighPart
= 0;
3755 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3757 hr
= BlockChainStream_ReadAt(
3758 This
->rootBlockChain
,
3764 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3765 return STG_E_READFAULT
;
3770 /******************************************************************************
3771 * StorageImpl_WriteRawDirEntry
3773 * This method will write the raw data from a directory entry in the file.
3775 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3777 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3779 ULARGE_INTEGER offset
;
3783 offset
.u
.HighPart
= 0;
3784 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3786 hr
= BlockChainStream_WriteAt(
3787 This
->rootBlockChain
,
3796 /******************************************************************************
3799 * Update raw directory entry data from the fields in newData.
3801 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3803 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3805 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3808 buffer
+ OFFSET_PS_NAME
,
3810 DIRENTRY_NAME_BUFFER_LEN
);
3812 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3814 StorageUtl_WriteWord(
3816 OFFSET_PS_NAMELENGTH
,
3817 newData
->sizeOfNameString
);
3819 StorageUtl_WriteDWord(
3821 OFFSET_PS_LEFTCHILD
,
3822 newData
->leftChild
);
3824 StorageUtl_WriteDWord(
3826 OFFSET_PS_RIGHTCHILD
,
3827 newData
->rightChild
);
3829 StorageUtl_WriteDWord(
3832 newData
->dirRootEntry
);
3834 StorageUtl_WriteGUID(
3839 StorageUtl_WriteDWord(
3842 newData
->ctime
.dwLowDateTime
);
3844 StorageUtl_WriteDWord(
3846 OFFSET_PS_CTIMEHIGH
,
3847 newData
->ctime
.dwHighDateTime
);
3849 StorageUtl_WriteDWord(
3852 newData
->mtime
.dwLowDateTime
);
3854 StorageUtl_WriteDWord(
3856 OFFSET_PS_MTIMEHIGH
,
3857 newData
->ctime
.dwHighDateTime
);
3859 StorageUtl_WriteDWord(
3861 OFFSET_PS_STARTBLOCK
,
3862 newData
->startingBlock
);
3864 StorageUtl_WriteDWord(
3867 newData
->size
.u
.LowPart
);
3870 /******************************************************************************
3871 * Storage32Impl_ReadDirEntry
3873 * This method will read the specified directory entry.
3875 HRESULT
StorageImpl_ReadDirEntry(
3880 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3883 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3885 if (SUCCEEDED(readRes
))
3887 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3890 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3891 DIRENTRY_NAME_BUFFER_LEN
);
3892 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3894 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3896 StorageUtl_ReadWord(
3898 OFFSET_PS_NAMELENGTH
,
3899 &buffer
->sizeOfNameString
);
3901 StorageUtl_ReadDWord(
3903 OFFSET_PS_LEFTCHILD
,
3904 &buffer
->leftChild
);
3906 StorageUtl_ReadDWord(
3908 OFFSET_PS_RIGHTCHILD
,
3909 &buffer
->rightChild
);
3911 StorageUtl_ReadDWord(
3914 &buffer
->dirRootEntry
);
3916 StorageUtl_ReadGUID(
3921 StorageUtl_ReadDWord(
3924 &buffer
->ctime
.dwLowDateTime
);
3926 StorageUtl_ReadDWord(
3928 OFFSET_PS_CTIMEHIGH
,
3929 &buffer
->ctime
.dwHighDateTime
);
3931 StorageUtl_ReadDWord(
3934 &buffer
->mtime
.dwLowDateTime
);
3936 StorageUtl_ReadDWord(
3938 OFFSET_PS_MTIMEHIGH
,
3939 &buffer
->mtime
.dwHighDateTime
);
3941 StorageUtl_ReadDWord(
3943 OFFSET_PS_STARTBLOCK
,
3944 &buffer
->startingBlock
);
3946 StorageUtl_ReadDWord(
3949 &buffer
->size
.u
.LowPart
);
3951 buffer
->size
.u
.HighPart
= 0;
3957 /*********************************************************************
3958 * Write the specified directory entry to the file
3960 HRESULT
StorageImpl_WriteDirEntry(
3963 const DirEntry
* buffer
)
3965 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3968 UpdateRawDirEntry(currentEntry
, buffer
);
3970 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3974 static BOOL
StorageImpl_ReadBigBlock(
3979 ULARGE_INTEGER ulOffset
;
3982 ulOffset
.u
.HighPart
= 0;
3983 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3985 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3987 if (read
&& read
< This
->bigBlockSize
)
3989 /* File ends during this block; fill the rest with 0's. */
3990 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3996 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4002 ULARGE_INTEGER ulOffset
;
4006 ulOffset
.u
.HighPart
= 0;
4007 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4008 ulOffset
.u
.LowPart
+= offset
;
4010 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4011 *value
= lendian32toh(tmp
);
4012 return (read
== sizeof(DWORD
));
4015 static BOOL
StorageImpl_WriteBigBlock(
4020 ULARGE_INTEGER ulOffset
;
4023 ulOffset
.u
.HighPart
= 0;
4024 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4026 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4027 return (wrote
== This
->bigBlockSize
);
4030 static BOOL
StorageImpl_WriteDWordToBigBlock(
4036 ULARGE_INTEGER ulOffset
;
4039 ulOffset
.u
.HighPart
= 0;
4040 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4041 ulOffset
.u
.LowPart
+= offset
;
4043 value
= htole32(value
);
4044 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4045 return (wrote
== sizeof(DWORD
));
4048 /******************************************************************************
4049 * Storage32Impl_SmallBlocksToBigBlocks
4051 * This method will convert a small block chain to a big block chain.
4052 * The small block chain will be destroyed.
4054 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4056 SmallBlockChainStream
** ppsbChain
)
4058 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4059 ULARGE_INTEGER size
, offset
;
4060 ULONG cbRead
, cbWritten
;
4061 ULARGE_INTEGER cbTotalRead
;
4062 DirRef streamEntryRef
;
4063 HRESULT resWrite
= S_OK
;
4065 DirEntry streamEntry
;
4067 BlockChainStream
*bbTempChain
= NULL
;
4068 BlockChainStream
*bigBlockChain
= NULL
;
4071 * Create a temporary big block chain that doesn't have
4072 * an associated directory entry. This temporary chain will be
4073 * used to copy data from small blocks to big blocks.
4075 bbTempChain
= BlockChainStream_Construct(This
,
4078 if(!bbTempChain
) return NULL
;
4080 * Grow the big block chain.
4082 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4083 BlockChainStream_SetSize(bbTempChain
, size
);
4086 * Copy the contents of the small block chain to the big block chain
4087 * by small block size increments.
4089 offset
.u
.LowPart
= 0;
4090 offset
.u
.HighPart
= 0;
4091 cbTotalRead
.QuadPart
= 0;
4093 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4096 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4098 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4101 if (FAILED(resRead
))
4106 cbTotalRead
.QuadPart
+= cbRead
;
4108 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4114 if (FAILED(resWrite
))
4117 offset
.u
.LowPart
+= cbRead
;
4121 resRead
= STG_E_READFAULT
;
4124 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4125 HeapFree(GetProcessHeap(),0,buffer
);
4127 size
.u
.HighPart
= 0;
4130 if (FAILED(resRead
) || FAILED(resWrite
))
4132 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4133 BlockChainStream_SetSize(bbTempChain
, size
);
4134 BlockChainStream_Destroy(bbTempChain
);
4139 * Destroy the small block chain.
4141 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4142 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4143 SmallBlockChainStream_Destroy(*ppsbChain
);
4147 * Change the directory entry. This chain is now a big block chain
4148 * and it doesn't reside in the small blocks chain anymore.
4150 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4152 streamEntry
.startingBlock
= bbHeadOfChain
;
4154 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4157 * Destroy the temporary entryless big block chain.
4158 * Create a new big block chain associated with this entry.
4160 BlockChainStream_Destroy(bbTempChain
);
4161 bigBlockChain
= BlockChainStream_Construct(This
,
4165 return bigBlockChain
;
4168 /******************************************************************************
4169 * Storage32Impl_BigBlocksToSmallBlocks
4171 * This method will convert a big block chain to a small block chain.
4172 * The big block chain will be destroyed on success.
4174 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4176 BlockChainStream
** ppbbChain
,
4177 ULARGE_INTEGER newSize
)
4179 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4180 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4181 DirRef streamEntryRef
;
4182 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4183 DirEntry streamEntry
;
4185 SmallBlockChainStream
* sbTempChain
;
4187 TRACE("%p %p\n", This
, ppbbChain
);
4189 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4195 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4196 size
= BlockChainStream_GetSize(*ppbbChain
);
4197 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4199 offset
.u
.HighPart
= 0;
4200 offset
.u
.LowPart
= 0;
4201 cbTotalRead
.QuadPart
= 0;
4202 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4203 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4205 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4206 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4214 cbTotalRead
.QuadPart
+= cbRead
;
4216 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4217 cbRead
, buffer
, &cbWritten
);
4219 if(FAILED(resWrite
))
4222 offset
.u
.LowPart
+= cbRead
;
4226 resRead
= STG_E_READFAULT
;
4230 HeapFree(GetProcessHeap(), 0, buffer
);
4232 size
.u
.HighPart
= 0;
4235 if(FAILED(resRead
) || FAILED(resWrite
))
4237 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4238 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4239 SmallBlockChainStream_Destroy(sbTempChain
);
4243 /* destroy the original big block chain */
4244 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4245 BlockChainStream_SetSize(*ppbbChain
, size
);
4246 BlockChainStream_Destroy(*ppbbChain
);
4249 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4250 streamEntry
.startingBlock
= sbHeadOfChain
;
4251 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4253 SmallBlockChainStream_Destroy(sbTempChain
);
4254 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4257 static HRESULT
StorageBaseImpl_CopyStream(
4258 StorageBaseImpl
*dst
, DirRef dst_entry
,
4259 StorageBaseImpl
*src
, DirRef src_entry
)
4264 ULARGE_INTEGER bytes_copied
;
4265 ULONG bytestocopy
, bytesread
, byteswritten
;
4267 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4271 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4273 bytes_copied
.QuadPart
= 0;
4274 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4276 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4278 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4280 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4283 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4284 data
, &byteswritten
);
4287 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4288 bytes_copied
.QuadPart
+= byteswritten
;
4296 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4298 DirRef result
=This
->firstFreeEntry
;
4300 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4303 if (result
== This
->entries_size
)
4305 ULONG new_size
= This
->entries_size
* 2;
4306 TransactedDirEntry
*new_entries
;
4308 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4309 if (!new_entries
) return DIRENTRY_NULL
;
4311 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4312 HeapFree(GetProcessHeap(), 0, This
->entries
);
4314 This
->entries
= new_entries
;
4315 This
->entries_size
= new_size
;
4318 This
->entries
[result
].inuse
= 1;
4320 This
->firstFreeEntry
= result
+1;
4325 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4326 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4328 DirRef stubEntryRef
;
4329 TransactedDirEntry
*entry
;
4331 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4333 if (stubEntryRef
!= DIRENTRY_NULL
)
4335 entry
= &This
->entries
[stubEntryRef
];
4337 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4342 return stubEntryRef
;
4345 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4346 TransactedSnapshotImpl
*This
, DirRef entry
)
4351 if (!This
->entries
[entry
].read
)
4353 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4354 This
->entries
[entry
].transactedParentEntry
,
4357 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4359 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4361 if (data
.leftChild
== DIRENTRY_NULL
)
4365 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4367 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4369 if (data
.rightChild
== DIRENTRY_NULL
)
4373 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4375 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4377 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4383 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4384 This
->entries
[entry
].read
= 1;
4391 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4392 TransactedSnapshotImpl
*This
, DirRef entry
)
4396 if (!This
->entries
[entry
].stream_dirty
)
4398 DirEntry new_entrydata
;
4400 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4401 new_entrydata
.name
[0] = 'S';
4402 new_entrydata
.sizeOfNameString
= 1;
4403 new_entrydata
.stgType
= STGTY_STREAM
;
4404 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4405 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4406 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4407 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4409 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4410 &This
->entries
[entry
].stream_entry
);
4412 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4414 hr
= StorageBaseImpl_CopyStream(
4415 This
->scratch
, This
->entries
[entry
].stream_entry
,
4416 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4419 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4423 This
->entries
[entry
].stream_dirty
= 1;
4425 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4427 /* Since this entry is modified, and we aren't using its stream data, we
4428 * no longer care about the original entry. */
4430 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4432 if (delete_ref
!= DIRENTRY_NULL
)
4433 This
->entries
[delete_ref
].deleted
= 1;
4435 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4442 /* Find the first entry in a depth-first traversal. */
4443 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4444 TransactedSnapshotImpl
* This
, DirRef parent
)
4446 DirRef cursor
, prev
;
4447 TransactedDirEntry
*entry
;
4450 entry
= &This
->entries
[cursor
];
4453 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4456 cursor
= entry
->data
.leftChild
;
4457 entry
= &This
->entries
[cursor
];
4458 entry
->parent
= prev
;
4460 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4463 cursor
= entry
->data
.rightChild
;
4464 entry
= &This
->entries
[cursor
];
4465 entry
->parent
= prev
;
4467 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4470 cursor
= entry
->data
.dirRootEntry
;
4471 entry
= &This
->entries
[cursor
];
4472 entry
->parent
= prev
;
4481 /* Find the next entry in a depth-first traversal. */
4482 static DirRef
TransactedSnapshotImpl_FindNextChild(
4483 TransactedSnapshotImpl
* This
, DirRef current
)
4486 TransactedDirEntry
*parent_entry
;
4488 parent
= This
->entries
[current
].parent
;
4489 parent_entry
= &This
->entries
[parent
];
4491 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4493 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4495 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4496 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4499 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4501 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4502 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4509 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4510 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4511 TransactedSnapshotImpl
* This
, DirRef entry
)
4513 return entry
!= DIRENTRY_NULL
&&
4514 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4517 /* Destroy the entries created by CopyTree. */
4518 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4519 TransactedSnapshotImpl
* This
, DirRef stop
)
4522 TransactedDirEntry
*entry
;
4523 ULARGE_INTEGER zero
;
4527 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4530 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4532 if (cursor
== DIRENTRY_NULL
)
4535 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4537 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4539 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4541 entry
= &This
->entries
[cursor
];
4543 if (entry
->stream_dirty
)
4544 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4545 entry
->newTransactedParentEntry
, zero
);
4547 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4548 entry
->newTransactedParentEntry
);
4550 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4553 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4557 /* Make a copy of our edited tree that we can use in the parent. */
4558 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4561 TransactedDirEntry
*entry
;
4564 cursor
= This
->base
.storageDirEntry
;
4565 entry
= &This
->entries
[cursor
];
4566 entry
->parent
= DIRENTRY_NULL
;
4567 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4569 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4572 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4574 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4575 entry
= &This
->entries
[cursor
];
4577 while (cursor
!= DIRENTRY_NULL
)
4579 /* Make a copy of this entry in the transacted parent. */
4581 (!entry
->dirty
&& !entry
->stream_dirty
&&
4582 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4583 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4584 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4585 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4590 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4592 newData
.size
.QuadPart
= 0;
4593 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4595 if (newData
.leftChild
!= DIRENTRY_NULL
)
4596 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4598 if (newData
.rightChild
!= DIRENTRY_NULL
)
4599 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4601 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4602 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4604 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4605 &entry
->newTransactedParentEntry
);
4608 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4612 if (entry
->stream_dirty
)
4614 hr
= StorageBaseImpl_CopyStream(
4615 This
->transactedParent
, entry
->newTransactedParentEntry
,
4616 This
->scratch
, entry
->stream_entry
);
4618 else if (entry
->data
.size
.QuadPart
)
4620 hr
= StorageBaseImpl_StreamLink(
4621 This
->transactedParent
, entry
->newTransactedParentEntry
,
4622 entry
->transactedParentEntry
);
4627 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4628 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4633 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4634 entry
= &This
->entries
[cursor
];
4640 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4642 DWORD grfCommitFlags
) /* [in] */
4644 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4645 TransactedDirEntry
*root_entry
;
4646 DirRef i
, dir_root_ref
;
4648 ULARGE_INTEGER zero
;
4653 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4655 /* Cannot commit a read-only transacted storage */
4656 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4657 return STG_E_ACCESSDENIED
;
4659 /* To prevent data loss, we create the new structure in the file before we
4660 * delete the old one, so that in case of errors the old data is intact. We
4661 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4662 * needed in the rare situation where we have just enough free disk space to
4663 * overwrite the existing data. */
4665 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4667 if (!root_entry
->read
)
4670 hr
= TransactedSnapshotImpl_CopyTree(This
);
4671 if (FAILED(hr
)) return hr
;
4673 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4674 dir_root_ref
= DIRENTRY_NULL
;
4676 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4678 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4680 /* Update the storage to use the new data in one step. */
4682 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4683 root_entry
->transactedParentEntry
, &data
);
4687 data
.dirRootEntry
= dir_root_ref
;
4688 data
.clsid
= root_entry
->data
.clsid
;
4689 data
.ctime
= root_entry
->data
.ctime
;
4690 data
.mtime
= root_entry
->data
.mtime
;
4692 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4693 root_entry
->transactedParentEntry
, &data
);
4696 /* Try to flush after updating the root storage, but if the flush fails, keep
4697 * going, on the theory that it'll either succeed later or the subsequent
4698 * writes will fail. */
4699 StorageBaseImpl_Flush(This
->transactedParent
);
4703 /* Destroy the old now-orphaned data. */
4704 for (i
=0; i
<This
->entries_size
; i
++)
4706 TransactedDirEntry
*entry
= &This
->entries
[i
];
4711 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4712 entry
->transactedParentEntry
, zero
);
4713 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4714 entry
->transactedParentEntry
);
4715 memset(entry
, 0, sizeof(TransactedDirEntry
));
4716 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4718 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4720 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4721 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4722 entry
->transactedParentEntry
);
4723 if (entry
->stream_dirty
)
4725 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4726 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4727 entry
->stream_dirty
= 0;
4730 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4737 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4741 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4746 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4749 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4750 ULARGE_INTEGER zero
;
4753 TRACE("(%p)\n", iface
);
4755 /* Destroy the open objects. */
4756 StorageBaseImpl_DeleteAll(&This
->base
);
4758 /* Clear out the scratch file. */
4760 for (i
=0; i
<This
->entries_size
; i
++)
4762 if (This
->entries
[i
].stream_dirty
)
4764 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4767 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4771 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4773 This
->firstFreeEntry
= 0;
4774 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4779 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4781 if (!This
->reverted
)
4783 TRACE("Storage invalidated (stg=%p)\n", This
);
4787 StorageBaseImpl_DeleteAll(This
);
4791 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4793 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4795 TransactedSnapshotImpl_Revert((IStorage
*)iface
);
4797 IStorage_Release((IStorage
*)This
->transactedParent
);
4799 IStorage_Release((IStorage
*)This
->scratch
);
4801 HeapFree(GetProcessHeap(), 0, This
->entries
);
4803 HeapFree(GetProcessHeap(), 0, This
);
4806 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4808 /* We only need to flush when committing. */
4812 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4814 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4816 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4819 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4820 const DirEntry
*newData
, DirRef
*index
)
4822 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4824 TransactedDirEntry
*new_entry
;
4826 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4827 if (new_ref
== DIRENTRY_NULL
)
4828 return E_OUTOFMEMORY
;
4830 new_entry
= &This
->entries
[new_ref
];
4832 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4833 new_entry
->read
= 1;
4834 new_entry
->dirty
= 1;
4835 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4839 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4844 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4845 DirRef index
, const DirEntry
*data
)
4847 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4850 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4852 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4853 if (FAILED(hr
)) return hr
;
4855 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4857 if (index
!= This
->base
.storageDirEntry
)
4859 This
->entries
[index
].dirty
= 1;
4861 if (data
->size
.QuadPart
== 0 &&
4862 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4864 /* Since this entry is modified, and we aren't using its stream data, we
4865 * no longer care about the original entry. */
4867 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4869 if (delete_ref
!= DIRENTRY_NULL
)
4870 This
->entries
[delete_ref
].deleted
= 1;
4872 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4879 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4880 DirRef index
, DirEntry
*data
)
4882 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4885 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4886 if (FAILED(hr
)) return hr
;
4888 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4890 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4895 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4898 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4900 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4901 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4903 /* If we deleted this entry while it has stream data. We must have left the
4904 * data because some other entry is using it, and we need to leave the
4905 * original entry alone. */
4906 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4907 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4911 This
->entries
[index
].deleted
= 1;
4917 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4918 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4920 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4922 if (This
->entries
[index
].stream_dirty
)
4924 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4925 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4927 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4929 /* This stream doesn't live in the parent, and we haven't allocated storage
4936 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4937 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4941 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4942 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4944 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4947 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4948 if (FAILED(hr
)) return hr
;
4950 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4951 if (FAILED(hr
)) return hr
;
4953 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4954 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4956 if (SUCCEEDED(hr
) && size
!= 0)
4957 This
->entries
[index
].data
.size
.QuadPart
= max(
4958 This
->entries
[index
].data
.size
.QuadPart
,
4959 offset
.QuadPart
+ size
);
4964 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4965 DirRef index
, ULARGE_INTEGER newsize
)
4967 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4970 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4971 if (FAILED(hr
)) return hr
;
4973 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4976 if (newsize
.QuadPart
== 0)
4978 /* Destroy any parent references or entries in the scratch file. */
4979 if (This
->entries
[index
].stream_dirty
)
4981 ULARGE_INTEGER zero
;
4983 StorageBaseImpl_StreamSetSize(This
->scratch
,
4984 This
->entries
[index
].stream_entry
, zero
);
4985 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4986 This
->entries
[index
].stream_entry
);
4987 This
->entries
[index
].stream_dirty
= 0;
4989 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4992 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4994 if (delete_ref
!= DIRENTRY_NULL
)
4995 This
->entries
[delete_ref
].deleted
= 1;
4997 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5002 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5003 if (FAILED(hr
)) return hr
;
5005 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5006 This
->entries
[index
].stream_entry
, newsize
);
5010 This
->entries
[index
].data
.size
= newsize
;
5015 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5016 DirRef dst
, DirRef src
)
5018 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5020 TransactedDirEntry
*dst_entry
, *src_entry
;
5022 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5023 if (FAILED(hr
)) return hr
;
5025 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5026 if (FAILED(hr
)) return hr
;
5028 dst_entry
= &This
->entries
[dst
];
5029 src_entry
= &This
->entries
[src
];
5031 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5032 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5033 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5034 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5035 dst_entry
->data
.size
= src_entry
->data
.size
;
5040 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5042 StorageBaseImpl_QueryInterface
,
5043 StorageBaseImpl_AddRef
,
5044 StorageBaseImpl_Release
,
5045 StorageBaseImpl_CreateStream
,
5046 StorageBaseImpl_OpenStream
,
5047 StorageBaseImpl_CreateStorage
,
5048 StorageBaseImpl_OpenStorage
,
5049 StorageBaseImpl_CopyTo
,
5050 StorageBaseImpl_MoveElementTo
,
5051 TransactedSnapshotImpl_Commit
,
5052 TransactedSnapshotImpl_Revert
,
5053 StorageBaseImpl_EnumElements
,
5054 StorageBaseImpl_DestroyElement
,
5055 StorageBaseImpl_RenameElement
,
5056 StorageBaseImpl_SetElementTimes
,
5057 StorageBaseImpl_SetClass
,
5058 StorageBaseImpl_SetStateBits
,
5059 StorageBaseImpl_Stat
5062 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5064 TransactedSnapshotImpl_Destroy
,
5065 TransactedSnapshotImpl_Invalidate
,
5066 TransactedSnapshotImpl_Flush
,
5067 TransactedSnapshotImpl_GetFilename
,
5068 TransactedSnapshotImpl_CreateDirEntry
,
5069 TransactedSnapshotImpl_WriteDirEntry
,
5070 TransactedSnapshotImpl_ReadDirEntry
,
5071 TransactedSnapshotImpl_DestroyDirEntry
,
5072 TransactedSnapshotImpl_StreamReadAt
,
5073 TransactedSnapshotImpl_StreamWriteAt
,
5074 TransactedSnapshotImpl_StreamSetSize
,
5075 TransactedSnapshotImpl_StreamLink
5078 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5079 TransactedSnapshotImpl
** result
)
5083 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5086 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5088 /* This is OK because the property set storage functions use the IStorage functions. */
5089 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
5091 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5093 list_init(&(*result
)->base
.strmHead
);
5095 list_init(&(*result
)->base
.storageHead
);
5097 (*result
)->base
.ref
= 1;
5099 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5101 /* Create a new temporary storage to act as the scratch file. */
5102 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5103 0, (IStorage
**)&(*result
)->scratch
);
5107 ULONG num_entries
= 20;
5109 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5111 (*result
)->entries_size
= num_entries
;
5113 (*result
)->firstFreeEntry
= 0;
5115 if ((*result
)->entries
)
5117 /* parentStorage already has 1 reference, which we take over here. */
5118 (*result
)->transactedParent
= parentStorage
;
5120 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5122 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5126 IStorage_Release((IStorage
*)(*result
)->scratch
);
5132 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5137 return E_OUTOFMEMORY
;
5140 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5141 StorageBaseImpl
** result
)
5145 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5147 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5150 return TransactedSnapshotImpl_Construct(parentStorage
,
5151 (TransactedSnapshotImpl
**)result
);
5154 static HRESULT
Storage_Construct(
5162 StorageBaseImpl
** result
)
5164 StorageImpl
*newStorage
;
5165 StorageBaseImpl
*newTransactedStorage
;
5168 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5169 if (FAILED(hr
)) goto end
;
5171 if (openFlags
& STGM_TRANSACTED
)
5173 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5175 IStorage_Release((IStorage
*)newStorage
);
5177 *result
= newTransactedStorage
;
5180 *result
= &newStorage
->base
;
5186 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5188 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5190 if (!This
->base
.reverted
)
5192 TRACE("Storage invalidated (stg=%p)\n", This
);
5194 This
->base
.reverted
= 1;
5196 This
->parentStorage
= NULL
;
5198 StorageBaseImpl_DeleteAll(&This
->base
);
5200 list_remove(&This
->ParentListEntry
);
5204 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5206 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5208 StorageInternalImpl_Invalidate(&This
->base
);
5210 HeapFree(GetProcessHeap(), 0, This
);
5213 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5215 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5217 return StorageBaseImpl_Flush(This
->parentStorage
);
5220 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5222 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5224 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5227 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5228 const DirEntry
*newData
, DirRef
*index
)
5230 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5232 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5236 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5237 DirRef index
, const DirEntry
*data
)
5239 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5241 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5245 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5246 DirRef index
, DirEntry
*data
)
5248 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5250 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5254 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5257 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5259 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5263 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5264 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5266 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5268 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5269 index
, offset
, size
, buffer
, bytesRead
);
5272 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5273 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5275 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5277 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5278 index
, offset
, size
, buffer
, bytesWritten
);
5281 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5282 DirRef index
, ULARGE_INTEGER newsize
)
5284 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5286 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5290 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5291 DirRef dst
, DirRef src
)
5293 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5295 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5299 /******************************************************************************
5301 ** Storage32InternalImpl_Commit
5304 static HRESULT WINAPI
StorageInternalImpl_Commit(
5306 DWORD grfCommitFlags
) /* [in] */
5308 StorageBaseImpl
* base
= (StorageBaseImpl
*) iface
;
5309 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5310 return StorageBaseImpl_Flush(base
);
5313 /******************************************************************************
5315 ** Storage32InternalImpl_Revert
5318 static HRESULT WINAPI
StorageInternalImpl_Revert(
5321 FIXME("(%p): stub\n", iface
);
5325 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5327 IStorage_Release((IStorage
*)This
->parentStorage
);
5328 HeapFree(GetProcessHeap(), 0, This
);
5331 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5332 IEnumSTATSTG
* iface
,
5336 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5339 return E_INVALIDARG
;
5343 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5344 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5347 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
5351 return E_NOINTERFACE
;
5354 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5355 IEnumSTATSTG
* iface
)
5357 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5358 return InterlockedIncrement(&This
->ref
);
5361 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5362 IEnumSTATSTG
* iface
)
5364 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5368 newRef
= InterlockedDecrement(&This
->ref
);
5372 IEnumSTATSTGImpl_Destroy(This
);
5378 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5379 IEnumSTATSTGImpl
* This
,
5382 DirRef result
= DIRENTRY_NULL
;
5386 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5388 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5389 This
->parentStorage
->storageDirEntry
, &entry
);
5390 searchNode
= entry
.dirRootEntry
;
5392 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5394 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5398 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5402 searchNode
= entry
.rightChild
;
5406 result
= searchNode
;
5407 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5408 searchNode
= entry
.leftChild
;
5416 if (result
!= DIRENTRY_NULL
)
5417 memcpy(This
->name
, result_name
, sizeof(result_name
));
5423 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5424 IEnumSTATSTG
* iface
,
5427 ULONG
* pceltFetched
)
5429 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5431 DirEntry currentEntry
;
5432 STATSTG
* currentReturnStruct
= rgelt
;
5433 ULONG objectFetched
= 0;
5434 DirRef currentSearchNode
;
5437 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5438 return E_INVALIDARG
;
5440 if (This
->parentStorage
->reverted
)
5441 return STG_E_REVERTED
;
5444 * To avoid the special case, get another pointer to a ULONG value if
5445 * the caller didn't supply one.
5447 if (pceltFetched
==0)
5448 pceltFetched
= &objectFetched
;
5451 * Start the iteration, we will iterate until we hit the end of the
5452 * linked list or until we hit the number of items to iterate through
5456 while ( *pceltFetched
< celt
)
5458 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5460 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5464 * Read the entry from the storage.
5466 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5471 * Copy the information to the return buffer.
5473 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5474 currentReturnStruct
,
5479 * Step to the next item in the iteration
5482 currentReturnStruct
++;
5485 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5492 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5493 IEnumSTATSTG
* iface
,
5496 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5498 ULONG objectFetched
= 0;
5499 DirRef currentSearchNode
;
5502 if (This
->parentStorage
->reverted
)
5503 return STG_E_REVERTED
;
5505 while ( (objectFetched
< celt
) )
5507 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5509 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5515 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5521 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5522 IEnumSTATSTG
* iface
)
5524 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5526 if (This
->parentStorage
->reverted
)
5527 return STG_E_REVERTED
;
5534 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5535 IEnumSTATSTG
* iface
,
5536 IEnumSTATSTG
** ppenum
)
5538 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
5540 IEnumSTATSTGImpl
* newClone
;
5542 if (This
->parentStorage
->reverted
)
5543 return STG_E_REVERTED
;
5546 * Perform a sanity check on the parameters.
5549 return E_INVALIDARG
;
5551 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5552 This
->storageDirEntry
);
5556 * The new clone enumeration must point to the same current node as
5559 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5561 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
5564 * Don't forget to nail down a reference to the clone before
5567 IEnumSTATSTGImpl_AddRef(*ppenum
);
5573 * Virtual function table for the IEnumSTATSTGImpl class.
5575 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5577 IEnumSTATSTGImpl_QueryInterface
,
5578 IEnumSTATSTGImpl_AddRef
,
5579 IEnumSTATSTGImpl_Release
,
5580 IEnumSTATSTGImpl_Next
,
5581 IEnumSTATSTGImpl_Skip
,
5582 IEnumSTATSTGImpl_Reset
,
5583 IEnumSTATSTGImpl_Clone
5586 /******************************************************************************
5587 ** IEnumSTATSTGImpl implementation
5590 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5591 StorageBaseImpl
* parentStorage
,
5592 DirRef storageDirEntry
)
5594 IEnumSTATSTGImpl
* newEnumeration
;
5596 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5598 if (newEnumeration
!=0)
5601 * Set-up the virtual function table and reference count.
5603 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5604 newEnumeration
->ref
= 0;
5607 * We want to nail-down the reference to the storage in case the
5608 * enumeration out-lives the storage in the client application.
5610 newEnumeration
->parentStorage
= parentStorage
;
5611 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
5613 newEnumeration
->storageDirEntry
= storageDirEntry
;
5616 * Make sure the current node of the iterator is the first one.
5618 IEnumSTATSTGImpl_Reset(&newEnumeration
->IEnumSTATSTG_iface
);
5621 return newEnumeration
;
5625 * Virtual function table for the Storage32InternalImpl class.
5627 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5629 StorageBaseImpl_QueryInterface
,
5630 StorageBaseImpl_AddRef
,
5631 StorageBaseImpl_Release
,
5632 StorageBaseImpl_CreateStream
,
5633 StorageBaseImpl_OpenStream
,
5634 StorageBaseImpl_CreateStorage
,
5635 StorageBaseImpl_OpenStorage
,
5636 StorageBaseImpl_CopyTo
,
5637 StorageBaseImpl_MoveElementTo
,
5638 StorageInternalImpl_Commit
,
5639 StorageInternalImpl_Revert
,
5640 StorageBaseImpl_EnumElements
,
5641 StorageBaseImpl_DestroyElement
,
5642 StorageBaseImpl_RenameElement
,
5643 StorageBaseImpl_SetElementTimes
,
5644 StorageBaseImpl_SetClass
,
5645 StorageBaseImpl_SetStateBits
,
5646 StorageBaseImpl_Stat
5649 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5651 StorageInternalImpl_Destroy
,
5652 StorageInternalImpl_Invalidate
,
5653 StorageInternalImpl_Flush
,
5654 StorageInternalImpl_GetFilename
,
5655 StorageInternalImpl_CreateDirEntry
,
5656 StorageInternalImpl_WriteDirEntry
,
5657 StorageInternalImpl_ReadDirEntry
,
5658 StorageInternalImpl_DestroyDirEntry
,
5659 StorageInternalImpl_StreamReadAt
,
5660 StorageInternalImpl_StreamWriteAt
,
5661 StorageInternalImpl_StreamSetSize
,
5662 StorageInternalImpl_StreamLink
5665 /******************************************************************************
5666 ** Storage32InternalImpl implementation
5669 static StorageInternalImpl
* StorageInternalImpl_Construct(
5670 StorageBaseImpl
* parentStorage
,
5672 DirRef storageDirEntry
)
5674 StorageInternalImpl
* newStorage
;
5676 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5680 list_init(&newStorage
->base
.strmHead
);
5682 list_init(&newStorage
->base
.storageHead
);
5685 * Initialize the virtual function table.
5687 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5688 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5689 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5690 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5692 newStorage
->base
.reverted
= 0;
5694 newStorage
->base
.ref
= 1;
5696 newStorage
->parentStorage
= parentStorage
;
5699 * Keep a reference to the directory entry of this storage
5701 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5703 newStorage
->base
.create
= 0;
5711 /******************************************************************************
5712 ** StorageUtl implementation
5715 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5719 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5720 *value
= lendian16toh(tmp
);
5723 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5725 value
= htole16(value
);
5726 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5729 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5733 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5734 *value
= lendian32toh(tmp
);
5737 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5739 value
= htole32(value
);
5740 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5743 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5744 ULARGE_INTEGER
* value
)
5746 #ifdef WORDS_BIGENDIAN
5749 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5750 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5751 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5753 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5757 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5758 const ULARGE_INTEGER
*value
)
5760 #ifdef WORDS_BIGENDIAN
5763 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5764 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5765 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5767 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5771 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5773 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5774 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5775 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5777 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5780 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5782 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5783 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5784 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5786 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5789 void StorageUtl_CopyDirEntryToSTATSTG(
5790 StorageBaseImpl
* storage
,
5791 STATSTG
* destination
,
5792 const DirEntry
* source
,
5796 * The copy of the string occurs only when the flag is not set
5798 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5800 /* Use the filename for the root storage. */
5801 destination
->pwcsName
= 0;
5802 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5804 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5805 (source
->name
[0] == 0) )
5807 destination
->pwcsName
= 0;
5811 destination
->pwcsName
=
5812 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5814 strcpyW(destination
->pwcsName
, source
->name
);
5817 switch (source
->stgType
)
5821 destination
->type
= STGTY_STORAGE
;
5824 destination
->type
= STGTY_STREAM
;
5827 destination
->type
= STGTY_STREAM
;
5831 destination
->cbSize
= source
->size
;
5833 currentReturnStruct->mtime = {0}; TODO
5834 currentReturnStruct->ctime = {0};
5835 currentReturnStruct->atime = {0};
5837 destination
->grfMode
= 0;
5838 destination
->grfLocksSupported
= 0;
5839 destination
->clsid
= source
->clsid
;
5840 destination
->grfStateBits
= 0;
5841 destination
->reserved
= 0;
5844 /******************************************************************************
5845 ** BlockChainStream implementation
5848 /* Read and save the index of all blocks in this stream. */
5849 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5851 ULONG next_sector
, next_offset
;
5853 struct BlockChainRun
*last_run
;
5855 if (This
->indexCacheLen
== 0)
5859 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5863 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5864 next_offset
= last_run
->lastOffset
+1;
5865 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5866 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5868 if (FAILED(hr
)) return hr
;
5871 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5873 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5875 /* Add the current block to the cache. */
5876 if (This
->indexCacheSize
== 0)
5878 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5879 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5880 This
->indexCacheSize
= 16;
5882 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5884 struct BlockChainRun
*new_cache
;
5887 new_size
= This
->indexCacheSize
* 2;
5888 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5889 if (!new_cache
) return E_OUTOFMEMORY
;
5890 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5892 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5893 This
->indexCache
= new_cache
;
5894 This
->indexCacheSize
= new_size
;
5897 This
->indexCacheLen
++;
5898 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5899 last_run
->firstSector
= next_sector
;
5900 last_run
->firstOffset
= next_offset
;
5903 last_run
->lastOffset
= next_offset
;
5905 /* Find the next block. */
5907 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5908 if (FAILED(hr
)) return hr
;
5911 if (This
->indexCacheLen
)
5913 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5914 This
->numBlocks
= last_run
->lastOffset
+1;
5918 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5919 This
->numBlocks
= 0;
5925 /* Locate the nth block in this stream. */
5926 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5928 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5929 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5931 if (offset
>= This
->numBlocks
)
5932 return BLOCK_END_OF_CHAIN
;
5934 while (min_run
< max_run
)
5936 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5937 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5939 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5940 max_run
= run_to_check
-1;
5942 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5944 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5945 min_run
= run_to_check
+1;
5948 /* Block is in this run. */
5949 min_run
= max_run
= run_to_check
;
5952 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5955 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5956 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5958 BlockChainBlock
*result
=NULL
;
5962 if (This
->cachedBlocks
[i
].index
== index
)
5964 *sector
= This
->cachedBlocks
[i
].sector
;
5965 *block
= &This
->cachedBlocks
[i
];
5969 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5970 if (*sector
== BLOCK_END_OF_CHAIN
)
5971 return STG_E_DOCFILECORRUPT
;
5975 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5976 result
= &This
->cachedBlocks
[0];
5977 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5978 result
= &This
->cachedBlocks
[1];
5981 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5982 if (This
->blockToEvict
== 2)
5983 This
->blockToEvict
= 0;
5988 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5989 return STG_E_WRITEFAULT
;
5994 result
->index
= index
;
5995 result
->sector
= *sector
;
6002 BlockChainStream
* BlockChainStream_Construct(
6003 StorageImpl
* parentStorage
,
6004 ULONG
* headOfStreamPlaceHolder
,
6007 BlockChainStream
* newStream
;
6009 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6011 newStream
->parentStorage
= parentStorage
;
6012 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6013 newStream
->ownerDirEntry
= dirEntry
;
6014 newStream
->indexCache
= NULL
;
6015 newStream
->indexCacheLen
= 0;
6016 newStream
->indexCacheSize
= 0;
6017 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6018 newStream
->cachedBlocks
[0].dirty
= 0;
6019 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6020 newStream
->cachedBlocks
[1].dirty
= 0;
6021 newStream
->blockToEvict
= 0;
6023 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6025 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6026 HeapFree(GetProcessHeap(), 0, newStream
);
6033 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6036 if (!This
) return S_OK
;
6039 if (This
->cachedBlocks
[i
].dirty
)
6041 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6042 This
->cachedBlocks
[i
].dirty
= 0;
6044 return STG_E_WRITEFAULT
;
6050 void BlockChainStream_Destroy(BlockChainStream
* This
)
6054 BlockChainStream_Flush(This
);
6055 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6057 HeapFree(GetProcessHeap(), 0, This
);
6060 /******************************************************************************
6061 * BlockChainStream_GetHeadOfChain
6063 * Returns the head of this stream chain.
6064 * Some special chains don't have directory entries, their heads are kept in
6065 * This->headOfStreamPlaceHolder.
6068 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6070 DirEntry chainEntry
;
6073 if (This
->headOfStreamPlaceHolder
!= 0)
6074 return *(This
->headOfStreamPlaceHolder
);
6076 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6078 hr
= StorageImpl_ReadDirEntry(
6079 This
->parentStorage
,
6080 This
->ownerDirEntry
,
6085 return chainEntry
.startingBlock
;
6089 return BLOCK_END_OF_CHAIN
;
6092 /******************************************************************************
6093 * BlockChainStream_GetCount
6095 * Returns the number of blocks that comprises this chain.
6096 * This is not the size of the stream as the last block may not be full!
6098 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6100 return This
->numBlocks
;
6103 /******************************************************************************
6104 * BlockChainStream_ReadAt
6106 * Reads a specified number of bytes from this chain at the specified offset.
6107 * bytesRead may be NULL.
6108 * Failure will be returned if the specified number of bytes has not been read.
6110 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6111 ULARGE_INTEGER offset
,
6116 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6117 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6118 ULONG bytesToReadInBuffer
;
6121 ULARGE_INTEGER stream_size
;
6123 BlockChainBlock
*cachedBlock
;
6125 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6128 * Find the first block in the stream that contains part of the buffer.
6130 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6134 stream_size
= BlockChainStream_GetSize(This
);
6135 if (stream_size
.QuadPart
> offset
.QuadPart
)
6136 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6141 * Start reading the buffer.
6143 bufferWalker
= buffer
;
6147 ULARGE_INTEGER ulOffset
;
6151 * Calculate how many bytes we can copy from this big block.
6153 bytesToReadInBuffer
=
6154 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6156 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6163 /* Not in cache, and we're going to read past the end of the block. */
6164 ulOffset
.u
.HighPart
= 0;
6165 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6168 StorageImpl_ReadAt(This
->parentStorage
,
6171 bytesToReadInBuffer
,
6176 if (!cachedBlock
->read
)
6178 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6179 return STG_E_READFAULT
;
6181 cachedBlock
->read
= 1;
6184 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6185 bytesReadAt
= bytesToReadInBuffer
;
6188 blockNoInSequence
++;
6189 bufferWalker
+= bytesReadAt
;
6190 size
-= bytesReadAt
;
6191 *bytesRead
+= bytesReadAt
;
6192 offsetInBlock
= 0; /* There is no offset on the next block */
6194 if (bytesToReadInBuffer
!= bytesReadAt
)
6201 /******************************************************************************
6202 * BlockChainStream_WriteAt
6204 * Writes the specified number of bytes to this chain at the specified offset.
6205 * Will fail if not all specified number of bytes have been written.
6207 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6208 ULARGE_INTEGER offset
,
6211 ULONG
* bytesWritten
)
6213 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6214 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6217 const BYTE
* bufferWalker
;
6219 BlockChainBlock
*cachedBlock
;
6222 bufferWalker
= buffer
;
6226 ULARGE_INTEGER ulOffset
;
6227 DWORD bytesWrittenAt
;
6230 * Calculate how many bytes we can copy to this big block.
6233 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6235 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6237 /* BlockChainStream_SetSize should have already been called to ensure we have
6238 * enough blocks in the chain to write into */
6241 ERR("not enough blocks in chain to write data\n");
6247 /* Not in cache, and we're going to write past the end of the block. */
6248 ulOffset
.u
.HighPart
= 0;
6249 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6252 StorageImpl_WriteAt(This
->parentStorage
,
6260 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6262 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6263 return STG_E_READFAULT
;
6266 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6267 bytesWrittenAt
= bytesToWrite
;
6268 cachedBlock
->read
= 1;
6269 cachedBlock
->dirty
= 1;
6272 blockNoInSequence
++;
6273 bufferWalker
+= bytesWrittenAt
;
6274 size
-= bytesWrittenAt
;
6275 *bytesWritten
+= bytesWrittenAt
;
6276 offsetInBlock
= 0; /* There is no offset on the next block */
6278 if (bytesWrittenAt
!= bytesToWrite
)
6282 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6285 /******************************************************************************
6286 * BlockChainStream_Shrink
6288 * Shrinks this chain in the big block depot.
6290 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6291 ULARGE_INTEGER newSize
)
6298 * Figure out how many blocks are needed to contain the new size
6300 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6302 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6308 * Go to the new end of chain
6310 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6312 /* Mark the new end of chain */
6313 StorageImpl_SetNextBlockInChain(
6314 This
->parentStorage
,
6316 BLOCK_END_OF_CHAIN
);
6318 This
->tailIndex
= blockIndex
;
6322 if (This
->headOfStreamPlaceHolder
!= 0)
6324 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6328 DirEntry chainEntry
;
6329 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6331 StorageImpl_ReadDirEntry(
6332 This
->parentStorage
,
6333 This
->ownerDirEntry
,
6336 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6338 StorageImpl_WriteDirEntry(
6339 This
->parentStorage
,
6340 This
->ownerDirEntry
,
6344 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6347 This
->numBlocks
= numBlocks
;
6350 * Mark the extra blocks as free
6352 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6354 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6355 StorageImpl_FreeBigBlock(This
->parentStorage
,
6356 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6357 if (last_run
->lastOffset
== last_run
->firstOffset
)
6358 This
->indexCacheLen
--;
6360 last_run
->lastOffset
--;
6364 * Reset the last accessed block cache.
6368 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6370 This
->cachedBlocks
[i
].index
= 0xffffffff;
6371 This
->cachedBlocks
[i
].dirty
= 0;
6378 /******************************************************************************
6379 * BlockChainStream_Enlarge
6381 * Grows this chain in the big block depot.
6383 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6384 ULARGE_INTEGER newSize
)
6386 ULONG blockIndex
, currentBlock
;
6388 ULONG oldNumBlocks
= 0;
6390 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6393 * Empty chain. Create the head.
6395 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6397 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6398 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6400 BLOCK_END_OF_CHAIN
);
6402 if (This
->headOfStreamPlaceHolder
!= 0)
6404 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6408 DirEntry chainEntry
;
6409 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6411 StorageImpl_ReadDirEntry(
6412 This
->parentStorage
,
6413 This
->ownerDirEntry
,
6416 chainEntry
.startingBlock
= blockIndex
;
6418 StorageImpl_WriteDirEntry(
6419 This
->parentStorage
,
6420 This
->ownerDirEntry
,
6424 This
->tailIndex
= blockIndex
;
6425 This
->numBlocks
= 1;
6429 * Figure out how many blocks are needed to contain this stream
6431 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6433 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6437 * Go to the current end of chain
6439 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6441 currentBlock
= blockIndex
;
6443 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6446 currentBlock
= blockIndex
;
6448 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6453 This
->tailIndex
= currentBlock
;
6456 currentBlock
= This
->tailIndex
;
6457 oldNumBlocks
= This
->numBlocks
;
6460 * Add new blocks to the chain
6462 if (oldNumBlocks
< newNumBlocks
)
6464 while (oldNumBlocks
< newNumBlocks
)
6466 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6468 StorageImpl_SetNextBlockInChain(
6469 This
->parentStorage
,
6473 StorageImpl_SetNextBlockInChain(
6474 This
->parentStorage
,
6476 BLOCK_END_OF_CHAIN
);
6478 currentBlock
= blockIndex
;
6482 This
->tailIndex
= blockIndex
;
6483 This
->numBlocks
= newNumBlocks
;
6486 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6492 /******************************************************************************
6493 * BlockChainStream_SetSize
6495 * Sets the size of this stream. The big block depot will be updated.
6496 * The file will grow if we grow the chain.
6498 * TODO: Free the actual blocks in the file when we shrink the chain.
6499 * Currently, the blocks are still in the file. So the file size
6500 * doesn't shrink even if we shrink streams.
6502 BOOL
BlockChainStream_SetSize(
6503 BlockChainStream
* This
,
6504 ULARGE_INTEGER newSize
)
6506 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6508 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6511 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6513 BlockChainStream_Shrink(This
, newSize
);
6517 BlockChainStream_Enlarge(This
, newSize
);
6523 /******************************************************************************
6524 * BlockChainStream_GetSize
6526 * Returns the size of this chain.
6527 * Will return the block count if this chain doesn't have a directory entry.
6529 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6531 DirEntry chainEntry
;
6533 if(This
->headOfStreamPlaceHolder
== NULL
)
6536 * This chain has a directory entry so use the size value from there.
6538 StorageImpl_ReadDirEntry(
6539 This
->parentStorage
,
6540 This
->ownerDirEntry
,
6543 return chainEntry
.size
;
6548 * this chain is a chain that does not have a directory entry, figure out the
6549 * size by making the product number of used blocks times the
6552 ULARGE_INTEGER result
;
6553 result
.u
.HighPart
= 0;
6556 BlockChainStream_GetCount(This
) *
6557 This
->parentStorage
->bigBlockSize
;
6563 /******************************************************************************
6564 ** SmallBlockChainStream implementation
6567 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6568 StorageImpl
* parentStorage
,
6569 ULONG
* headOfStreamPlaceHolder
,
6572 SmallBlockChainStream
* newStream
;
6574 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6576 newStream
->parentStorage
= parentStorage
;
6577 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6578 newStream
->ownerDirEntry
= dirEntry
;
6583 void SmallBlockChainStream_Destroy(
6584 SmallBlockChainStream
* This
)
6586 HeapFree(GetProcessHeap(), 0, This
);
6589 /******************************************************************************
6590 * SmallBlockChainStream_GetHeadOfChain
6592 * Returns the head of this chain of small blocks.
6594 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6595 SmallBlockChainStream
* This
)
6597 DirEntry chainEntry
;
6600 if (This
->headOfStreamPlaceHolder
!= NULL
)
6601 return *(This
->headOfStreamPlaceHolder
);
6603 if (This
->ownerDirEntry
)
6605 hr
= StorageImpl_ReadDirEntry(
6606 This
->parentStorage
,
6607 This
->ownerDirEntry
,
6612 return chainEntry
.startingBlock
;
6617 return BLOCK_END_OF_CHAIN
;
6620 /******************************************************************************
6621 * SmallBlockChainStream_GetNextBlockInChain
6623 * Returns the index of the next small block in this chain.
6626 * - BLOCK_END_OF_CHAIN: end of this chain
6627 * - BLOCK_UNUSED: small block 'blockIndex' is free
6629 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6630 SmallBlockChainStream
* This
,
6632 ULONG
* nextBlockInChain
)
6634 ULARGE_INTEGER offsetOfBlockInDepot
;
6639 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6641 offsetOfBlockInDepot
.u
.HighPart
= 0;
6642 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6645 * Read those bytes in the buffer from the small block file.
6647 res
= BlockChainStream_ReadAt(
6648 This
->parentStorage
->smallBlockDepotChain
,
6649 offsetOfBlockInDepot
,
6654 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6655 res
= STG_E_READFAULT
;
6659 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6666 /******************************************************************************
6667 * SmallBlockChainStream_SetNextBlockInChain
6669 * Writes the index of the next block of the specified block in the small
6671 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6672 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6674 static void SmallBlockChainStream_SetNextBlockInChain(
6675 SmallBlockChainStream
* This
,
6679 ULARGE_INTEGER offsetOfBlockInDepot
;
6683 offsetOfBlockInDepot
.u
.HighPart
= 0;
6684 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6686 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6689 * Read those bytes in the buffer from the small block file.
6691 BlockChainStream_WriteAt(
6692 This
->parentStorage
->smallBlockDepotChain
,
6693 offsetOfBlockInDepot
,
6699 /******************************************************************************
6700 * SmallBlockChainStream_FreeBlock
6702 * Flag small block 'blockIndex' as free in the small block depot.
6704 static void SmallBlockChainStream_FreeBlock(
6705 SmallBlockChainStream
* This
,
6708 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6711 /******************************************************************************
6712 * SmallBlockChainStream_GetNextFreeBlock
6714 * Returns the index of a free small block. The small block depot will be
6715 * enlarged if necessary. The small block chain will also be enlarged if
6718 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6719 SmallBlockChainStream
* This
)
6721 ULARGE_INTEGER offsetOfBlockInDepot
;
6724 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6725 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6727 ULONG smallBlocksPerBigBlock
;
6729 ULONG blocksRequired
;
6730 ULARGE_INTEGER old_size
, size_required
;
6732 offsetOfBlockInDepot
.u
.HighPart
= 0;
6735 * Scan the small block depot for a free block
6737 while (nextBlockIndex
!= BLOCK_UNUSED
)
6739 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6741 res
= BlockChainStream_ReadAt(
6742 This
->parentStorage
->smallBlockDepotChain
,
6743 offsetOfBlockInDepot
,
6749 * If we run out of space for the small block depot, enlarge it
6751 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6753 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6755 if (nextBlockIndex
!= BLOCK_UNUSED
)
6761 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6763 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6764 ULARGE_INTEGER newSize
, offset
;
6767 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6768 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6771 * Initialize all the small blocks to free
6773 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6774 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6775 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6776 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6778 StorageImpl_SaveFileHeader(This
->parentStorage
);
6782 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6784 smallBlocksPerBigBlock
=
6785 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6788 * Verify if we have to allocate big blocks to contain small blocks
6790 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6792 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6794 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6796 if (size_required
.QuadPart
> old_size
.QuadPart
)
6798 BlockChainStream_SetSize(
6799 This
->parentStorage
->smallBlockRootChain
,
6802 StorageImpl_ReadDirEntry(
6803 This
->parentStorage
,
6804 This
->parentStorage
->base
.storageDirEntry
,
6807 rootEntry
.size
= size_required
;
6809 StorageImpl_WriteDirEntry(
6810 This
->parentStorage
,
6811 This
->parentStorage
->base
.storageDirEntry
,
6818 /******************************************************************************
6819 * SmallBlockChainStream_ReadAt
6821 * Reads a specified number of bytes from this chain at the specified offset.
6822 * bytesRead may be NULL.
6823 * Failure will be returned if the specified number of bytes has not been read.
6825 HRESULT
SmallBlockChainStream_ReadAt(
6826 SmallBlockChainStream
* This
,
6827 ULARGE_INTEGER offset
,
6833 ULARGE_INTEGER offsetInBigBlockFile
;
6834 ULONG blockNoInSequence
=
6835 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6837 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6838 ULONG bytesToReadInBuffer
;
6840 ULONG bytesReadFromBigBlockFile
;
6842 ULARGE_INTEGER stream_size
;
6845 * This should never happen on a small block file.
6847 assert(offset
.u
.HighPart
==0);
6851 stream_size
= SmallBlockChainStream_GetSize(This
);
6852 if (stream_size
.QuadPart
> offset
.QuadPart
)
6853 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6858 * Find the first block in the stream that contains part of the buffer.
6860 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6862 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6864 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6867 blockNoInSequence
--;
6871 * Start reading the buffer.
6873 bufferWalker
= buffer
;
6875 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6878 * Calculate how many bytes we can copy from this small block.
6880 bytesToReadInBuffer
=
6881 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6884 * Calculate the offset of the small block in the small block file.
6886 offsetInBigBlockFile
.u
.HighPart
= 0;
6887 offsetInBigBlockFile
.u
.LowPart
=
6888 blockIndex
* This
->parentStorage
->smallBlockSize
;
6890 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6893 * Read those bytes in the buffer from the small block file.
6894 * The small block has already been identified so it shouldn't fail
6895 * unless the file is corrupt.
6897 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6898 offsetInBigBlockFile
,
6899 bytesToReadInBuffer
,
6901 &bytesReadFromBigBlockFile
);
6906 if (!bytesReadFromBigBlockFile
)
6907 return STG_E_DOCFILECORRUPT
;
6910 * Step to the next big block.
6912 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6914 return STG_E_DOCFILECORRUPT
;
6916 bufferWalker
+= bytesReadFromBigBlockFile
;
6917 size
-= bytesReadFromBigBlockFile
;
6918 *bytesRead
+= bytesReadFromBigBlockFile
;
6919 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6925 /******************************************************************************
6926 * SmallBlockChainStream_WriteAt
6928 * Writes the specified number of bytes to this chain at the specified offset.
6929 * Will fail if not all specified number of bytes have been written.
6931 HRESULT
SmallBlockChainStream_WriteAt(
6932 SmallBlockChainStream
* This
,
6933 ULARGE_INTEGER offset
,
6936 ULONG
* bytesWritten
)
6938 ULARGE_INTEGER offsetInBigBlockFile
;
6939 ULONG blockNoInSequence
=
6940 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6942 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6943 ULONG bytesToWriteInBuffer
;
6945 ULONG bytesWrittenToBigBlockFile
;
6946 const BYTE
* bufferWalker
;
6950 * This should never happen on a small block file.
6952 assert(offset
.u
.HighPart
==0);
6955 * Find the first block in the stream that contains part of the buffer.
6957 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6959 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6961 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6962 return STG_E_DOCFILECORRUPT
;
6963 blockNoInSequence
--;
6967 * Start writing the buffer.
6970 bufferWalker
= buffer
;
6971 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6974 * Calculate how many bytes we can copy to this small block.
6976 bytesToWriteInBuffer
=
6977 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6980 * Calculate the offset of the small block in the small block file.
6982 offsetInBigBlockFile
.u
.HighPart
= 0;
6983 offsetInBigBlockFile
.u
.LowPart
=
6984 blockIndex
* This
->parentStorage
->smallBlockSize
;
6986 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6989 * Write those bytes in the buffer to the small block file.
6991 res
= BlockChainStream_WriteAt(
6992 This
->parentStorage
->smallBlockRootChain
,
6993 offsetInBigBlockFile
,
6994 bytesToWriteInBuffer
,
6996 &bytesWrittenToBigBlockFile
);
7001 * Step to the next big block.
7003 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7006 bufferWalker
+= bytesWrittenToBigBlockFile
;
7007 size
-= bytesWrittenToBigBlockFile
;
7008 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7009 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7012 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7015 /******************************************************************************
7016 * SmallBlockChainStream_Shrink
7018 * Shrinks this chain in the small block depot.
7020 static BOOL
SmallBlockChainStream_Shrink(
7021 SmallBlockChainStream
* This
,
7022 ULARGE_INTEGER newSize
)
7024 ULONG blockIndex
, extraBlock
;
7028 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7030 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7033 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7036 * Go to the new end of chain
7038 while (count
< numBlocks
)
7040 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7047 * If the count is 0, we have a special case, the head of the chain was
7052 DirEntry chainEntry
;
7054 StorageImpl_ReadDirEntry(This
->parentStorage
,
7055 This
->ownerDirEntry
,
7058 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7060 StorageImpl_WriteDirEntry(This
->parentStorage
,
7061 This
->ownerDirEntry
,
7065 * We start freeing the chain at the head block.
7067 extraBlock
= blockIndex
;
7071 /* Get the next block before marking the new end */
7072 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7076 /* Mark the new end of chain */
7077 SmallBlockChainStream_SetNextBlockInChain(
7080 BLOCK_END_OF_CHAIN
);
7084 * Mark the extra blocks as free
7086 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7088 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7091 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7092 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7093 extraBlock
= blockIndex
;
7099 /******************************************************************************
7100 * SmallBlockChainStream_Enlarge
7102 * Grows this chain in the small block depot.
7104 static BOOL
SmallBlockChainStream_Enlarge(
7105 SmallBlockChainStream
* This
,
7106 ULARGE_INTEGER newSize
)
7108 ULONG blockIndex
, currentBlock
;
7110 ULONG oldNumBlocks
= 0;
7112 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7115 * Empty chain. Create the head.
7117 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7119 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7120 SmallBlockChainStream_SetNextBlockInChain(
7123 BLOCK_END_OF_CHAIN
);
7125 if (This
->headOfStreamPlaceHolder
!= NULL
)
7127 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7131 DirEntry chainEntry
;
7133 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7136 chainEntry
.startingBlock
= blockIndex
;
7138 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7143 currentBlock
= blockIndex
;
7146 * Figure out how many blocks are needed to contain this stream
7148 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7150 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7154 * Go to the current end of chain
7156 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7159 currentBlock
= blockIndex
;
7160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7165 * Add new blocks to the chain
7167 while (oldNumBlocks
< newNumBlocks
)
7169 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7170 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7172 SmallBlockChainStream_SetNextBlockInChain(
7175 BLOCK_END_OF_CHAIN
);
7177 currentBlock
= blockIndex
;
7184 /******************************************************************************
7185 * SmallBlockChainStream_SetSize
7187 * Sets the size of this stream.
7188 * The file will grow if we grow the chain.
7190 * TODO: Free the actual blocks in the file when we shrink the chain.
7191 * Currently, the blocks are still in the file. So the file size
7192 * doesn't shrink even if we shrink streams.
7194 BOOL
SmallBlockChainStream_SetSize(
7195 SmallBlockChainStream
* This
,
7196 ULARGE_INTEGER newSize
)
7198 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7200 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7203 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7205 SmallBlockChainStream_Shrink(This
, newSize
);
7209 SmallBlockChainStream_Enlarge(This
, newSize
);
7215 /******************************************************************************
7216 * SmallBlockChainStream_GetCount
7218 * Returns the number of small blocks that comprises this chain.
7219 * This is not the size of the stream as the last block may not be full!
7222 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7227 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7229 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7234 blockIndex
, &blockIndex
)))
7241 /******************************************************************************
7242 * SmallBlockChainStream_GetSize
7244 * Returns the size of this chain.
7246 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7248 DirEntry chainEntry
;
7250 if(This
->headOfStreamPlaceHolder
!= NULL
)
7252 ULARGE_INTEGER result
;
7253 result
.u
.HighPart
= 0;
7255 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7256 This
->parentStorage
->smallBlockSize
;
7261 StorageImpl_ReadDirEntry(
7262 This
->parentStorage
,
7263 This
->ownerDirEntry
,
7266 return chainEntry
.size
;
7269 static HRESULT
create_storagefile(
7273 STGOPTIONS
* pStgOptions
,
7277 StorageBaseImpl
* newStorage
= 0;
7278 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7279 HRESULT hr
= STG_E_INVALIDFLAG
;
7283 DWORD fileAttributes
;
7284 WCHAR tempFileName
[MAX_PATH
];
7287 return STG_E_INVALIDPOINTER
;
7289 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7290 return STG_E_INVALIDPARAMETER
;
7292 /* if no share mode given then DENY_NONE is the default */
7293 if (STGM_SHARE_MODE(grfMode
) == 0)
7294 grfMode
|= STGM_SHARE_DENY_NONE
;
7296 if ( FAILED( validateSTGM(grfMode
) ))
7299 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7300 switch(STGM_ACCESS_MODE(grfMode
))
7303 case STGM_READWRITE
:
7309 /* in direct mode, can only use SHARE_EXCLUSIVE */
7310 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7313 /* but in transacted mode, any share mode is valid */
7316 * Generate a unique name.
7320 WCHAR tempPath
[MAX_PATH
];
7321 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7323 memset(tempPath
, 0, sizeof(tempPath
));
7324 memset(tempFileName
, 0, sizeof(tempFileName
));
7326 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7329 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7330 pwcsName
= tempFileName
;
7333 hr
= STG_E_INSUFFICIENTMEMORY
;
7337 creationMode
= TRUNCATE_EXISTING
;
7341 creationMode
= GetCreationModeFromSTGM(grfMode
);
7345 * Interpret the STGM value grfMode
7347 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7348 accessMode
= GetAccessModeFromSTGM(grfMode
);
7350 if (grfMode
& STGM_DELETEONRELEASE
)
7351 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7353 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7355 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7359 FIXME("Storage share mode not implemented.\n");
7364 hFile
= CreateFileW(pwcsName
,
7372 if (hFile
== INVALID_HANDLE_VALUE
)
7374 if(GetLastError() == ERROR_FILE_EXISTS
)
7375 hr
= STG_E_FILEALREADYEXISTS
;
7382 * Allocate and initialize the new IStorage32object.
7384 hr
= Storage_Construct(
7391 pStgOptions
->ulSectorSize
,
7399 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
7401 IStorage_Release((IStorage
*)newStorage
);
7404 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7409 /******************************************************************************
7410 * StgCreateDocfile [OLE32.@]
7411 * Creates a new compound file storage object
7414 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7415 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7416 * reserved [ ?] unused?, usually 0
7417 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7420 * S_OK if the file was successfully created
7421 * some STG_E_ value if error
7423 * if pwcsName is NULL, create file with new unique name
7424 * the function can returns
7425 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7428 HRESULT WINAPI
StgCreateDocfile(
7432 IStorage
**ppstgOpen
)
7434 STGOPTIONS stgoptions
= {1, 0, 512};
7436 TRACE("(%s, %x, %d, %p)\n",
7437 debugstr_w(pwcsName
), grfMode
,
7438 reserved
, ppstgOpen
);
7441 return STG_E_INVALIDPOINTER
;
7443 return STG_E_INVALIDPARAMETER
;
7445 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7448 /******************************************************************************
7449 * StgCreateStorageEx [OLE32.@]
7451 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7453 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7454 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7456 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7458 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7459 return STG_E_INVALIDPARAMETER
;
7462 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7464 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7465 return STG_E_INVALIDPARAMETER
;
7468 if (stgfmt
== STGFMT_FILE
)
7470 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7471 return STG_E_INVALIDPARAMETER
;
7474 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7476 STGOPTIONS defaultOptions
= {1, 0, 512};
7478 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
7479 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7483 ERR("Invalid stgfmt argument\n");
7484 return STG_E_INVALIDPARAMETER
;
7487 /******************************************************************************
7488 * StgCreatePropSetStg [OLE32.@]
7490 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7491 IPropertySetStorage
**ppPropSetStg
)
7495 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
7497 hr
= STG_E_INVALIDPARAMETER
;
7499 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
7500 (void**)ppPropSetStg
);
7504 /******************************************************************************
7505 * StgOpenStorageEx [OLE32.@]
7507 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7509 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7510 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7512 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7514 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7515 return STG_E_INVALIDPARAMETER
;
7521 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7522 return STG_E_INVALIDPARAMETER
;
7524 case STGFMT_STORAGE
:
7527 case STGFMT_DOCFILE
:
7528 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7530 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7531 return STG_E_INVALIDPARAMETER
;
7533 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7537 WARN("STGFMT_ANY assuming storage\n");
7541 return STG_E_INVALIDPARAMETER
;
7544 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7548 /******************************************************************************
7549 * StgOpenStorage [OLE32.@]
7551 HRESULT WINAPI
StgOpenStorage(
7552 const OLECHAR
*pwcsName
,
7553 IStorage
*pstgPriority
,
7557 IStorage
**ppstgOpen
)
7559 StorageBaseImpl
* newStorage
= 0;
7565 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7566 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7567 snbExclude
, reserved
, ppstgOpen
);
7571 hr
= STG_E_INVALIDNAME
;
7577 hr
= STG_E_INVALIDPOINTER
;
7583 hr
= STG_E_INVALIDPARAMETER
;
7587 if (grfMode
& STGM_PRIORITY
)
7589 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7590 return STG_E_INVALIDFLAG
;
7591 if (grfMode
& STGM_DELETEONRELEASE
)
7592 return STG_E_INVALIDFUNCTION
;
7593 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7594 return STG_E_INVALIDFLAG
;
7595 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7596 grfMode
|= STGM_SHARE_DENY_NONE
;
7598 /* STGM_PRIORITY stops other IStorage objects on the same file from
7599 * committing until the STGM_PRIORITY IStorage is closed. it also
7600 * stops non-transacted mode StgOpenStorage calls with write access from
7601 * succeeding. obviously, both of these cannot be achieved through just
7602 * file share flags */
7603 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7607 * Validate the sharing mode
7609 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7610 switch(STGM_SHARE_MODE(grfMode
))
7612 case STGM_SHARE_EXCLUSIVE
:
7613 case STGM_SHARE_DENY_WRITE
:
7616 hr
= STG_E_INVALIDFLAG
;
7620 if ( FAILED( validateSTGM(grfMode
) ) ||
7621 (grfMode
&STGM_CREATE
))
7623 hr
= STG_E_INVALIDFLAG
;
7627 /* shared reading requires transacted mode */
7628 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7629 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7630 !(grfMode
&STGM_TRANSACTED
) )
7632 hr
= STG_E_INVALIDFLAG
;
7637 * Interpret the STGM value grfMode
7639 shareMode
= GetShareModeFromSTGM(grfMode
);
7640 accessMode
= GetAccessModeFromSTGM(grfMode
);
7644 hFile
= CreateFileW( pwcsName
,
7649 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7652 if (hFile
==INVALID_HANDLE_VALUE
)
7654 DWORD last_error
= GetLastError();
7660 case ERROR_FILE_NOT_FOUND
:
7661 hr
= STG_E_FILENOTFOUND
;
7664 case ERROR_PATH_NOT_FOUND
:
7665 hr
= STG_E_PATHNOTFOUND
;
7668 case ERROR_ACCESS_DENIED
:
7669 case ERROR_WRITE_PROTECT
:
7670 hr
= STG_E_ACCESSDENIED
;
7673 case ERROR_SHARING_VIOLATION
:
7674 hr
= STG_E_SHAREVIOLATION
;
7685 * Refuse to open the file if it's too small to be a structured storage file
7686 * FIXME: verify the file when reading instead of here
7688 if (GetFileSize(hFile
, NULL
) < 0x100)
7691 hr
= STG_E_FILEALREADYEXISTS
;
7696 * Allocate and initialize the new IStorage32object.
7698 hr
= Storage_Construct(
7711 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7713 if(hr
== STG_E_INVALIDHEADER
)
7714 hr
= STG_E_FILEALREADYEXISTS
;
7719 * Get an "out" pointer for the caller.
7721 *ppstgOpen
= (IStorage
*)newStorage
;
7724 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7728 /******************************************************************************
7729 * StgCreateDocfileOnILockBytes [OLE32.@]
7731 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7735 IStorage
** ppstgOpen
)
7737 StorageBaseImpl
* newStorage
= 0;
7740 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7741 return STG_E_INVALIDPOINTER
;
7744 * Allocate and initialize the new IStorage object.
7746 hr
= Storage_Construct(
7762 * Get an "out" pointer for the caller.
7764 *ppstgOpen
= (IStorage
*)newStorage
;
7769 /******************************************************************************
7770 * StgOpenStorageOnILockBytes [OLE32.@]
7772 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7774 IStorage
*pstgPriority
,
7778 IStorage
**ppstgOpen
)
7780 StorageBaseImpl
* newStorage
= 0;
7783 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7784 return STG_E_INVALIDPOINTER
;
7786 if ( FAILED( validateSTGM(grfMode
) ))
7787 return STG_E_INVALIDFLAG
;
7792 * Allocate and initialize the new IStorage object.
7794 hr
= Storage_Construct(
7810 * Get an "out" pointer for the caller.
7812 *ppstgOpen
= (IStorage
*)newStorage
;
7817 /******************************************************************************
7818 * StgSetTimes [ole32.@]
7819 * StgSetTimes [OLE32.@]
7823 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7824 FILETIME
const *patime
, FILETIME
const *pmtime
)
7826 IStorage
*stg
= NULL
;
7829 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7831 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7835 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7836 IStorage_Release(stg
);
7842 /******************************************************************************
7843 * StgIsStorageILockBytes [OLE32.@]
7845 * Determines if the ILockBytes contains a storage object.
7847 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7849 BYTE sig
[sizeof(STORAGE_magic
)];
7850 ULARGE_INTEGER offset
;
7853 offset
.u
.HighPart
= 0;
7854 offset
.u
.LowPart
= 0;
7856 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
7858 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
7864 /******************************************************************************
7865 * WriteClassStg [OLE32.@]
7867 * This method will store the specified CLSID in the specified storage object
7869 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7874 return E_INVALIDARG
;
7877 return STG_E_INVALIDPOINTER
;
7879 hRes
= IStorage_SetClass(pStg
, rclsid
);
7884 /***********************************************************************
7885 * ReadClassStg (OLE32.@)
7887 * This method reads the CLSID previously written to a storage object with
7888 * the WriteClassStg.
7891 * pstg [I] IStorage pointer
7892 * pclsid [O] Pointer to where the CLSID is written
7896 * Failure: HRESULT code.
7898 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7903 TRACE("(%p, %p)\n", pstg
, pclsid
);
7905 if(!pstg
|| !pclsid
)
7906 return E_INVALIDARG
;
7909 * read a STATSTG structure (contains the clsid) from the storage
7911 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7914 *pclsid
=pstatstg
.clsid
;
7919 /***********************************************************************
7920 * OleLoadFromStream (OLE32.@)
7922 * This function loads an object from stream
7924 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7928 LPPERSISTSTREAM xstm
;
7930 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7932 res
=ReadClassStm(pStm
,&clsid
);
7935 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7938 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7940 IUnknown_Release((IUnknown
*)*ppvObj
);
7943 res
=IPersistStream_Load(xstm
,pStm
);
7944 IPersistStream_Release(xstm
);
7945 /* FIXME: all refcounts ok at this point? I think they should be:
7948 * xstm : 0 (released)
7953 /***********************************************************************
7954 * OleSaveToStream (OLE32.@)
7956 * This function saves an object with the IPersistStream interface on it
7957 * to the specified stream.
7959 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7965 TRACE("(%p,%p)\n",pPStm
,pStm
);
7967 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7969 if (SUCCEEDED(res
)){
7971 res
=WriteClassStm(pStm
,&clsid
);
7975 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7978 TRACE("Finished Save\n");
7982 /****************************************************************************
7983 * This method validate a STGM parameter that can contain the values below
7985 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7986 * The stgm values contained in 0xffff0000 are bitmasks.
7988 * STGM_DIRECT 0x00000000
7989 * STGM_TRANSACTED 0x00010000
7990 * STGM_SIMPLE 0x08000000
7992 * STGM_READ 0x00000000
7993 * STGM_WRITE 0x00000001
7994 * STGM_READWRITE 0x00000002
7996 * STGM_SHARE_DENY_NONE 0x00000040
7997 * STGM_SHARE_DENY_READ 0x00000030
7998 * STGM_SHARE_DENY_WRITE 0x00000020
7999 * STGM_SHARE_EXCLUSIVE 0x00000010
8001 * STGM_PRIORITY 0x00040000
8002 * STGM_DELETEONRELEASE 0x04000000
8004 * STGM_CREATE 0x00001000
8005 * STGM_CONVERT 0x00020000
8006 * STGM_FAILIFTHERE 0x00000000
8008 * STGM_NOSCRATCH 0x00100000
8009 * STGM_NOSNAPSHOT 0x00200000
8011 static HRESULT
validateSTGM(DWORD stgm
)
8013 DWORD access
= STGM_ACCESS_MODE(stgm
);
8014 DWORD share
= STGM_SHARE_MODE(stgm
);
8015 DWORD create
= STGM_CREATE_MODE(stgm
);
8017 if (stgm
&~STGM_KNOWN_FLAGS
)
8019 ERR("unknown flags %08x\n", stgm
);
8027 case STGM_READWRITE
:
8035 case STGM_SHARE_DENY_NONE
:
8036 case STGM_SHARE_DENY_READ
:
8037 case STGM_SHARE_DENY_WRITE
:
8038 case STGM_SHARE_EXCLUSIVE
:
8047 case STGM_FAILIFTHERE
:
8054 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8056 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8060 * STGM_CREATE | STGM_CONVERT
8061 * if both are false, STGM_FAILIFTHERE is set to TRUE
8063 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8067 * STGM_NOSCRATCH requires STGM_TRANSACTED
8069 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8073 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8074 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8076 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8077 (!(stgm
& STGM_TRANSACTED
) ||
8078 share
== STGM_SHARE_EXCLUSIVE
||
8079 share
== STGM_SHARE_DENY_WRITE
) )
8085 /****************************************************************************
8086 * GetShareModeFromSTGM
8088 * This method will return a share mode flag from a STGM value.
8089 * The STGM value is assumed valid.
8091 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8093 switch (STGM_SHARE_MODE(stgm
))
8095 case STGM_SHARE_DENY_NONE
:
8096 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8097 case STGM_SHARE_DENY_READ
:
8098 return FILE_SHARE_WRITE
;
8099 case STGM_SHARE_DENY_WRITE
:
8100 return FILE_SHARE_READ
;
8101 case STGM_SHARE_EXCLUSIVE
:
8104 ERR("Invalid share mode!\n");
8109 /****************************************************************************
8110 * GetAccessModeFromSTGM
8112 * This method will return an access mode flag from a STGM value.
8113 * The STGM value is assumed valid.
8115 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8117 switch (STGM_ACCESS_MODE(stgm
))
8120 return GENERIC_READ
;
8122 case STGM_READWRITE
:
8123 return GENERIC_READ
| GENERIC_WRITE
;
8125 ERR("Invalid access mode!\n");
8130 /****************************************************************************
8131 * GetCreationModeFromSTGM
8133 * This method will return a creation mode flag from a STGM value.
8134 * The STGM value is assumed valid.
8136 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8138 switch(STGM_CREATE_MODE(stgm
))
8141 return CREATE_ALWAYS
;
8143 FIXME("STGM_CONVERT not implemented!\n");
8145 case STGM_FAILIFTHERE
:
8148 ERR("Invalid create mode!\n");
8154 /*************************************************************************
8155 * OLECONVERT_LoadOLE10 [Internal]
8157 * Loads the OLE10 STREAM to memory
8160 * pOleStream [I] The OLESTREAM
8161 * pData [I] Data Structure for the OLESTREAM Data
8165 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8166 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8169 * This function is used by OleConvertOLESTREAMToIStorage only.
8171 * Memory allocated for pData must be freed by the caller
8173 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8176 HRESULT hRes
= S_OK
;
8180 pData
->pData
= NULL
;
8181 pData
->pstrOleObjFileName
= NULL
;
8183 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8186 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8187 if(dwSize
!= sizeof(pData
->dwOleID
))
8189 hRes
= CONVERT10_E_OLESTREAM_GET
;
8191 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8193 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8204 /* Get the TypeID... more info needed for this field */
8205 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8206 if(dwSize
!= sizeof(pData
->dwTypeID
))
8208 hRes
= CONVERT10_E_OLESTREAM_GET
;
8213 if(pData
->dwTypeID
!= 0)
8215 /* Get the length of the OleTypeName */
8216 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8217 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8219 hRes
= CONVERT10_E_OLESTREAM_GET
;
8224 if(pData
->dwOleTypeNameLength
> 0)
8226 /* Get the OleTypeName */
8227 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8228 if(dwSize
!= pData
->dwOleTypeNameLength
)
8230 hRes
= CONVERT10_E_OLESTREAM_GET
;
8236 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8237 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8239 hRes
= CONVERT10_E_OLESTREAM_GET
;
8243 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8244 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8245 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8246 if(pData
->pstrOleObjFileName
)
8248 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8249 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8251 hRes
= CONVERT10_E_OLESTREAM_GET
;
8255 hRes
= CONVERT10_E_OLESTREAM_GET
;
8260 /* Get the Width of the Metafile */
8261 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8262 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8264 hRes
= CONVERT10_E_OLESTREAM_GET
;
8268 /* Get the Height of the Metafile */
8269 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8270 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8272 hRes
= CONVERT10_E_OLESTREAM_GET
;
8278 /* Get the Length of the Data */
8279 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8280 if(dwSize
!= sizeof(pData
->dwDataLength
))
8282 hRes
= CONVERT10_E_OLESTREAM_GET
;
8286 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8288 if(!bStrem1
) /* if it is a second OLE stream data */
8290 pData
->dwDataLength
-= 8;
8291 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8292 if(dwSize
!= sizeof(pData
->strUnknown
))
8294 hRes
= CONVERT10_E_OLESTREAM_GET
;
8300 if(pData
->dwDataLength
> 0)
8302 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8304 /* Get Data (ex. IStorage, Metafile, or BMP) */
8307 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8308 if(dwSize
!= pData
->dwDataLength
)
8310 hRes
= CONVERT10_E_OLESTREAM_GET
;
8315 hRes
= CONVERT10_E_OLESTREAM_GET
;
8324 /*************************************************************************
8325 * OLECONVERT_SaveOLE10 [Internal]
8327 * Saves the OLE10 STREAM From memory
8330 * pData [I] Data Structure for the OLESTREAM Data
8331 * pOleStream [I] The OLESTREAM to save
8335 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8338 * This function is used by OleConvertIStorageToOLESTREAM only.
8341 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8344 HRESULT hRes
= S_OK
;
8348 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8349 if(dwSize
!= sizeof(pData
->dwOleID
))
8351 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8356 /* Set the TypeID */
8357 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8358 if(dwSize
!= sizeof(pData
->dwTypeID
))
8360 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8364 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8366 /* Set the Length of the OleTypeName */
8367 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8368 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8370 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8375 if(pData
->dwOleTypeNameLength
> 0)
8377 /* Set the OleTypeName */
8378 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8379 if(dwSize
!= pData
->dwOleTypeNameLength
)
8381 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8388 /* Set the width of the Metafile */
8389 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8390 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8392 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8398 /* Set the height of the Metafile */
8399 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8400 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8402 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8408 /* Set the length of the Data */
8409 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8410 if(dwSize
!= sizeof(pData
->dwDataLength
))
8412 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8418 if(pData
->dwDataLength
> 0)
8420 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8421 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8422 if(dwSize
!= pData
->dwDataLength
)
8424 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8432 /*************************************************************************
8433 * OLECONVERT_GetOLE20FromOLE10[Internal]
8435 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8436 * opens it, and copies the content to the dest IStorage for
8437 * OleConvertOLESTREAMToIStorage
8441 * pDestStorage [I] The IStorage to copy the data to
8442 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8443 * nBufferLength [I] The size of the buffer
8452 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8456 IStorage
*pTempStorage
;
8457 DWORD dwNumOfBytesWritten
;
8458 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8459 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8461 /* Create a temp File */
8462 GetTempPathW(MAX_PATH
, wstrTempDir
);
8463 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8464 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8466 if(hFile
!= INVALID_HANDLE_VALUE
)
8468 /* Write IStorage Data to File */
8469 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8472 /* Open and copy temp storage to the Dest Storage */
8473 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8476 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8477 IStorage_Release(pTempStorage
);
8479 DeleteFileW(wstrTempFile
);
8484 /*************************************************************************
8485 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8487 * Saves the OLE10 STREAM From memory
8490 * pStorage [I] The Src IStorage to copy
8491 * pData [I] The Dest Memory to write to.
8494 * The size in bytes allocated for pData
8497 * Memory allocated for pData must be freed by the caller
8499 * Used by OleConvertIStorageToOLESTREAM only.
8502 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8506 DWORD nDataLength
= 0;
8507 IStorage
*pTempStorage
;
8508 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8509 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8513 /* Create temp Storage */
8514 GetTempPathW(MAX_PATH
, wstrTempDir
);
8515 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8516 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8520 /* Copy Src Storage to the Temp Storage */
8521 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8522 IStorage_Release(pTempStorage
);
8524 /* Open Temp Storage as a file and copy to memory */
8525 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8526 if(hFile
!= INVALID_HANDLE_VALUE
)
8528 nDataLength
= GetFileSize(hFile
, NULL
);
8529 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8530 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8533 DeleteFileW(wstrTempFile
);
8538 /*************************************************************************
8539 * OLECONVERT_CreateOleStream [Internal]
8541 * Creates the "\001OLE" stream in the IStorage if necessary.
8544 * pStorage [I] Dest storage to create the stream in
8550 * This function is used by OleConvertOLESTREAMToIStorage only.
8552 * This stream is still unknown, MS Word seems to have extra data
8553 * but since the data is stored in the OLESTREAM there should be
8554 * no need to recreate the stream. If the stream is manually
8555 * deleted it will create it with this default data.
8558 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8562 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8563 BYTE pOleStreamHeader
[] =
8565 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8567 0x00, 0x00, 0x00, 0x00
8570 /* Create stream if not present */
8571 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8572 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8576 /* Write default Data */
8577 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8578 IStream_Release(pStream
);
8582 /* write a string to a stream, preceded by its length */
8583 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8590 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8591 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8596 str
= CoTaskMemAlloc( len
);
8597 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8598 r
= IStream_Write( stm
, str
, len
, NULL
);
8599 CoTaskMemFree( str
);
8603 /* read a string preceded by its length from a stream */
8604 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8607 DWORD len
, count
= 0;
8611 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8614 if( count
!= sizeof(len
) )
8615 return E_OUTOFMEMORY
;
8617 TRACE("%d bytes\n",len
);
8619 str
= CoTaskMemAlloc( len
);
8621 return E_OUTOFMEMORY
;
8623 r
= IStream_Read( stm
, str
, len
, &count
);
8628 CoTaskMemFree( str
);
8629 return E_OUTOFMEMORY
;
8632 TRACE("Read string %s\n",debugstr_an(str
,len
));
8634 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8635 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8637 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8638 CoTaskMemFree( str
);
8646 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8647 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8651 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8653 static const BYTE unknown1
[12] =
8654 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8655 0xFF, 0xFF, 0xFF, 0xFF};
8656 static const BYTE unknown2
[16] =
8657 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8658 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8660 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8661 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8662 debugstr_w(szProgIDName
));
8664 /* Create a CompObj stream */
8665 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8666 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8670 /* Write CompObj Structure to stream */
8671 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8673 if( SUCCEEDED( r
) )
8674 r
= WriteClassStm( pstm
, clsid
);
8676 if( SUCCEEDED( r
) )
8677 r
= STREAM_WriteString( pstm
, lpszUserType
);
8678 if( SUCCEEDED( r
) )
8679 r
= STREAM_WriteString( pstm
, szClipName
);
8680 if( SUCCEEDED( r
) )
8681 r
= STREAM_WriteString( pstm
, szProgIDName
);
8682 if( SUCCEEDED( r
) )
8683 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8685 IStream_Release( pstm
);
8690 /***********************************************************************
8691 * WriteFmtUserTypeStg (OLE32.@)
8693 HRESULT WINAPI
WriteFmtUserTypeStg(
8694 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8697 WCHAR szwClipName
[0x40];
8698 CLSID clsid
= CLSID_NULL
;
8699 LPWSTR wstrProgID
= NULL
;
8702 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8704 /* get the clipboard format name */
8705 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8708 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8710 /* FIXME: There's room to save a CLSID and its ProgID, but
8711 the CLSID is not looked up in the registry and in all the
8712 tests I wrote it was CLSID_NULL. Where does it come from?
8715 /* get the real program ID. This may fail, but that's fine */
8716 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8718 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8720 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8721 lpszUserType
, szwClipName
, wstrProgID
);
8723 CoTaskMemFree(wstrProgID
);
8729 /******************************************************************************
8730 * ReadFmtUserTypeStg [OLE32.@]
8732 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8736 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8737 unsigned char unknown1
[12];
8738 unsigned char unknown2
[16];
8740 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8743 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8745 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8746 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8749 WARN("Failed to open stream r = %08x\n", r
);
8753 /* read the various parts of the structure */
8754 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8755 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8757 r
= ReadClassStm( stm
, &clsid
);
8761 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8765 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8769 r
= STREAM_ReadString( stm
, &szProgIDName
);
8773 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8774 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8777 /* ok, success... now we just need to store what we found */
8779 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8780 CoTaskMemFree( szOleTypeName
);
8782 if( lplpszUserType
)
8783 *lplpszUserType
= szCLSIDName
;
8784 CoTaskMemFree( szProgIDName
);
8787 IStream_Release( stm
);
8793 /*************************************************************************
8794 * OLECONVERT_CreateCompObjStream [Internal]
8796 * Creates a "\001CompObj" is the destination IStorage if necessary.
8799 * pStorage [I] The dest IStorage to create the CompObj Stream
8801 * strOleTypeName [I] The ProgID
8805 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8808 * This function is used by OleConvertOLESTREAMToIStorage only.
8810 * The stream data is stored in the OLESTREAM and there should be
8811 * no need to recreate the stream. If the stream is manually
8812 * deleted it will attempt to create it by querying the registry.
8816 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8819 HRESULT hStorageRes
, hRes
= S_OK
;
8820 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8821 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8822 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8824 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8825 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8827 /* Initialize the CompObj structure */
8828 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8829 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8830 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8833 /* Create a CompObj stream if it doesn't exist */
8834 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8835 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8836 if(hStorageRes
== S_OK
)
8838 /* copy the OleTypeName to the compobj struct */
8839 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8840 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8842 /* copy the OleTypeName to the compobj struct */
8843 /* Note: in the test made, these were Identical */
8844 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8845 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8848 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8849 bufferW
, OLESTREAM_MAX_STR_LEN
);
8850 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8856 /* Get the CLSID Default Name from the Registry */
8857 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8858 if(hErr
== ERROR_SUCCESS
)
8860 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8861 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8862 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8863 if(hErr
== ERROR_SUCCESS
)
8865 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8871 /* Write CompObj Structure to stream */
8872 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8874 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8876 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8877 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8879 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8881 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8882 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8884 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8886 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8887 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8889 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8891 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8892 IStream_Release(pStream
);
8898 /*************************************************************************
8899 * OLECONVERT_CreateOlePresStream[Internal]
8901 * Creates the "\002OlePres000" Stream with the Metafile data
8904 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8905 * dwExtentX [I] Width of the Metafile
8906 * dwExtentY [I] Height of the Metafile
8907 * pData [I] Metafile data
8908 * dwDataLength [I] Size of the Metafile data
8912 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8915 * This function is used by OleConvertOLESTREAMToIStorage only.
8918 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8922 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8923 BYTE pOlePresStreamHeader
[] =
8925 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8926 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8927 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8928 0x00, 0x00, 0x00, 0x00
8931 BYTE pOlePresStreamHeaderEmpty
[] =
8933 0x00, 0x00, 0x00, 0x00,
8934 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8935 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8936 0x00, 0x00, 0x00, 0x00
8939 /* Create the OlePres000 Stream */
8940 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8941 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8946 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8948 memset(&OlePres
, 0, sizeof(OlePres
));
8949 /* Do we have any metafile data to save */
8950 if(dwDataLength
> 0)
8952 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8953 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8957 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8958 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8960 /* Set width and height of the metafile */
8961 OlePres
.dwExtentX
= dwExtentX
;
8962 OlePres
.dwExtentY
= -dwExtentY
;
8964 /* Set Data and Length */
8965 if(dwDataLength
> sizeof(METAFILEPICT16
))
8967 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8968 OlePres
.pData
= &(pData
[8]);
8970 /* Save OlePres000 Data to Stream */
8971 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8972 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8973 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8974 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8975 if(OlePres
.dwSize
> 0)
8977 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8979 IStream_Release(pStream
);
8983 /*************************************************************************
8984 * OLECONVERT_CreateOle10NativeStream [Internal]
8986 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8989 * pStorage [I] Dest storage to create the stream in
8990 * pData [I] Ole10 Native Data (ex. bmp)
8991 * dwDataLength [I] Size of the Ole10 Native Data
8997 * This function is used by OleConvertOLESTREAMToIStorage only.
8999 * Might need to verify the data and return appropriate error message
9002 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9006 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9008 /* Create the Ole10Native Stream */
9009 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9010 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9014 /* Write info to stream */
9015 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9016 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9017 IStream_Release(pStream
);
9022 /*************************************************************************
9023 * OLECONVERT_GetOLE10ProgID [Internal]
9025 * Finds the ProgID (or OleTypeID) from the IStorage
9028 * pStorage [I] The Src IStorage to get the ProgID
9029 * strProgID [I] the ProgID string to get
9030 * dwSize [I] the size of the string
9034 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9037 * This function is used by OleConvertIStorageToOLESTREAM only.
9041 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9045 LARGE_INTEGER iSeekPos
;
9046 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9047 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9049 /* Open the CompObj Stream */
9050 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9051 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9055 /*Get the OleType from the CompObj Stream */
9056 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9057 iSeekPos
.u
.HighPart
= 0;
9059 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9060 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9061 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9062 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9063 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9064 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9065 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9067 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9070 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9072 IStream_Release(pStream
);
9077 LPOLESTR wstrProgID
;
9079 /* Get the OleType from the registry */
9080 REFCLSID clsid
= &(stat
.clsid
);
9081 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9082 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9085 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9092 /*************************************************************************
9093 * OLECONVERT_GetOle10PresData [Internal]
9095 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9098 * pStorage [I] Src IStroage
9099 * pOleStream [I] Dest OleStream Mem Struct
9105 * This function is used by OleConvertIStorageToOLESTREAM only.
9107 * Memory allocated for pData must be freed by the caller
9111 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9116 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9118 /* Initialize Default data for OLESTREAM */
9119 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9120 pOleStreamData
[0].dwTypeID
= 2;
9121 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9122 pOleStreamData
[1].dwTypeID
= 0;
9123 pOleStreamData
[0].dwMetaFileWidth
= 0;
9124 pOleStreamData
[0].dwMetaFileHeight
= 0;
9125 pOleStreamData
[0].pData
= NULL
;
9126 pOleStreamData
[1].pData
= NULL
;
9128 /* Open Ole10Native Stream */
9129 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9130 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9134 /* Read Size and Data */
9135 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9136 if(pOleStreamData
->dwDataLength
> 0)
9138 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9139 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9141 IStream_Release(pStream
);
9147 /*************************************************************************
9148 * OLECONVERT_GetOle20PresData[Internal]
9150 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9153 * pStorage [I] Src IStroage
9154 * pOleStreamData [I] Dest OleStream Mem Struct
9160 * This function is used by OleConvertIStorageToOLESTREAM only.
9162 * Memory allocated for pData must be freed by the caller
9164 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9168 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9169 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9171 /* Initialize Default data for OLESTREAM */
9172 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9173 pOleStreamData
[0].dwTypeID
= 2;
9174 pOleStreamData
[0].dwMetaFileWidth
= 0;
9175 pOleStreamData
[0].dwMetaFileHeight
= 0;
9176 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9177 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9178 pOleStreamData
[1].dwTypeID
= 0;
9179 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9180 pOleStreamData
[1].strOleTypeName
[0] = 0;
9181 pOleStreamData
[1].dwMetaFileWidth
= 0;
9182 pOleStreamData
[1].dwMetaFileHeight
= 0;
9183 pOleStreamData
[1].pData
= NULL
;
9184 pOleStreamData
[1].dwDataLength
= 0;
9187 /* Open OlePress000 stream */
9188 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9189 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9192 LARGE_INTEGER iSeekPos
;
9193 METAFILEPICT16 MetaFilePict
;
9194 static const char strMetafilePictName
[] = "METAFILEPICT";
9196 /* Set the TypeID for a Metafile */
9197 pOleStreamData
[1].dwTypeID
= 5;
9199 /* Set the OleTypeName to Metafile */
9200 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9201 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9203 iSeekPos
.u
.HighPart
= 0;
9204 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9206 /* Get Presentation Data */
9207 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9208 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9209 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9210 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9212 /*Set width and Height */
9213 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9214 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9215 if(olePress
.dwSize
> 0)
9218 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9220 /* Set MetaFilePict struct */
9221 MetaFilePict
.mm
= 8;
9222 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9223 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9224 MetaFilePict
.hMF
= 0;
9226 /* Get Metafile Data */
9227 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9228 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9229 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9231 IStream_Release(pStream
);
9235 /*************************************************************************
9236 * OleConvertOLESTREAMToIStorage [OLE32.@]
9241 * DVTARGETDEVICE parameter is not handled
9242 * Still unsure of some mem fields for OLE 10 Stream
9243 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9244 * and "\001OLE" streams
9247 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9248 LPOLESTREAM pOleStream
,
9250 const DVTARGETDEVICE
* ptd
)
9254 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9256 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9258 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9262 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9265 if(pstg
== NULL
|| pOleStream
== NULL
)
9267 hRes
= E_INVALIDARG
;
9272 /* Load the OLESTREAM to Memory */
9273 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9278 /* Load the OLESTREAM to Memory (part 2)*/
9279 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9285 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9287 /* Do we have the IStorage Data in the OLESTREAM */
9288 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9290 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9291 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9295 /* It must be an original OLE 1.0 source */
9296 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9301 /* It must be an original OLE 1.0 source */
9302 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9305 /* Create CompObj Stream if necessary */
9306 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9309 /*Create the Ole Stream if necessary */
9310 OLECONVERT_CreateOleStream(pstg
);
9315 /* Free allocated memory */
9316 for(i
=0; i
< 2; i
++)
9318 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9319 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9320 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9325 /*************************************************************************
9326 * OleConvertIStorageToOLESTREAM [OLE32.@]
9333 * Still unsure of some mem fields for OLE 10 Stream
9334 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9335 * and "\001OLE" streams.
9338 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9340 LPOLESTREAM pOleStream
)
9343 HRESULT hRes
= S_OK
;
9345 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9346 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9348 TRACE("%p %p\n", pstg
, pOleStream
);
9350 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9352 if(pstg
== NULL
|| pOleStream
== NULL
)
9354 hRes
= E_INVALIDARG
;
9358 /* Get the ProgID */
9359 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9360 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9364 /* Was it originally Ole10 */
9365 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9368 IStream_Release(pStream
);
9369 /* Get Presentation Data for Ole10Native */
9370 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9374 /* Get Presentation Data (OLE20) */
9375 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9378 /* Save OLESTREAM */
9379 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9382 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9387 /* Free allocated memory */
9388 for(i
=0; i
< 2; i
++)
9390 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9396 /***********************************************************************
9397 * GetConvertStg (OLE32.@)
9399 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
9400 FIXME("unimplemented stub!\n");
9404 /******************************************************************************
9405 * StgIsStorageFile [OLE32.@]
9406 * Verify if the file contains a storage object
9412 * S_OK if file has magic bytes as a storage object
9413 * S_FALSE if file is not storage
9416 StgIsStorageFile(LPCOLESTR fn
)
9422 TRACE("%s\n", debugstr_w(fn
));
9423 hf
= CreateFileW(fn
, GENERIC_READ
,
9424 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9425 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9427 if (hf
== INVALID_HANDLE_VALUE
)
9428 return STG_E_FILENOTFOUND
;
9430 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9432 WARN(" unable to read file\n");
9439 if (bytes_read
!= 8) {
9440 TRACE(" too short\n");
9444 if (!memcmp(magic
,STORAGE_magic
,8)) {
9449 TRACE(" -> Invalid header.\n");
9453 /***********************************************************************
9454 * WriteClassStm (OLE32.@)
9456 * Writes a CLSID to a stream.
9459 * pStm [I] Stream to write to.
9460 * rclsid [I] CLSID to write.
9464 * Failure: HRESULT code.
9466 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9468 TRACE("(%p,%p)\n",pStm
,rclsid
);
9470 if (!pStm
|| !rclsid
)
9471 return E_INVALIDARG
;
9473 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9476 /***********************************************************************
9477 * ReadClassStm (OLE32.@)
9479 * Reads a CLSID from a stream.
9482 * pStm [I] Stream to read from.
9483 * rclsid [O] CLSID to read.
9487 * Failure: HRESULT code.
9489 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9494 TRACE("(%p,%p)\n",pStm
,pclsid
);
9496 if (!pStm
|| !pclsid
)
9497 return E_INVALIDARG
;
9499 /* clear the output args */
9500 *pclsid
= CLSID_NULL
;
9502 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9507 if (nbByte
!= sizeof(CLSID
))
9508 return STG_E_READFAULT
;