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 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
92 DWORD openFlags
, DirRef storageDirEntry
);
93 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
94 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
95 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
96 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
98 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
99 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
101 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
102 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
103 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
104 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
107 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
108 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
109 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
111 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
112 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
113 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
114 ULONG blockIndex
, ULONG offset
, DWORD value
);
115 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
116 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
118 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
119 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base
;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl
*snapshot
;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl
*transactedParent
;
137 } TransactedSnapshotImpl
;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength
;
149 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
150 CHAR
*pstrOleObjFileName
;
151 DWORD dwOleObjFileNameLength
;
152 DWORD dwMetaFileWidth
;
153 DWORD dwMetaFileHeight
;
154 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA
;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength
;
166 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
167 DWORD dwOleTypeNameLength
;
168 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
169 DWORD dwProgIDNameLength
;
170 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
172 }OLECONVERT_ISTORAGE_COMPOBJ
;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES
;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT
deleteStorageContents(
192 StorageBaseImpl
*parentStorage
,
193 DirRef indexToDelete
,
194 DirEntry entryDataToDelete
);
196 static HRESULT
deleteStreamContents(
197 StorageBaseImpl
*parentStorage
,
198 DirRef indexToDelete
,
199 DirEntry entryDataToDelete
);
201 static HRESULT
removeFromTree(
202 StorageBaseImpl
*This
,
203 DirRef parentStorageIndex
,
204 DirRef deletedIndex
);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT
insertIntoTree(
211 StorageBaseImpl
*This
,
212 DirRef parentStorageIndex
,
213 DirRef newEntryIndex
);
215 static LONG
entryNameCmp(
216 const OLECHAR
*name1
,
217 const OLECHAR
*name2
);
219 static DirRef
findElement(
220 StorageBaseImpl
*storage
,
225 static HRESULT
findTreeParent(
226 StorageBaseImpl
*storage
,
228 const OLECHAR
*childName
,
229 DirEntry
*parentData
,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT
validateSTGM(DWORD stgmValue
);
238 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
239 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
240 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref
; /* Reference count */
258 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
259 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
261 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
265 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
268 /************************************************************************
272 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
274 if (index
== 0xffffffff)
279 return index
* BIG_BLOCK_SIZE
;
282 /************************************************************************
283 ** Storage32BaseImpl implementation
285 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
286 ULARGE_INTEGER offset
,
291 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
294 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
295 ULARGE_INTEGER offset
,
300 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
303 /************************************************************************
304 * Storage32BaseImpl_QueryInterface (IUnknown)
306 * This method implements the common QueryInterface for all IStorage32
307 * implementations contained in this file.
309 * See Windows documentation for more details on IUnknown methods.
311 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
316 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
318 if ( (This
==0) || (ppvObject
==0) )
323 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
324 IsEqualGUID(&IID_IStorage
, riid
))
328 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
330 *ppvObject
= &This
->pssVtbl
;
334 return E_NOINTERFACE
;
336 IStorage_AddRef(iface
);
341 /************************************************************************
342 * Storage32BaseImpl_AddRef (IUnknown)
344 * This method implements the common AddRef for all IStorage32
345 * implementations contained in this file.
347 * See Windows documentation for more details on IUnknown methods.
349 static ULONG WINAPI
StorageBaseImpl_AddRef(
352 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
353 ULONG ref
= InterlockedIncrement(&This
->ref
);
355 TRACE("(%p) AddRef to %d\n", This
, ref
);
360 /************************************************************************
361 * Storage32BaseImpl_Release (IUnknown)
363 * This method implements the common Release for all IStorage32
364 * implementations contained in this file.
366 * See Windows documentation for more details on IUnknown methods.
368 static ULONG WINAPI
StorageBaseImpl_Release(
371 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
373 ULONG ref
= InterlockedDecrement(&This
->ref
);
375 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
380 * Since we are using a system of base-classes, we want to call the
381 * destructor of the appropriate derived class. To do this, we are
382 * using virtual functions to implement the destructor.
384 StorageBaseImpl_Destroy(This
);
390 /************************************************************************
391 * Storage32BaseImpl_OpenStream (IStorage)
393 * This method will open the specified stream object from the current storage.
395 * See Windows documentation for more details on IStorage methods.
397 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
399 const OLECHAR
* pwcsName
, /* [string][in] */
400 void* reserved1
, /* [unique][in] */
401 DWORD grfMode
, /* [in] */
402 DWORD reserved2
, /* [in] */
403 IStream
** ppstm
) /* [out] */
405 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
406 StgStreamImpl
* newStream
;
407 DirEntry currentEntry
;
408 DirRef streamEntryRef
;
409 HRESULT res
= STG_E_UNKNOWN
;
411 TRACE("(%p, %s, %p, %x, %d, %p)\n",
412 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
414 if ( (pwcsName
==NULL
) || (ppstm
==0) )
422 if ( FAILED( validateSTGM(grfMode
) ) ||
423 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
425 res
= STG_E_INVALIDFLAG
;
432 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
434 res
= STG_E_INVALIDFUNCTION
;
440 res
= STG_E_REVERTED
;
445 * Check that we're compatible with the parent's storage mode, but
446 * only if we are not in transacted mode
448 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
449 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
451 res
= STG_E_INVALIDFLAG
;
457 * Search for the element with the given name
459 streamEntryRef
= findElement(
461 This
->storageDirEntry
,
466 * If it was found, construct the stream object and return a pointer to it.
468 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
469 (currentEntry
.stgType
==STGTY_STREAM
) )
471 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
473 /* A single stream cannot be opened a second time. */
474 res
= STG_E_ACCESSDENIED
;
478 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
482 newStream
->grfMode
= grfMode
;
483 *ppstm
= (IStream
*)newStream
;
485 IStream_AddRef(*ppstm
);
495 res
= STG_E_FILENOTFOUND
;
499 TRACE("<-- IStream %p\n", *ppstm
);
500 TRACE("<-- %08x\n", res
);
504 /************************************************************************
505 * Storage32BaseImpl_OpenStorage (IStorage)
507 * This method will open a new storage object from the current storage.
509 * See Windows documentation for more details on IStorage methods.
511 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
513 const OLECHAR
* pwcsName
, /* [string][unique][in] */
514 IStorage
* pstgPriority
, /* [unique][in] */
515 DWORD grfMode
, /* [in] */
516 SNB snbExclude
, /* [unique][in] */
517 DWORD reserved
, /* [in] */
518 IStorage
** ppstg
) /* [out] */
520 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
521 StorageInternalImpl
* newStorage
;
522 StorageBaseImpl
* newTransactedStorage
;
523 DirEntry currentEntry
;
524 DirRef storageEntryRef
;
525 HRESULT res
= STG_E_UNKNOWN
;
527 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
528 iface
, debugstr_w(pwcsName
), pstgPriority
,
529 grfMode
, snbExclude
, reserved
, ppstg
);
531 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
537 if (This
->openFlags
& STGM_SIMPLE
)
539 res
= STG_E_INVALIDFUNCTION
;
544 if (snbExclude
!= NULL
)
546 res
= STG_E_INVALIDPARAMETER
;
550 if ( FAILED( validateSTGM(grfMode
) ))
552 res
= STG_E_INVALIDFLAG
;
559 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
560 (grfMode
& STGM_DELETEONRELEASE
) ||
561 (grfMode
& STGM_PRIORITY
) )
563 res
= STG_E_INVALIDFUNCTION
;
568 return STG_E_REVERTED
;
571 * Check that we're compatible with the parent's storage mode,
572 * but only if we are not transacted
574 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
575 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
577 res
= STG_E_ACCESSDENIED
;
584 storageEntryRef
= findElement(
586 This
->storageDirEntry
,
590 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
591 (currentEntry
.stgType
==STGTY_STORAGE
) )
593 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
595 /* A single storage cannot be opened a second time. */
596 res
= STG_E_ACCESSDENIED
;
600 newStorage
= StorageInternalImpl_Construct(
607 if (grfMode
& STGM_TRANSACTED
)
609 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
613 HeapFree(GetProcessHeap(), 0, newStorage
);
617 *ppstg
= (IStorage
*)newTransactedStorage
;
621 *ppstg
= (IStorage
*)newStorage
;
624 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
630 res
= STG_E_INSUFFICIENTMEMORY
;
634 res
= STG_E_FILENOTFOUND
;
637 TRACE("<-- %08x\n", res
);
641 /************************************************************************
642 * Storage32BaseImpl_EnumElements (IStorage)
644 * This method will create an enumerator object that can be used to
645 * retrieve information about all the elements in the storage object.
647 * See Windows documentation for more details on IStorage methods.
649 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
651 DWORD reserved1
, /* [in] */
652 void* reserved2
, /* [size_is][unique][in] */
653 DWORD reserved3
, /* [in] */
654 IEnumSTATSTG
** ppenum
) /* [out] */
656 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
657 IEnumSTATSTGImpl
* newEnum
;
659 TRACE("(%p, %d, %p, %d, %p)\n",
660 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
662 if ( (This
==0) || (ppenum
==0))
666 return STG_E_REVERTED
;
668 newEnum
= IEnumSTATSTGImpl_Construct(
670 This
->storageDirEntry
);
674 *ppenum
= (IEnumSTATSTG
*)newEnum
;
676 IEnumSTATSTG_AddRef(*ppenum
);
681 return E_OUTOFMEMORY
;
684 /************************************************************************
685 * Storage32BaseImpl_Stat (IStorage)
687 * This method will retrieve information about this storage object.
689 * See Windows documentation for more details on IStorage methods.
691 static HRESULT WINAPI
StorageBaseImpl_Stat(
693 STATSTG
* pstatstg
, /* [out] */
694 DWORD grfStatFlag
) /* [in] */
696 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
697 DirEntry currentEntry
;
698 HRESULT res
= STG_E_UNKNOWN
;
700 TRACE("(%p, %p, %x)\n",
701 iface
, pstatstg
, grfStatFlag
);
703 if ( (This
==0) || (pstatstg
==0))
711 res
= STG_E_REVERTED
;
715 res
= StorageBaseImpl_ReadDirEntry(
717 This
->storageDirEntry
,
722 StorageUtl_CopyDirEntryToSTATSTG(
728 pstatstg
->grfMode
= This
->openFlags
;
729 pstatstg
->grfStateBits
= This
->stateBits
;
735 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
);
737 TRACE("<-- %08x\n", res
);
741 /************************************************************************
742 * Storage32BaseImpl_RenameElement (IStorage)
744 * This method will rename the specified element.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
750 const OLECHAR
* pwcsOldName
, /* [in] */
751 const OLECHAR
* pwcsNewName
) /* [in] */
753 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
754 DirEntry currentEntry
;
755 DirRef currentEntryRef
;
757 TRACE("(%p, %s, %s)\n",
758 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
761 return STG_E_REVERTED
;
763 currentEntryRef
= findElement(This
,
764 This
->storageDirEntry
,
768 if (currentEntryRef
!= DIRENTRY_NULL
)
771 * There is already an element with the new name
773 return STG_E_FILEALREADYEXISTS
;
777 * Search for the old element name
779 currentEntryRef
= findElement(This
,
780 This
->storageDirEntry
,
784 if (currentEntryRef
!= DIRENTRY_NULL
)
786 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
787 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
789 WARN("Element is already open; cannot rename.\n");
790 return STG_E_ACCESSDENIED
;
793 /* Remove the element from its current position in the tree */
794 removeFromTree(This
, This
->storageDirEntry
,
797 /* Change the name of the element */
798 strcpyW(currentEntry
.name
, pwcsNewName
);
800 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
803 /* Insert the element in a new position in the tree */
804 insertIntoTree(This
, This
->storageDirEntry
,
810 * There is no element with the old name
812 return STG_E_FILENOTFOUND
;
818 /************************************************************************
819 * Storage32BaseImpl_CreateStream (IStorage)
821 * This method will create a stream object within this storage
823 * See Windows documentation for more details on IStorage methods.
825 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
827 const OLECHAR
* pwcsName
, /* [string][in] */
828 DWORD grfMode
, /* [in] */
829 DWORD reserved1
, /* [in] */
830 DWORD reserved2
, /* [in] */
831 IStream
** ppstm
) /* [out] */
833 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
834 StgStreamImpl
* newStream
;
835 DirEntry currentEntry
, newStreamEntry
;
836 DirRef currentEntryRef
, newStreamEntryRef
;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface
, debugstr_w(pwcsName
), grfMode
,
840 reserved1
, reserved2
, ppstm
);
843 return STG_E_INVALIDPOINTER
;
846 return STG_E_INVALIDNAME
;
848 if (reserved1
|| reserved2
)
849 return STG_E_INVALIDPARAMETER
;
851 if ( FAILED( validateSTGM(grfMode
) ))
852 return STG_E_INVALIDFLAG
;
854 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
855 return STG_E_INVALIDFLAG
;
858 return STG_E_REVERTED
;
863 if ((grfMode
& STGM_DELETEONRELEASE
) ||
864 (grfMode
& STGM_TRANSACTED
))
865 return STG_E_INVALIDFUNCTION
;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
874 return STG_E_ACCESSDENIED
;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
878 return STG_E_ACCESSDENIED
;
881 if(This
->openFlags
& STGM_SIMPLE
)
882 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
886 currentEntryRef
= findElement(This
,
887 This
->storageDirEntry
,
891 if (currentEntryRef
!= DIRENTRY_NULL
)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
898 IStorage_DestroyElement(iface
, pwcsName
);
901 return STG_E_FILEALREADYEXISTS
;
905 * memset the empty entry
907 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
909 newStreamEntry
.sizeOfNameString
=
910 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
912 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
913 return STG_E_INVALIDNAME
;
915 strcpyW(newStreamEntry
.name
, pwcsName
);
917 newStreamEntry
.stgType
= STGTY_STREAM
;
918 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
919 newStreamEntry
.size
.u
.LowPart
= 0;
920 newStreamEntry
.size
.u
.HighPart
= 0;
922 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
923 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
924 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
926 /* call CoFileTime to get the current time
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
938 * Insert the new entry in the parent storage's tree.
942 This
->storageDirEntry
,
946 * Open the stream to return it.
948 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
952 *ppstm
= (IStream
*)newStream
;
954 IStream_AddRef(*ppstm
);
958 return STG_E_INSUFFICIENTMEMORY
;
964 /************************************************************************
965 * Storage32BaseImpl_SetClass (IStorage)
967 * This method will write the specified CLSID in the directory entry of this
970 * See Windows documentation for more details on IStorage methods.
972 static HRESULT WINAPI
StorageBaseImpl_SetClass(
974 REFCLSID clsid
) /* [in] */
976 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
978 DirEntry currentEntry
;
980 TRACE("(%p, %p)\n", iface
, clsid
);
983 return STG_E_REVERTED
;
985 hRes
= StorageBaseImpl_ReadDirEntry(This
,
986 This
->storageDirEntry
,
990 currentEntry
.clsid
= *clsid
;
992 hRes
= StorageBaseImpl_WriteDirEntry(This
,
993 This
->storageDirEntry
,
1000 /************************************************************************
1001 ** Storage32Impl implementation
1004 /************************************************************************
1005 * Storage32BaseImpl_CreateStorage (IStorage)
1007 * This method will create the storage object within the provided storage.
1009 * See Windows documentation for more details on IStorage methods.
1011 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1013 const OLECHAR
*pwcsName
, /* [string][in] */
1014 DWORD grfMode
, /* [in] */
1015 DWORD reserved1
, /* [in] */
1016 DWORD reserved2
, /* [in] */
1017 IStorage
**ppstg
) /* [out] */
1019 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1021 DirEntry currentEntry
;
1023 DirRef currentEntryRef
;
1027 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1028 iface
, debugstr_w(pwcsName
), grfMode
,
1029 reserved1
, reserved2
, ppstg
);
1032 return STG_E_INVALIDPOINTER
;
1034 if (This
->openFlags
& STGM_SIMPLE
)
1036 return STG_E_INVALIDFUNCTION
;
1040 return STG_E_INVALIDNAME
;
1044 if ( FAILED( validateSTGM(grfMode
) ) ||
1045 (grfMode
& STGM_DELETEONRELEASE
) )
1047 WARN("bad grfMode: 0x%x\n", grfMode
);
1048 return STG_E_INVALIDFLAG
;
1052 return STG_E_REVERTED
;
1055 * Check that we're compatible with the parent's storage mode
1057 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1058 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1060 WARN("access denied\n");
1061 return STG_E_ACCESSDENIED
;
1064 currentEntryRef
= findElement(This
,
1065 This
->storageDirEntry
,
1069 if (currentEntryRef
!= DIRENTRY_NULL
)
1072 * An element with this name already exists
1074 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1075 ((This
->openFlags
& STGM_TRANSACTED
) ||
1076 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1078 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1084 WARN("file already exists\n");
1085 return STG_E_FILEALREADYEXISTS
;
1088 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1089 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1091 WARN("read-only storage\n");
1092 return STG_E_ACCESSDENIED
;
1095 memset(&newEntry
, 0, sizeof(DirEntry
));
1097 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1099 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1101 FIXME("name too long\n");
1102 return STG_E_INVALIDNAME
;
1105 strcpyW(newEntry
.name
, pwcsName
);
1107 newEntry
.stgType
= STGTY_STORAGE
;
1108 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1109 newEntry
.size
.u
.LowPart
= 0;
1110 newEntry
.size
.u
.HighPart
= 0;
1112 newEntry
.leftChild
= DIRENTRY_NULL
;
1113 newEntry
.rightChild
= DIRENTRY_NULL
;
1114 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1116 /* call CoFileTime to get the current time
1121 /* newEntry.clsid */
1124 * Create a new directory entry for the storage
1126 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1129 * Insert the new directory entry into the parent storage's tree
1133 This
->storageDirEntry
,
1137 * Open it to get a pointer to return.
1139 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1141 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1151 /***************************************************************************
1155 * Reserve a directory entry in the file and initialize it.
1157 static HRESULT
StorageImpl_CreateDirEntry(
1158 StorageBaseImpl
*base
,
1159 const DirEntry
*newData
,
1162 StorageImpl
*storage
= (StorageImpl
*)base
;
1163 ULONG currentEntryIndex
= 0;
1164 ULONG newEntryIndex
= DIRENTRY_NULL
;
1166 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1167 WORD sizeOfNameString
;
1171 hr
= StorageImpl_ReadRawDirEntry(storage
,
1177 StorageUtl_ReadWord(
1179 OFFSET_PS_NAMELENGTH
,
1182 if (sizeOfNameString
== 0)
1185 * The entry exists and is available, we found it.
1187 newEntryIndex
= currentEntryIndex
;
1193 * We exhausted the directory entries, we will create more space below
1195 newEntryIndex
= currentEntryIndex
;
1197 currentEntryIndex
++;
1199 } while (newEntryIndex
== DIRENTRY_NULL
);
1202 * grow the directory stream
1206 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1207 ULARGE_INTEGER newSize
;
1209 ULONG lastEntry
= 0;
1210 ULONG blockCount
= 0;
1213 * obtain the new count of blocks in the directory stream
1215 blockCount
= BlockChainStream_GetCount(
1216 storage
->rootBlockChain
)+1;
1219 * initialize the size used by the directory stream
1221 newSize
.u
.HighPart
= 0;
1222 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1225 * add a block to the directory stream
1227 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1230 * memset the empty entry in order to initialize the unused newly
1233 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1238 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1241 entryIndex
= newEntryIndex
+ 1;
1242 entryIndex
< lastEntry
;
1245 StorageImpl_WriteRawDirEntry(
1252 UpdateRawDirEntry(currentData
, newData
);
1254 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1257 *index
= newEntryIndex
;
1262 /***************************************************************************
1266 * Mark a directory entry in the file as free.
1268 static HRESULT
StorageImpl_DestroyDirEntry(
1269 StorageBaseImpl
*base
,
1273 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1274 StorageImpl
*storage
= (StorageImpl
*)base
;
1276 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1278 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1284 /***************************************************************************
1288 * Destroy an entry, its attached data, and all entries reachable from it.
1290 static HRESULT
DestroyReachableEntries(
1291 StorageBaseImpl
*base
,
1296 ULARGE_INTEGER zero
;
1300 if (index
!= DIRENTRY_NULL
)
1302 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1305 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1308 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1311 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1314 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1317 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1324 /****************************************************************************
1328 * Case insensitive comparison of DirEntry.name by first considering
1331 * Returns <0 when name1 < name2
1332 * >0 when name1 > name2
1333 * 0 when name1 == name2
1335 static LONG
entryNameCmp(
1336 const OLECHAR
*name1
,
1337 const OLECHAR
*name2
)
1339 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1341 while (diff
== 0 && *name1
!= 0)
1344 * We compare the string themselves only when they are of the same length
1346 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1352 /****************************************************************************
1356 * Add a directory entry to a storage
1358 static HRESULT
insertIntoTree(
1359 StorageBaseImpl
*This
,
1360 DirRef parentStorageIndex
,
1361 DirRef newEntryIndex
)
1363 DirEntry currentEntry
;
1367 * Read the inserted entry
1369 StorageBaseImpl_ReadDirEntry(This
,
1374 * Read the storage entry
1376 StorageBaseImpl_ReadDirEntry(This
,
1380 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1383 * The root storage contains some element, therefore, start the research
1384 * for the appropriate location.
1387 DirRef current
, next
, previous
, currentEntryId
;
1390 * Keep a reference to the root of the storage's element tree
1392 currentEntryId
= currentEntry
.dirRootEntry
;
1397 StorageBaseImpl_ReadDirEntry(This
,
1398 currentEntry
.dirRootEntry
,
1401 previous
= currentEntry
.leftChild
;
1402 next
= currentEntry
.rightChild
;
1403 current
= currentEntryId
;
1407 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1411 if (previous
!= DIRENTRY_NULL
)
1413 StorageBaseImpl_ReadDirEntry(This
,
1420 currentEntry
.leftChild
= newEntryIndex
;
1421 StorageBaseImpl_WriteDirEntry(This
,
1429 if (next
!= DIRENTRY_NULL
)
1431 StorageBaseImpl_ReadDirEntry(This
,
1438 currentEntry
.rightChild
= newEntryIndex
;
1439 StorageBaseImpl_WriteDirEntry(This
,
1448 * Trying to insert an item with the same name in the
1449 * subtree structure.
1451 return STG_E_FILEALREADYEXISTS
;
1454 previous
= currentEntry
.leftChild
;
1455 next
= currentEntry
.rightChild
;
1461 * The storage is empty, make the new entry the root of its element tree
1463 currentEntry
.dirRootEntry
= newEntryIndex
;
1464 StorageBaseImpl_WriteDirEntry(This
,
1472 /****************************************************************************
1476 * Find and read the element of a storage with the given name.
1478 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1479 const OLECHAR
*name
, DirEntry
*data
)
1481 DirRef currentEntry
;
1483 /* Read the storage entry to find the root of the tree. */
1484 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1486 currentEntry
= data
->dirRootEntry
;
1488 while (currentEntry
!= DIRENTRY_NULL
)
1492 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1494 cmp
= entryNameCmp(name
, data
->name
);
1501 currentEntry
= data
->leftChild
;
1504 currentEntry
= data
->rightChild
;
1507 return currentEntry
;
1510 /****************************************************************************
1514 * Find and read the binary tree parent of the element with the given name.
1516 * If there is no such element, find a place where it could be inserted and
1517 * return STG_E_FILENOTFOUND.
1519 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1520 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1526 /* Read the storage entry to find the root of the tree. */
1527 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1529 *parentEntry
= storageEntry
;
1530 *relation
= DIRENTRY_RELATION_DIR
;
1532 childEntry
= parentData
->dirRootEntry
;
1534 while (childEntry
!= DIRENTRY_NULL
)
1538 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1540 cmp
= entryNameCmp(childName
, childData
.name
);
1548 *parentData
= childData
;
1549 *parentEntry
= childEntry
;
1550 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1552 childEntry
= parentData
->leftChild
;
1557 *parentData
= childData
;
1558 *parentEntry
= childEntry
;
1559 *relation
= DIRENTRY_RELATION_NEXT
;
1561 childEntry
= parentData
->rightChild
;
1565 if (childEntry
== DIRENTRY_NULL
)
1566 return STG_E_FILENOTFOUND
;
1572 /*************************************************************************
1575 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1577 DWORD ciidExclude
, /* [in] */
1578 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1579 SNB snbExclude
, /* [unique][in] */
1580 IStorage
* pstgDest
) /* [unique][in] */
1582 IEnumSTATSTG
*elements
= 0;
1583 STATSTG curElement
, strStat
;
1585 IStorage
*pstgTmp
, *pstgChild
;
1586 IStream
*pstrTmp
, *pstrChild
;
1587 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1590 TRACE("(%p, %d, %p, %p, %p)\n",
1591 iface
, ciidExclude
, rgiidExclude
,
1592 snbExclude
, pstgDest
);
1594 if ( pstgDest
== 0 )
1595 return STG_E_INVALIDPOINTER
;
1598 * Enumerate the elements
1600 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1608 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1609 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1611 for(i
= 0; i
< ciidExclude
; ++i
)
1613 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1614 skip_storage
= TRUE
;
1615 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1618 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1624 * Obtain the next element
1626 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1628 if ( hr
== S_FALSE
)
1630 hr
= S_OK
; /* done, every element has been copied */
1636 WCHAR
**snb
= snbExclude
;
1638 while ( *snb
!= NULL
&& !skip
)
1640 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1649 if (curElement
.type
== STGTY_STORAGE
)
1655 * open child source storage
1657 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1658 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1659 NULL
, 0, &pstgChild
);
1665 * create a new storage in destination storage
1667 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1668 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1672 * if it already exist, don't create a new one use this one
1674 if (hr
== STG_E_FILEALREADYEXISTS
)
1676 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1677 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1678 NULL
, 0, &pstgTmp
);
1684 * do the copy recursively
1686 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1689 IStorage_Release( pstgTmp
);
1692 IStorage_Release( pstgChild
);
1694 else if (curElement
.type
== STGTY_STREAM
)
1700 * create a new stream in destination storage. If the stream already
1701 * exist, it will be deleted and a new one will be created.
1703 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1704 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1711 * open child stream storage
1713 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1714 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1720 * Get the size of the source stream
1722 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1725 * Set the size of the destination stream.
1727 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1732 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1735 IStream_Release( pstrChild
);
1738 IStream_Release( pstrTmp
);
1742 WARN("unknown element type: %d\n", curElement
.type
);
1746 CoTaskMemFree(curElement
.pwcsName
);
1747 } while (hr
== S_OK
);
1752 IEnumSTATSTG_Release(elements
);
1757 /*************************************************************************
1758 * MoveElementTo (IStorage)
1760 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1762 const OLECHAR
*pwcsName
, /* [string][in] */
1763 IStorage
*pstgDest
, /* [unique][in] */
1764 const OLECHAR
*pwcsNewName
,/* [string][in] */
1765 DWORD grfFlags
) /* [in] */
1767 FIXME("(%p %s %p %s %u): stub\n", iface
,
1768 debugstr_w(pwcsName
), pstgDest
,
1769 debugstr_w(pwcsNewName
), grfFlags
);
1773 /*************************************************************************
1776 * Ensures that any changes made to a storage object open in transacted mode
1777 * are reflected in the parent storage
1780 * Wine doesn't implement transacted mode, which seems to be a basic
1781 * optimization, so we can ignore this stub for now.
1783 static HRESULT WINAPI
StorageImpl_Commit(
1785 DWORD grfCommitFlags
)/* [in] */
1787 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1791 /*************************************************************************
1794 * Discard all changes that have been made since the last commit operation
1796 static HRESULT WINAPI
StorageImpl_Revert(
1799 TRACE("(%p)\n", iface
);
1803 /*************************************************************************
1804 * DestroyElement (IStorage)
1806 * Strategy: This implementation is built this way for simplicity not for speed.
1807 * I always delete the topmost element of the enumeration and adjust
1808 * the deleted element pointer all the time. This takes longer to
1809 * do but allow to reinvoke DestroyElement whenever we encounter a
1810 * storage object. The optimisation resides in the usage of another
1811 * enumeration strategy that would give all the leaves of a storage
1812 * first. (postfix order)
1814 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1816 const OLECHAR
*pwcsName
)/* [string][in] */
1818 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1821 DirEntry entryToDelete
;
1822 DirRef entryToDeleteRef
;
1825 iface
, debugstr_w(pwcsName
));
1828 return STG_E_INVALIDPOINTER
;
1831 return STG_E_REVERTED
;
1833 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1834 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1835 return STG_E_ACCESSDENIED
;
1837 entryToDeleteRef
= findElement(
1839 This
->storageDirEntry
,
1843 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1845 return STG_E_FILENOTFOUND
;
1848 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1850 hr
= deleteStorageContents(
1855 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1857 hr
= deleteStreamContents(
1867 * Remove the entry from its parent storage
1869 hr
= removeFromTree(
1871 This
->storageDirEntry
,
1875 * Invalidate the entry
1878 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1884 /******************************************************************************
1885 * Internal stream list handlers
1888 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1890 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1891 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1894 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1896 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1897 list_remove(&(strm
->StrmListEntry
));
1900 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1902 StgStreamImpl
*strm
;
1904 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1906 if (strm
->dirEntry
== streamEntry
)
1915 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1917 StorageInternalImpl
*childstg
;
1919 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1921 if (childstg
->base
.storageDirEntry
== storageEntry
)
1930 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1932 struct list
*cur
, *cur2
;
1933 StgStreamImpl
*strm
=NULL
;
1934 StorageInternalImpl
*childstg
=NULL
;
1936 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1937 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1938 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1939 strm
->parentStorage
= NULL
;
1943 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1944 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1945 StorageBaseImpl_Invalidate( &childstg
->base
);
1948 if (stg
->transactedChild
)
1950 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1952 stg
->transactedChild
= NULL
;
1957 /*********************************************************************
1961 * Delete the contents of a storage entry.
1964 static HRESULT
deleteStorageContents(
1965 StorageBaseImpl
*parentStorage
,
1966 DirRef indexToDelete
,
1967 DirEntry entryDataToDelete
)
1969 IEnumSTATSTG
*elements
= 0;
1970 IStorage
*childStorage
= 0;
1971 STATSTG currentElement
;
1973 HRESULT destroyHr
= S_OK
;
1974 StorageInternalImpl
*stg
, *stg2
;
1976 /* Invalidate any open storage objects. */
1977 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1979 if (stg
->base
.storageDirEntry
== indexToDelete
)
1981 StorageBaseImpl_Invalidate(&stg
->base
);
1986 * Open the storage and enumerate it
1988 hr
= StorageBaseImpl_OpenStorage(
1989 (IStorage
*)parentStorage
,
1990 entryDataToDelete
.name
,
1992 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2003 * Enumerate the elements
2005 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2010 * Obtain the next element
2012 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2015 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2017 CoTaskMemFree(currentElement
.pwcsName
);
2021 * We need to Reset the enumeration every time because we delete elements
2022 * and the enumeration could be invalid
2024 IEnumSTATSTG_Reset(elements
);
2026 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2028 IStorage_Release(childStorage
);
2029 IEnumSTATSTG_Release(elements
);
2034 /*********************************************************************
2038 * Perform the deletion of a stream's data
2041 static HRESULT
deleteStreamContents(
2042 StorageBaseImpl
*parentStorage
,
2043 DirRef indexToDelete
,
2044 DirEntry entryDataToDelete
)
2048 ULARGE_INTEGER size
;
2049 StgStreamImpl
*strm
, *strm2
;
2051 /* Invalidate any open stream objects. */
2052 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2054 if (strm
->dirEntry
== indexToDelete
)
2056 TRACE("Stream deleted %p\n", strm
);
2057 strm
->parentStorage
= NULL
;
2058 list_remove(&strm
->StrmListEntry
);
2062 size
.u
.HighPart
= 0;
2065 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2066 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2076 hr
= IStream_SetSize(pis
, size
);
2084 * Release the stream object.
2086 IStream_Release(pis
);
2091 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2095 case DIRENTRY_RELATION_PREVIOUS
:
2096 entry
->leftChild
= new_target
;
2098 case DIRENTRY_RELATION_NEXT
:
2099 entry
->rightChild
= new_target
;
2101 case DIRENTRY_RELATION_DIR
:
2102 entry
->dirRootEntry
= new_target
;
2109 /*************************************************************************
2113 * This method removes a directory entry from its parent storage tree without
2114 * freeing any resources attached to it.
2116 static HRESULT
removeFromTree(
2117 StorageBaseImpl
*This
,
2118 DirRef parentStorageIndex
,
2119 DirRef deletedIndex
)
2122 DirEntry entryToDelete
;
2123 DirEntry parentEntry
;
2124 DirRef parentEntryRef
;
2125 ULONG typeOfRelation
;
2127 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2133 * Find the element that links to the one we want to delete.
2135 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2136 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2141 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2144 * Replace the deleted entry with its left child
2146 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2148 hr
= StorageBaseImpl_WriteDirEntry(
2157 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2160 * We need to reinsert the right child somewhere. We already know it and
2161 * its children are greater than everything in the left tree, so we
2162 * insert it at the rightmost point in the left tree.
2164 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2165 DirEntry newRightChildParentEntry
;
2169 hr
= StorageBaseImpl_ReadDirEntry(
2171 newRightChildParent
,
2172 &newRightChildParentEntry
);
2178 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2179 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2180 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2182 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2184 hr
= StorageBaseImpl_WriteDirEntry(
2186 newRightChildParent
,
2187 &newRightChildParentEntry
);
2197 * Replace the deleted entry with its right child
2199 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2201 hr
= StorageBaseImpl_WriteDirEntry(
2215 /******************************************************************************
2216 * SetElementTimes (IStorage)
2218 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2220 const OLECHAR
*pwcsName
,/* [string][in] */
2221 const FILETIME
*pctime
, /* [in] */
2222 const FILETIME
*patime
, /* [in] */
2223 const FILETIME
*pmtime
) /* [in] */
2225 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2229 /******************************************************************************
2230 * SetStateBits (IStorage)
2232 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2234 DWORD grfStateBits
,/* [in] */
2235 DWORD grfMask
) /* [in] */
2237 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2240 return STG_E_REVERTED
;
2242 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2246 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2247 DirRef index
, const DirEntry
*data
)
2249 StorageImpl
*This
= (StorageImpl
*)base
;
2250 return StorageImpl_WriteDirEntry(This
, index
, data
);
2253 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2254 DirRef index
, DirEntry
*data
)
2256 StorageImpl
*This
= (StorageImpl
*)base
;
2257 return StorageImpl_ReadDirEntry(This
, index
, data
);
2260 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2264 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2266 if (!This
->blockChainCache
[i
])
2268 return &This
->blockChainCache
[i
];
2272 i
= This
->blockChainToEvict
;
2274 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2275 This
->blockChainCache
[i
] = NULL
;
2277 This
->blockChainToEvict
++;
2278 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2279 This
->blockChainToEvict
= 0;
2281 return &This
->blockChainCache
[i
];
2284 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2287 int i
, free_index
=-1;
2289 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2291 if (!This
->blockChainCache
[i
])
2293 if (free_index
== -1) free_index
= i
;
2295 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2297 return &This
->blockChainCache
[i
];
2301 if (free_index
== -1)
2303 free_index
= This
->blockChainToEvict
;
2305 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2306 This
->blockChainCache
[free_index
] = NULL
;
2308 This
->blockChainToEvict
++;
2309 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2310 This
->blockChainToEvict
= 0;
2313 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2314 return &This
->blockChainCache
[free_index
];
2317 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2318 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2320 StorageImpl
*This
= (StorageImpl
*)base
;
2325 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2326 if (FAILED(hr
)) return hr
;
2328 if (data
.size
.QuadPart
== 0)
2334 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2336 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2343 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2345 SmallBlockChainStream
*stream
;
2347 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2348 if (!stream
) return E_OUTOFMEMORY
;
2350 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2352 SmallBlockChainStream_Destroy(stream
);
2358 BlockChainStream
*stream
= NULL
;
2360 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2361 if (!stream
) return E_OUTOFMEMORY
;
2363 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2369 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2370 ULARGE_INTEGER newsize
)
2372 StorageImpl
*This
= (StorageImpl
*)base
;
2375 SmallBlockChainStream
*smallblock
=NULL
;
2376 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2378 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2379 if (FAILED(hr
)) return hr
;
2381 /* In simple mode keep the stream size above the small block limit */
2382 if (This
->base
.openFlags
& STGM_SIMPLE
)
2383 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2385 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2388 /* Create a block chain object of the appropriate type */
2389 if (data
.size
.QuadPart
== 0)
2391 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2393 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2394 if (!smallblock
) return E_OUTOFMEMORY
;
2398 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2399 bigblock
= *pbigblock
;
2400 if (!bigblock
) return E_OUTOFMEMORY
;
2403 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2405 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2406 if (!smallblock
) return E_OUTOFMEMORY
;
2410 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2411 bigblock
= *pbigblock
;
2412 if (!bigblock
) return E_OUTOFMEMORY
;
2415 /* Change the block chain type if necessary. */
2416 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2418 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2421 SmallBlockChainStream_Destroy(smallblock
);
2425 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2426 *pbigblock
= bigblock
;
2428 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2430 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2435 /* Set the size of the block chain. */
2438 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2439 SmallBlockChainStream_Destroy(smallblock
);
2443 BlockChainStream_SetSize(bigblock
, newsize
);
2446 /* Set the size in the directory entry. */
2447 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2450 data
.size
= newsize
;
2452 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2457 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2458 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2460 StorageImpl
*This
= (StorageImpl
*)base
;
2463 ULARGE_INTEGER newSize
;
2465 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2466 if (FAILED(hr
)) return hr
;
2468 /* Grow the stream if necessary */
2469 newSize
.QuadPart
= 0;
2470 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2472 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2474 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2478 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2479 if (FAILED(hr
)) return hr
;
2482 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2484 SmallBlockChainStream
*stream
;
2486 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2487 if (!stream
) return E_OUTOFMEMORY
;
2489 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2491 SmallBlockChainStream_Destroy(stream
);
2497 BlockChainStream
*stream
;
2499 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2500 if (!stream
) return E_OUTOFMEMORY
;
2502 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2509 * Virtual function table for the IStorage32Impl class.
2511 static const IStorageVtbl Storage32Impl_Vtbl
=
2513 StorageBaseImpl_QueryInterface
,
2514 StorageBaseImpl_AddRef
,
2515 StorageBaseImpl_Release
,
2516 StorageBaseImpl_CreateStream
,
2517 StorageBaseImpl_OpenStream
,
2518 StorageBaseImpl_CreateStorage
,
2519 StorageBaseImpl_OpenStorage
,
2520 StorageBaseImpl_CopyTo
,
2521 StorageBaseImpl_MoveElementTo
,
2524 StorageBaseImpl_EnumElements
,
2525 StorageBaseImpl_DestroyElement
,
2526 StorageBaseImpl_RenameElement
,
2527 StorageBaseImpl_SetElementTimes
,
2528 StorageBaseImpl_SetClass
,
2529 StorageBaseImpl_SetStateBits
,
2530 StorageBaseImpl_Stat
2533 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2535 StorageImpl_Destroy
,
2536 StorageImpl_Invalidate
,
2537 StorageImpl_CreateDirEntry
,
2538 StorageImpl_BaseWriteDirEntry
,
2539 StorageImpl_BaseReadDirEntry
,
2540 StorageImpl_DestroyDirEntry
,
2541 StorageImpl_StreamReadAt
,
2542 StorageImpl_StreamWriteAt
,
2543 StorageImpl_StreamSetSize
2546 static HRESULT
StorageImpl_Construct(
2553 StorageImpl
** result
)
2557 DirEntry currentEntry
;
2558 DirRef currentEntryRef
;
2559 WCHAR fullpath
[MAX_PATH
];
2561 if ( FAILED( validateSTGM(openFlags
) ))
2562 return STG_E_INVALIDFLAG
;
2564 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2566 return E_OUTOFMEMORY
;
2568 memset(This
, 0, sizeof(StorageImpl
));
2570 list_init(&This
->base
.strmHead
);
2572 list_init(&This
->base
.storageHead
);
2574 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2575 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2576 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2577 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2579 This
->base
.create
= create
;
2581 This
->base
.reverted
= 0;
2583 This
->hFile
= hFile
;
2586 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2588 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2590 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2591 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2592 if (!This
->pwcsName
)
2594 hr
= STG_E_INSUFFICIENTMEMORY
;
2597 strcpyW(This
->pwcsName
, fullpath
);
2598 This
->base
.filename
= This
->pwcsName
;
2602 * Initialize the big block cache.
2604 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2605 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2606 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2612 if (This
->bigBlockFile
== 0)
2620 ULARGE_INTEGER size
;
2621 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2624 * Initialize all header variables:
2625 * - The big block depot consists of one block and it is at block 0
2626 * - The directory table starts at block 1
2627 * - There is no small block depot
2629 memset( This
->bigBlockDepotStart
,
2631 sizeof(This
->bigBlockDepotStart
));
2633 This
->bigBlockDepotCount
= 1;
2634 This
->bigBlockDepotStart
[0] = 0;
2635 This
->rootStartBlock
= 1;
2636 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2637 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2638 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2639 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2640 This
->extBigBlockDepotCount
= 0;
2642 StorageImpl_SaveFileHeader(This
);
2645 * Add one block for the big block depot and one block for the directory table
2647 size
.u
.HighPart
= 0;
2648 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2649 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2652 * Initialize the big block depot
2654 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2655 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2656 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2657 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2662 * Load the header for the file.
2664 hr
= StorageImpl_LoadFileHeader(This
);
2673 * There is no block depot cached yet.
2675 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2678 * Start searching for free blocks with block 0.
2680 This
->prevFreeBlock
= 0;
2683 * Create the block chain abstractions.
2685 if(!(This
->rootBlockChain
=
2686 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2688 hr
= STG_E_READFAULT
;
2692 if(!(This
->smallBlockDepotChain
=
2693 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2696 hr
= STG_E_READFAULT
;
2701 * Write the root storage entry (memory only)
2707 * Initialize the directory table
2709 memset(&rootEntry
, 0, sizeof(rootEntry
));
2710 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2711 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2712 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2713 rootEntry
.stgType
= STGTY_ROOT
;
2714 rootEntry
.leftChild
= DIRENTRY_NULL
;
2715 rootEntry
.rightChild
= DIRENTRY_NULL
;
2716 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2717 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2718 rootEntry
.size
.u
.HighPart
= 0;
2719 rootEntry
.size
.u
.LowPart
= 0;
2721 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2725 * Find the ID of the root storage.
2727 currentEntryRef
= 0;
2731 hr
= StorageImpl_ReadDirEntry(
2738 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2739 (currentEntry
.stgType
== STGTY_ROOT
) )
2741 This
->base
.storageDirEntry
= currentEntryRef
;
2747 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2751 hr
= STG_E_READFAULT
;
2756 * Create the block chain abstraction for the small block root chain.
2758 if(!(This
->smallBlockRootChain
=
2759 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2761 hr
= STG_E_READFAULT
;
2767 IStorage_Release((IStorage
*)This
);
2776 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2778 StorageImpl
*This
= (StorageImpl
*) iface
;
2780 StorageBaseImpl_DeleteAll(&This
->base
);
2782 This
->base
.reverted
= 1;
2785 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2787 StorageImpl
*This
= (StorageImpl
*) iface
;
2789 TRACE("(%p)\n", This
);
2791 StorageImpl_Invalidate(iface
);
2793 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2795 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2796 BlockChainStream_Destroy(This
->rootBlockChain
);
2797 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2799 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2800 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2802 if (This
->bigBlockFile
)
2803 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2804 HeapFree(GetProcessHeap(), 0, This
);
2807 /******************************************************************************
2808 * Storage32Impl_GetNextFreeBigBlock
2810 * Returns the index of the next free big block.
2811 * If the big block depot is filled, this method will enlarge it.
2814 static ULONG
StorageImpl_GetNextFreeBigBlock(
2817 ULONG depotBlockIndexPos
;
2818 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2820 ULONG depotBlockOffset
;
2821 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2822 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2824 ULONG freeBlock
= BLOCK_UNUSED
;
2826 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2827 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2830 * Scan the entire big block depot until we find a block marked free
2832 while (nextBlockIndex
!= BLOCK_UNUSED
)
2834 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2836 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2839 * Grow the primary depot.
2841 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2843 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2846 * Add a block depot.
2848 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2849 This
->bigBlockDepotCount
++;
2850 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2853 * Flag it as a block depot.
2855 StorageImpl_SetNextBlockInChain(This
,
2859 /* Save new header information.
2861 StorageImpl_SaveFileHeader(This
);
2866 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2868 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2871 * Grow the extended depot.
2873 ULONG extIndex
= BLOCK_UNUSED
;
2874 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2875 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2877 if (extBlockOffset
== 0)
2879 /* We need an extended block.
2881 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2882 This
->extBigBlockDepotCount
++;
2883 depotBlockIndexPos
= extIndex
+ 1;
2886 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2889 * Add a block depot and mark it in the extended block.
2891 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2892 This
->bigBlockDepotCount
++;
2893 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2895 /* Flag the block depot.
2897 StorageImpl_SetNextBlockInChain(This
,
2901 /* If necessary, flag the extended depot block.
2903 if (extIndex
!= BLOCK_UNUSED
)
2904 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2906 /* Save header information.
2908 StorageImpl_SaveFileHeader(This
);
2912 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2916 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2917 ( nextBlockIndex
!= BLOCK_UNUSED
))
2919 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2921 if (nextBlockIndex
== BLOCK_UNUSED
)
2923 freeBlock
= (depotIndex
* blocksPerDepot
) +
2924 (depotBlockOffset
/sizeof(ULONG
));
2927 depotBlockOffset
+= sizeof(ULONG
);
2932 depotBlockOffset
= 0;
2936 * make sure that the block physically exists before using it
2938 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2940 This
->prevFreeBlock
= freeBlock
;
2945 /******************************************************************************
2946 * Storage32Impl_AddBlockDepot
2948 * This will create a depot block, essentially it is a block initialized
2951 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2953 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2956 * Initialize blocks as free
2958 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2959 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2962 /******************************************************************************
2963 * Storage32Impl_GetExtDepotBlock
2965 * Returns the index of the block that corresponds to the specified depot
2966 * index. This method is only for depot indexes equal or greater than
2967 * COUNT_BBDEPOTINHEADER.
2969 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2971 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2972 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2973 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2974 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2975 ULONG blockIndex
= BLOCK_UNUSED
;
2976 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2978 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2980 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2981 return BLOCK_UNUSED
;
2983 while (extBlockCount
> 0)
2985 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2989 if (extBlockIndex
!= BLOCK_UNUSED
)
2990 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2991 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2996 /******************************************************************************
2997 * Storage32Impl_SetExtDepotBlock
2999 * Associates the specified block index to the specified depot index.
3000 * This method is only for depot indexes equal or greater than
3001 * COUNT_BBDEPOTINHEADER.
3003 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3005 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3006 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3007 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3008 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3009 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3011 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3013 while (extBlockCount
> 0)
3015 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3019 if (extBlockIndex
!= BLOCK_UNUSED
)
3021 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3022 extBlockOffset
* sizeof(ULONG
),
3027 /******************************************************************************
3028 * Storage32Impl_AddExtBlockDepot
3030 * Creates an extended depot block.
3032 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3034 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3035 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3036 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3037 ULONG index
= BLOCK_UNUSED
;
3038 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3039 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3040 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3042 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3043 blocksPerDepotBlock
;
3045 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3048 * The first extended block.
3050 This
->extBigBlockDepotStart
= index
;
3056 * Follow the chain to the last one.
3058 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3060 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3064 * Add the new extended block to the chain.
3066 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3071 * Initialize this block.
3073 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3074 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3079 /******************************************************************************
3080 * Storage32Impl_FreeBigBlock
3082 * This method will flag the specified block as free in the big block depot.
3084 static void StorageImpl_FreeBigBlock(
3088 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3090 if (blockIndex
< This
->prevFreeBlock
)
3091 This
->prevFreeBlock
= blockIndex
;
3094 /************************************************************************
3095 * Storage32Impl_GetNextBlockInChain
3097 * This method will retrieve the block index of the next big block in
3100 * Params: This - Pointer to the Storage object.
3101 * blockIndex - Index of the block to retrieve the chain
3103 * nextBlockIndex - receives the return value.
3105 * Returns: This method returns the index of the next block in the chain.
3106 * It will return the constants:
3107 * BLOCK_SPECIAL - If the block given was not part of a
3109 * BLOCK_END_OF_CHAIN - If the block given was the last in
3111 * BLOCK_UNUSED - If the block given was not past of a chain
3113 * BLOCK_EXTBBDEPOT - This block is part of the extended
3116 * See Windows documentation for more details on IStorage methods.
3118 static HRESULT
StorageImpl_GetNextBlockInChain(
3121 ULONG
* nextBlockIndex
)
3123 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3124 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3125 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3126 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3128 ULONG depotBlockIndexPos
;
3131 *nextBlockIndex
= BLOCK_SPECIAL
;
3133 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3135 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3136 This
->bigBlockDepotCount
);
3137 return STG_E_READFAULT
;
3141 * Cache the currently accessed depot block.
3143 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3145 This
->indexBlockDepotCached
= depotBlockCount
;
3147 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3149 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3154 * We have to look in the extended depot.
3156 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3159 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3162 return STG_E_READFAULT
;
3164 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3166 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3167 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3171 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3176 /******************************************************************************
3177 * Storage32Impl_GetNextExtendedBlock
3179 * Given an extended block this method will return the next extended block.
3182 * The last ULONG of an extended block is the block index of the next
3183 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3187 * - The index of the next extended block
3188 * - BLOCK_UNUSED: there is no next extended block.
3189 * - Any other return values denotes failure.
3191 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3193 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3194 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3196 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3199 return nextBlockIndex
;
3202 /******************************************************************************
3203 * Storage32Impl_SetNextBlockInChain
3205 * This method will write the index of the specified block's next block
3206 * in the big block depot.
3208 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3211 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3212 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3213 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3216 static void StorageImpl_SetNextBlockInChain(
3221 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3222 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3223 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3224 ULONG depotBlockIndexPos
;
3226 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3227 assert(blockIndex
!= nextBlock
);
3229 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3231 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3236 * We have to look in the extended depot.
3238 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3241 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3244 * Update the cached block depot, if necessary.
3246 if (depotBlockCount
== This
->indexBlockDepotCached
)
3248 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3252 /******************************************************************************
3253 * Storage32Impl_LoadFileHeader
3255 * This method will read in the file header, i.e. big block index -1.
3257 static HRESULT
StorageImpl_LoadFileHeader(
3260 HRESULT hr
= STG_E_FILENOTFOUND
;
3261 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3267 * Get a pointer to the big block of data containing the header.
3269 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3272 * Extract the information from the header.
3277 * Check for the "magic number" signature and return an error if it is not
3280 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3282 return STG_E_OLDFORMAT
;
3285 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3287 return STG_E_INVALIDHEADER
;
3290 StorageUtl_ReadWord(
3292 OFFSET_BIGBLOCKSIZEBITS
,
3293 &This
->bigBlockSizeBits
);
3295 StorageUtl_ReadWord(
3297 OFFSET_SMALLBLOCKSIZEBITS
,
3298 &This
->smallBlockSizeBits
);
3300 StorageUtl_ReadDWord(
3302 OFFSET_BBDEPOTCOUNT
,
3303 &This
->bigBlockDepotCount
);
3305 StorageUtl_ReadDWord(
3307 OFFSET_ROOTSTARTBLOCK
,
3308 &This
->rootStartBlock
);
3310 StorageUtl_ReadDWord(
3312 OFFSET_SBDEPOTSTART
,
3313 &This
->smallBlockDepotStart
);
3315 StorageUtl_ReadDWord(
3317 OFFSET_EXTBBDEPOTSTART
,
3318 &This
->extBigBlockDepotStart
);
3320 StorageUtl_ReadDWord(
3322 OFFSET_EXTBBDEPOTCOUNT
,
3323 &This
->extBigBlockDepotCount
);
3325 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3327 StorageUtl_ReadDWord(
3329 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3330 &(This
->bigBlockDepotStart
[index
]));
3334 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3336 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3337 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3340 * Right now, the code is making some assumptions about the size of the
3341 * blocks, just make sure they are what we're expecting.
3343 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3344 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3346 WARN("Broken OLE storage file\n");
3347 hr
= STG_E_INVALIDHEADER
;
3356 /******************************************************************************
3357 * Storage32Impl_SaveFileHeader
3359 * This method will save to the file the header, i.e. big block -1.
3361 static void StorageImpl_SaveFileHeader(
3364 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3369 * Get a pointer to the big block of data containing the header.
3371 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3374 * If the block read failed, the file is probably new.
3379 * Initialize for all unknown fields.
3381 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3384 * Initialize the magic number.
3386 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3389 * And a bunch of things we don't know what they mean
3391 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3392 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3393 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3394 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3398 * Write the information to the header.
3400 StorageUtl_WriteWord(
3402 OFFSET_BIGBLOCKSIZEBITS
,
3403 This
->bigBlockSizeBits
);
3405 StorageUtl_WriteWord(
3407 OFFSET_SMALLBLOCKSIZEBITS
,
3408 This
->smallBlockSizeBits
);
3410 StorageUtl_WriteDWord(
3412 OFFSET_BBDEPOTCOUNT
,
3413 This
->bigBlockDepotCount
);
3415 StorageUtl_WriteDWord(
3417 OFFSET_ROOTSTARTBLOCK
,
3418 This
->rootStartBlock
);
3420 StorageUtl_WriteDWord(
3422 OFFSET_SBDEPOTSTART
,
3423 This
->smallBlockDepotStart
);
3425 StorageUtl_WriteDWord(
3427 OFFSET_SBDEPOTCOUNT
,
3428 This
->smallBlockDepotChain
?
3429 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3431 StorageUtl_WriteDWord(
3433 OFFSET_EXTBBDEPOTSTART
,
3434 This
->extBigBlockDepotStart
);
3436 StorageUtl_WriteDWord(
3438 OFFSET_EXTBBDEPOTCOUNT
,
3439 This
->extBigBlockDepotCount
);
3441 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3443 StorageUtl_WriteDWord(
3445 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3446 (This
->bigBlockDepotStart
[index
]));
3450 * Write the big block back to the file.
3452 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3455 /******************************************************************************
3456 * StorageImpl_ReadRawDirEntry
3458 * This method will read the raw data from a directory entry in the file.
3460 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3462 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3464 ULARGE_INTEGER offset
;
3468 offset
.u
.HighPart
= 0;
3469 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3471 hr
= BlockChainStream_ReadAt(
3472 This
->rootBlockChain
,
3481 /******************************************************************************
3482 * StorageImpl_WriteRawDirEntry
3484 * This method will write the raw data from a directory entry in the file.
3486 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3488 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3490 ULARGE_INTEGER offset
;
3494 offset
.u
.HighPart
= 0;
3495 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3497 hr
= BlockChainStream_WriteAt(
3498 This
->rootBlockChain
,
3507 /******************************************************************************
3510 * Update raw directory entry data from the fields in newData.
3512 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3514 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3516 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3519 buffer
+ OFFSET_PS_NAME
,
3521 DIRENTRY_NAME_BUFFER_LEN
);
3523 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3525 StorageUtl_WriteWord(
3527 OFFSET_PS_NAMELENGTH
,
3528 newData
->sizeOfNameString
);
3530 StorageUtl_WriteDWord(
3532 OFFSET_PS_LEFTCHILD
,
3533 newData
->leftChild
);
3535 StorageUtl_WriteDWord(
3537 OFFSET_PS_RIGHTCHILD
,
3538 newData
->rightChild
);
3540 StorageUtl_WriteDWord(
3543 newData
->dirRootEntry
);
3545 StorageUtl_WriteGUID(
3550 StorageUtl_WriteDWord(
3553 newData
->ctime
.dwLowDateTime
);
3555 StorageUtl_WriteDWord(
3557 OFFSET_PS_CTIMEHIGH
,
3558 newData
->ctime
.dwHighDateTime
);
3560 StorageUtl_WriteDWord(
3563 newData
->mtime
.dwLowDateTime
);
3565 StorageUtl_WriteDWord(
3567 OFFSET_PS_MTIMEHIGH
,
3568 newData
->ctime
.dwHighDateTime
);
3570 StorageUtl_WriteDWord(
3572 OFFSET_PS_STARTBLOCK
,
3573 newData
->startingBlock
);
3575 StorageUtl_WriteDWord(
3578 newData
->size
.u
.LowPart
);
3581 /******************************************************************************
3582 * Storage32Impl_ReadDirEntry
3584 * This method will read the specified directory entry.
3586 HRESULT
StorageImpl_ReadDirEntry(
3591 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3594 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3596 if (SUCCEEDED(readRes
))
3598 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3601 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3602 DIRENTRY_NAME_BUFFER_LEN
);
3603 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3605 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3607 StorageUtl_ReadWord(
3609 OFFSET_PS_NAMELENGTH
,
3610 &buffer
->sizeOfNameString
);
3612 StorageUtl_ReadDWord(
3614 OFFSET_PS_LEFTCHILD
,
3615 &buffer
->leftChild
);
3617 StorageUtl_ReadDWord(
3619 OFFSET_PS_RIGHTCHILD
,
3620 &buffer
->rightChild
);
3622 StorageUtl_ReadDWord(
3625 &buffer
->dirRootEntry
);
3627 StorageUtl_ReadGUID(
3632 StorageUtl_ReadDWord(
3635 &buffer
->ctime
.dwLowDateTime
);
3637 StorageUtl_ReadDWord(
3639 OFFSET_PS_CTIMEHIGH
,
3640 &buffer
->ctime
.dwHighDateTime
);
3642 StorageUtl_ReadDWord(
3645 &buffer
->mtime
.dwLowDateTime
);
3647 StorageUtl_ReadDWord(
3649 OFFSET_PS_MTIMEHIGH
,
3650 &buffer
->mtime
.dwHighDateTime
);
3652 StorageUtl_ReadDWord(
3654 OFFSET_PS_STARTBLOCK
,
3655 &buffer
->startingBlock
);
3657 StorageUtl_ReadDWord(
3660 &buffer
->size
.u
.LowPart
);
3662 buffer
->size
.u
.HighPart
= 0;
3668 /*********************************************************************
3669 * Write the specified directory entry to the file
3671 HRESULT
StorageImpl_WriteDirEntry(
3674 const DirEntry
* buffer
)
3676 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3679 UpdateRawDirEntry(currentEntry
, buffer
);
3681 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3685 static BOOL
StorageImpl_ReadBigBlock(
3690 ULARGE_INTEGER ulOffset
;
3693 ulOffset
.u
.HighPart
= 0;
3694 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3696 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3697 return (read
== This
->bigBlockSize
);
3700 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3706 ULARGE_INTEGER ulOffset
;
3710 ulOffset
.u
.HighPart
= 0;
3711 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3712 ulOffset
.u
.LowPart
+= offset
;
3714 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3715 *value
= lendian32toh(tmp
);
3716 return (read
== sizeof(DWORD
));
3719 static BOOL
StorageImpl_WriteBigBlock(
3724 ULARGE_INTEGER ulOffset
;
3727 ulOffset
.u
.HighPart
= 0;
3728 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3730 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3731 return (wrote
== This
->bigBlockSize
);
3734 static BOOL
StorageImpl_WriteDWordToBigBlock(
3740 ULARGE_INTEGER ulOffset
;
3743 ulOffset
.u
.HighPart
= 0;
3744 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3745 ulOffset
.u
.LowPart
+= offset
;
3747 value
= htole32(value
);
3748 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3749 return (wrote
== sizeof(DWORD
));
3752 /******************************************************************************
3753 * Storage32Impl_SmallBlocksToBigBlocks
3755 * This method will convert a small block chain to a big block chain.
3756 * The small block chain will be destroyed.
3758 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3760 SmallBlockChainStream
** ppsbChain
)
3762 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3763 ULARGE_INTEGER size
, offset
;
3764 ULONG cbRead
, cbWritten
;
3765 ULARGE_INTEGER cbTotalRead
;
3766 DirRef streamEntryRef
;
3767 HRESULT resWrite
= S_OK
;
3769 DirEntry streamEntry
;
3771 BlockChainStream
*bbTempChain
= NULL
;
3772 BlockChainStream
*bigBlockChain
= NULL
;
3775 * Create a temporary big block chain that doesn't have
3776 * an associated directory entry. This temporary chain will be
3777 * used to copy data from small blocks to big blocks.
3779 bbTempChain
= BlockChainStream_Construct(This
,
3782 if(!bbTempChain
) return NULL
;
3784 * Grow the big block chain.
3786 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3787 BlockChainStream_SetSize(bbTempChain
, size
);
3790 * Copy the contents of the small block chain to the big block chain
3791 * by small block size increments.
3793 offset
.u
.LowPart
= 0;
3794 offset
.u
.HighPart
= 0;
3795 cbTotalRead
.QuadPart
= 0;
3797 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3800 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3802 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3805 if (FAILED(resRead
))
3810 cbTotalRead
.QuadPart
+= cbRead
;
3812 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3818 if (FAILED(resWrite
))
3821 offset
.u
.LowPart
+= cbRead
;
3823 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3824 HeapFree(GetProcessHeap(),0,buffer
);
3826 size
.u
.HighPart
= 0;
3829 if (FAILED(resRead
) || FAILED(resWrite
))
3831 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3832 BlockChainStream_SetSize(bbTempChain
, size
);
3833 BlockChainStream_Destroy(bbTempChain
);
3838 * Destroy the small block chain.
3840 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3841 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3842 SmallBlockChainStream_Destroy(*ppsbChain
);
3846 * Change the directory entry. This chain is now a big block chain
3847 * and it doesn't reside in the small blocks chain anymore.
3849 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3851 streamEntry
.startingBlock
= bbHeadOfChain
;
3853 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3856 * Destroy the temporary entryless big block chain.
3857 * Create a new big block chain associated with this entry.
3859 BlockChainStream_Destroy(bbTempChain
);
3860 bigBlockChain
= BlockChainStream_Construct(This
,
3864 return bigBlockChain
;
3867 /******************************************************************************
3868 * Storage32Impl_BigBlocksToSmallBlocks
3870 * This method will convert a big block chain to a small block chain.
3871 * The big block chain will be destroyed on success.
3873 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3875 BlockChainStream
** ppbbChain
)
3877 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3878 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3879 DirRef streamEntryRef
;
3880 HRESULT resWrite
= S_OK
, resRead
;
3881 DirEntry streamEntry
;
3883 SmallBlockChainStream
* sbTempChain
;
3885 TRACE("%p %p\n", This
, ppbbChain
);
3887 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3893 size
= BlockChainStream_GetSize(*ppbbChain
);
3894 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3896 offset
.u
.HighPart
= 0;
3897 offset
.u
.LowPart
= 0;
3898 cbTotalRead
.QuadPart
= 0;
3899 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3902 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3903 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3911 cbTotalRead
.QuadPart
+= cbRead
;
3913 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3914 cbRead
, buffer
, &cbWritten
);
3916 if(FAILED(resWrite
))
3919 offset
.u
.LowPart
+= cbRead
;
3921 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3922 HeapFree(GetProcessHeap(), 0, buffer
);
3924 size
.u
.HighPart
= 0;
3927 if(FAILED(resRead
) || FAILED(resWrite
))
3929 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3930 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3931 SmallBlockChainStream_Destroy(sbTempChain
);
3935 /* destroy the original big block chain */
3936 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3937 BlockChainStream_SetSize(*ppbbChain
, size
);
3938 BlockChainStream_Destroy(*ppbbChain
);
3941 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3942 streamEntry
.startingBlock
= sbHeadOfChain
;
3943 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3945 SmallBlockChainStream_Destroy(sbTempChain
);
3946 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3949 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3952 DirEntry parentData
, snapshotData
;
3954 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3955 0, (IStorage
**)snapshot
);
3959 hr
= StorageBaseImpl_ReadDirEntry(original
,
3960 original
->storageDirEntry
, &parentData
);
3963 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3964 (*snapshot
)->storageDirEntry
, &snapshotData
);
3968 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3969 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3970 snapshotData
.stgType
= parentData
.stgType
;
3971 snapshotData
.clsid
= parentData
.clsid
;
3972 snapshotData
.ctime
= parentData
.ctime
;
3973 snapshotData
.mtime
= parentData
.mtime
;
3974 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3975 (*snapshot
)->storageDirEntry
, &snapshotData
);
3979 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
3980 (IStorage
*)(*snapshot
));
3982 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
3988 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
3990 DWORD grfCommitFlags
) /* [in] */
3992 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
3994 DirEntry data
, tempStorageData
, snapshotRootData
;
3995 DirRef tempStorageEntry
, oldDirRoot
;
3996 StorageInternalImpl
*tempStorage
;
3998 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4000 /* Cannot commit a read-only transacted storage */
4001 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4002 return STG_E_ACCESSDENIED
;
4004 /* To prevent data loss, we create the new structure in the file before we
4005 * delete the old one, so that in case of errors the old data is intact. We
4006 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4007 * needed in the rare situation where we have just enough free disk space to
4008 * overwrite the existing data. */
4010 /* Create an orphaned storage in the parent for the new directory structure. */
4011 memset(&data
, 0, sizeof(data
));
4013 data
.sizeOfNameString
= 1;
4014 data
.stgType
= STGTY_STORAGE
;
4015 data
.leftChild
= DIRENTRY_NULL
;
4016 data
.rightChild
= DIRENTRY_NULL
;
4017 data
.dirRootEntry
= DIRENTRY_NULL
;
4018 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4020 if (FAILED(hr
)) return hr
;
4022 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4023 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4026 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4027 (IStorage
*)tempStorage
);
4029 list_init(&tempStorage
->ParentListEntry
);
4031 IStorage_Release((IStorage
*) tempStorage
);
4038 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4042 /* Update the storage to use the new data in one step. */
4043 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4044 This
->transactedParent
->storageDirEntry
, &data
);
4048 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4049 tempStorageEntry
, &tempStorageData
);
4054 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4055 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4060 oldDirRoot
= data
.dirRootEntry
;
4061 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4062 data
.clsid
= snapshotRootData
.clsid
;
4063 data
.ctime
= snapshotRootData
.ctime
;
4064 data
.mtime
= snapshotRootData
.mtime
;
4066 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4067 This
->transactedParent
->storageDirEntry
, &data
);
4072 /* Destroy the old now-orphaned data. */
4073 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4074 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4078 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4084 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4087 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4088 StorageBaseImpl
*newSnapshot
;
4091 TRACE("(%p)\n", iface
);
4093 /* Create a new copy of the parent data. */
4094 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4095 if (FAILED(hr
)) return hr
;
4097 /* Destroy the open objects. */
4098 StorageBaseImpl_DeleteAll(&This
->base
);
4100 /* Replace our current snapshot. */
4101 IStorage_Release((IStorage
*)This
->snapshot
);
4102 This
->snapshot
= newSnapshot
;
4107 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4109 if (!This
->reverted
)
4111 TRACE("Storage invalidated (stg=%p)\n", This
);
4115 StorageBaseImpl_DeleteAll(This
);
4119 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4121 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4123 TransactedSnapshotImpl_Invalidate(iface
);
4125 IStorage_Release((IStorage
*)This
->transactedParent
);
4127 IStorage_Release((IStorage
*)This
->snapshot
);
4129 HeapFree(GetProcessHeap(), 0, This
);
4132 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4133 const DirEntry
*newData
, DirRef
*index
)
4135 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4137 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4141 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4142 DirRef index
, const DirEntry
*data
)
4144 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4146 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4150 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4151 DirRef index
, DirEntry
*data
)
4153 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4155 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4159 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4162 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4164 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4168 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4169 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4171 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4173 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4174 index
, offset
, size
, buffer
, bytesRead
);
4177 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4178 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4180 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4182 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4183 index
, offset
, size
, buffer
, bytesWritten
);
4186 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4187 DirRef index
, ULARGE_INTEGER newsize
)
4189 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4191 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4195 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4197 StorageBaseImpl_QueryInterface
,
4198 StorageBaseImpl_AddRef
,
4199 StorageBaseImpl_Release
,
4200 StorageBaseImpl_CreateStream
,
4201 StorageBaseImpl_OpenStream
,
4202 StorageBaseImpl_CreateStorage
,
4203 StorageBaseImpl_OpenStorage
,
4204 StorageBaseImpl_CopyTo
,
4205 StorageBaseImpl_MoveElementTo
,
4206 TransactedSnapshotImpl_Commit
,
4207 TransactedSnapshotImpl_Revert
,
4208 StorageBaseImpl_EnumElements
,
4209 StorageBaseImpl_DestroyElement
,
4210 StorageBaseImpl_RenameElement
,
4211 StorageBaseImpl_SetElementTimes
,
4212 StorageBaseImpl_SetClass
,
4213 StorageBaseImpl_SetStateBits
,
4214 StorageBaseImpl_Stat
4217 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4219 TransactedSnapshotImpl_Destroy
,
4220 TransactedSnapshotImpl_Invalidate
,
4221 TransactedSnapshotImpl_CreateDirEntry
,
4222 TransactedSnapshotImpl_WriteDirEntry
,
4223 TransactedSnapshotImpl_ReadDirEntry
,
4224 TransactedSnapshotImpl_DestroyDirEntry
,
4225 TransactedSnapshotImpl_StreamReadAt
,
4226 TransactedSnapshotImpl_StreamWriteAt
,
4227 TransactedSnapshotImpl_StreamSetSize
4230 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4231 TransactedSnapshotImpl
** result
)
4235 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4238 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4240 /* This is OK because the property set storage functions use the IStorage functions. */
4241 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4243 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4245 list_init(&(*result
)->base
.strmHead
);
4247 list_init(&(*result
)->base
.storageHead
);
4249 (*result
)->base
.ref
= 1;
4251 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4253 (*result
)->base
.filename
= parentStorage
->filename
;
4255 /* Create a new temporary storage to act as the snapshot */
4256 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4260 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4262 /* parentStorage already has 1 reference, which we take over here. */
4263 (*result
)->transactedParent
= parentStorage
;
4265 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4268 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4273 return E_OUTOFMEMORY
;
4276 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4277 StorageBaseImpl
** result
)
4281 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4283 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4286 return TransactedSnapshotImpl_Construct(parentStorage
,
4287 (TransactedSnapshotImpl
**)result
);
4290 static HRESULT
Storage_Construct(
4297 StorageBaseImpl
** result
)
4299 StorageImpl
*newStorage
;
4300 StorageBaseImpl
*newTransactedStorage
;
4303 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4304 if (FAILED(hr
)) goto end
;
4306 if (openFlags
& STGM_TRANSACTED
)
4308 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4310 IStorage_Release((IStorage
*)newStorage
);
4312 *result
= newTransactedStorage
;
4315 *result
= &newStorage
->base
;
4321 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4323 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4325 if (!This
->base
.reverted
)
4327 TRACE("Storage invalidated (stg=%p)\n", This
);
4329 This
->base
.reverted
= 1;
4331 This
->parentStorage
= NULL
;
4333 StorageBaseImpl_DeleteAll(&This
->base
);
4335 list_remove(&This
->ParentListEntry
);
4339 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4341 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4343 StorageInternalImpl_Invalidate(&This
->base
);
4345 HeapFree(GetProcessHeap(), 0, This
);
4348 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4349 const DirEntry
*newData
, DirRef
*index
)
4351 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4353 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4357 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4358 DirRef index
, const DirEntry
*data
)
4360 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4362 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4366 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4367 DirRef index
, DirEntry
*data
)
4369 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4371 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4375 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4378 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4380 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4384 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4385 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4387 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4389 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4390 index
, offset
, size
, buffer
, bytesRead
);
4393 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4394 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4396 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4398 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4399 index
, offset
, size
, buffer
, bytesWritten
);
4402 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4403 DirRef index
, ULARGE_INTEGER newsize
)
4405 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4407 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4411 /******************************************************************************
4413 ** Storage32InternalImpl_Commit
4416 static HRESULT WINAPI
StorageInternalImpl_Commit(
4418 DWORD grfCommitFlags
) /* [in] */
4420 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4424 /******************************************************************************
4426 ** Storage32InternalImpl_Revert
4429 static HRESULT WINAPI
StorageInternalImpl_Revert(
4432 FIXME("(%p): stub\n", iface
);
4436 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4438 IStorage_Release((IStorage
*)This
->parentStorage
);
4439 HeapFree(GetProcessHeap(), 0, This
);
4442 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4443 IEnumSTATSTG
* iface
,
4447 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4450 return E_INVALIDARG
;
4454 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4455 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4458 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4462 return E_NOINTERFACE
;
4465 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4466 IEnumSTATSTG
* iface
)
4468 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4469 return InterlockedIncrement(&This
->ref
);
4472 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4473 IEnumSTATSTG
* iface
)
4475 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4479 newRef
= InterlockedDecrement(&This
->ref
);
4483 IEnumSTATSTGImpl_Destroy(This
);
4489 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4490 IEnumSTATSTGImpl
* This
,
4493 DirRef result
= DIRENTRY_NULL
;
4497 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4499 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4500 This
->parentStorage
->storageDirEntry
, &entry
);
4501 searchNode
= entry
.dirRootEntry
;
4503 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4505 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4509 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4513 searchNode
= entry
.rightChild
;
4517 result
= searchNode
;
4518 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4519 searchNode
= entry
.leftChild
;
4527 if (result
!= DIRENTRY_NULL
)
4528 memcpy(This
->name
, result_name
, sizeof(result_name
));
4534 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4535 IEnumSTATSTG
* iface
,
4538 ULONG
* pceltFetched
)
4540 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4542 DirEntry currentEntry
;
4543 STATSTG
* currentReturnStruct
= rgelt
;
4544 ULONG objectFetched
= 0;
4545 DirRef currentSearchNode
;
4548 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4549 return E_INVALIDARG
;
4551 if (This
->parentStorage
->reverted
)
4552 return STG_E_REVERTED
;
4555 * To avoid the special case, get another pointer to a ULONG value if
4556 * the caller didn't supply one.
4558 if (pceltFetched
==0)
4559 pceltFetched
= &objectFetched
;
4562 * Start the iteration, we will iterate until we hit the end of the
4563 * linked list or until we hit the number of items to iterate through
4567 while ( *pceltFetched
< celt
)
4569 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4571 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4575 * Read the entry from the storage.
4577 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4582 * Copy the information to the return buffer.
4584 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4585 currentReturnStruct
,
4590 * Step to the next item in the iteration
4593 currentReturnStruct
++;
4596 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4603 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4604 IEnumSTATSTG
* iface
,
4607 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4609 ULONG objectFetched
= 0;
4610 DirRef currentSearchNode
;
4613 if (This
->parentStorage
->reverted
)
4614 return STG_E_REVERTED
;
4616 while ( (objectFetched
< celt
) )
4618 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4620 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4626 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4632 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4633 IEnumSTATSTG
* iface
)
4635 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4637 if (This
->parentStorage
->reverted
)
4638 return STG_E_REVERTED
;
4645 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4646 IEnumSTATSTG
* iface
,
4647 IEnumSTATSTG
** ppenum
)
4649 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4651 IEnumSTATSTGImpl
* newClone
;
4653 if (This
->parentStorage
->reverted
)
4654 return STG_E_REVERTED
;
4657 * Perform a sanity check on the parameters.
4660 return E_INVALIDARG
;
4662 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4663 This
->storageDirEntry
);
4667 * The new clone enumeration must point to the same current node as
4670 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4672 *ppenum
= (IEnumSTATSTG
*)newClone
;
4675 * Don't forget to nail down a reference to the clone before
4678 IEnumSTATSTGImpl_AddRef(*ppenum
);
4684 * Virtual function table for the IEnumSTATSTGImpl class.
4686 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4688 IEnumSTATSTGImpl_QueryInterface
,
4689 IEnumSTATSTGImpl_AddRef
,
4690 IEnumSTATSTGImpl_Release
,
4691 IEnumSTATSTGImpl_Next
,
4692 IEnumSTATSTGImpl_Skip
,
4693 IEnumSTATSTGImpl_Reset
,
4694 IEnumSTATSTGImpl_Clone
4697 /******************************************************************************
4698 ** IEnumSTATSTGImpl implementation
4701 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4702 StorageBaseImpl
* parentStorage
,
4703 DirRef storageDirEntry
)
4705 IEnumSTATSTGImpl
* newEnumeration
;
4707 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4709 if (newEnumeration
!=0)
4712 * Set-up the virtual function table and reference count.
4714 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4715 newEnumeration
->ref
= 0;
4718 * We want to nail-down the reference to the storage in case the
4719 * enumeration out-lives the storage in the client application.
4721 newEnumeration
->parentStorage
= parentStorage
;
4722 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4724 newEnumeration
->storageDirEntry
= storageDirEntry
;
4727 * Make sure the current node of the iterator is the first one.
4729 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4732 return newEnumeration
;
4736 * Virtual function table for the Storage32InternalImpl class.
4738 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4740 StorageBaseImpl_QueryInterface
,
4741 StorageBaseImpl_AddRef
,
4742 StorageBaseImpl_Release
,
4743 StorageBaseImpl_CreateStream
,
4744 StorageBaseImpl_OpenStream
,
4745 StorageBaseImpl_CreateStorage
,
4746 StorageBaseImpl_OpenStorage
,
4747 StorageBaseImpl_CopyTo
,
4748 StorageBaseImpl_MoveElementTo
,
4749 StorageInternalImpl_Commit
,
4750 StorageInternalImpl_Revert
,
4751 StorageBaseImpl_EnumElements
,
4752 StorageBaseImpl_DestroyElement
,
4753 StorageBaseImpl_RenameElement
,
4754 StorageBaseImpl_SetElementTimes
,
4755 StorageBaseImpl_SetClass
,
4756 StorageBaseImpl_SetStateBits
,
4757 StorageBaseImpl_Stat
4760 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4762 StorageInternalImpl_Destroy
,
4763 StorageInternalImpl_Invalidate
,
4764 StorageInternalImpl_CreateDirEntry
,
4765 StorageInternalImpl_WriteDirEntry
,
4766 StorageInternalImpl_ReadDirEntry
,
4767 StorageInternalImpl_DestroyDirEntry
,
4768 StorageInternalImpl_StreamReadAt
,
4769 StorageInternalImpl_StreamWriteAt
,
4770 StorageInternalImpl_StreamSetSize
4773 /******************************************************************************
4774 ** Storage32InternalImpl implementation
4777 static StorageInternalImpl
* StorageInternalImpl_Construct(
4778 StorageBaseImpl
* parentStorage
,
4780 DirRef storageDirEntry
)
4782 StorageInternalImpl
* newStorage
;
4784 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4788 list_init(&newStorage
->base
.strmHead
);
4790 list_init(&newStorage
->base
.storageHead
);
4793 * Initialize the virtual function table.
4795 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4796 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4797 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4799 newStorage
->base
.reverted
= 0;
4801 newStorage
->base
.ref
= 1;
4803 newStorage
->parentStorage
= parentStorage
;
4806 * Keep a reference to the directory entry of this storage
4808 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4810 newStorage
->base
.create
= 0;
4818 /******************************************************************************
4819 ** StorageUtl implementation
4822 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4826 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4827 *value
= lendian16toh(tmp
);
4830 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4832 value
= htole16(value
);
4833 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4836 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4840 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4841 *value
= lendian32toh(tmp
);
4844 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4846 value
= htole32(value
);
4847 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4850 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4851 ULARGE_INTEGER
* value
)
4853 #ifdef WORDS_BIGENDIAN
4856 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4857 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4858 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4860 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4864 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4865 const ULARGE_INTEGER
*value
)
4867 #ifdef WORDS_BIGENDIAN
4870 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4871 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4872 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4874 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4878 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4880 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4881 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4882 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4884 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4887 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4889 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4890 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4891 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4893 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4896 void StorageUtl_CopyDirEntryToSTATSTG(
4897 StorageBaseImpl
* storage
,
4898 STATSTG
* destination
,
4899 const DirEntry
* source
,
4904 if (source
->stgType
== STGTY_ROOT
)
4906 /* replace the name of root entry (often "Root Entry") by the file name */
4907 entryName
= storage
->filename
;
4911 entryName
= source
->name
;
4915 * The copy of the string occurs only when the flag is not set
4917 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4918 (entryName
== NULL
) ||
4919 (entryName
[0] == 0) )
4921 destination
->pwcsName
= 0;
4925 destination
->pwcsName
=
4926 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4928 strcpyW(destination
->pwcsName
, entryName
);
4931 switch (source
->stgType
)
4935 destination
->type
= STGTY_STORAGE
;
4938 destination
->type
= STGTY_STREAM
;
4941 destination
->type
= STGTY_STREAM
;
4945 destination
->cbSize
= source
->size
;
4947 currentReturnStruct->mtime = {0}; TODO
4948 currentReturnStruct->ctime = {0};
4949 currentReturnStruct->atime = {0};
4951 destination
->grfMode
= 0;
4952 destination
->grfLocksSupported
= 0;
4953 destination
->clsid
= source
->clsid
;
4954 destination
->grfStateBits
= 0;
4955 destination
->reserved
= 0;
4958 /******************************************************************************
4959 ** BlockChainStream implementation
4962 BlockChainStream
* BlockChainStream_Construct(
4963 StorageImpl
* parentStorage
,
4964 ULONG
* headOfStreamPlaceHolder
,
4967 BlockChainStream
* newStream
;
4970 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4972 newStream
->parentStorage
= parentStorage
;
4973 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4974 newStream
->ownerDirEntry
= dirEntry
;
4975 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4976 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4977 newStream
->numBlocks
= 0;
4979 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4981 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4983 newStream
->numBlocks
++;
4984 newStream
->tailIndex
= blockIndex
;
4986 if(FAILED(StorageImpl_GetNextBlockInChain(
4991 HeapFree(GetProcessHeap(), 0, newStream
);
4999 void BlockChainStream_Destroy(BlockChainStream
* This
)
5001 HeapFree(GetProcessHeap(), 0, This
);
5004 /******************************************************************************
5005 * BlockChainStream_GetHeadOfChain
5007 * Returns the head of this stream chain.
5008 * Some special chains don't have directory entries, their heads are kept in
5009 * This->headOfStreamPlaceHolder.
5012 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5014 DirEntry chainEntry
;
5017 if (This
->headOfStreamPlaceHolder
!= 0)
5018 return *(This
->headOfStreamPlaceHolder
);
5020 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5022 hr
= StorageImpl_ReadDirEntry(
5023 This
->parentStorage
,
5024 This
->ownerDirEntry
,
5029 return chainEntry
.startingBlock
;
5033 return BLOCK_END_OF_CHAIN
;
5036 /******************************************************************************
5037 * BlockChainStream_GetCount
5039 * Returns the number of blocks that comprises this chain.
5040 * This is not the size of the stream as the last block may not be full!
5043 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5048 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5050 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5054 if(FAILED(StorageImpl_GetNextBlockInChain(
5055 This
->parentStorage
,
5064 /******************************************************************************
5065 * BlockChainStream_ReadAt
5067 * Reads a specified number of bytes from this chain at the specified offset.
5068 * bytesRead may be NULL.
5069 * Failure will be returned if the specified number of bytes has not been read.
5071 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5072 ULARGE_INTEGER offset
,
5077 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5078 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5079 ULONG bytesToReadInBuffer
;
5083 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5086 * Find the first block in the stream that contains part of the buffer.
5088 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5089 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5090 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5092 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5093 This
->lastBlockNoInSequence
= blockNoInSequence
;
5097 ULONG temp
= blockNoInSequence
;
5099 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5100 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5101 This
->lastBlockNoInSequence
= temp
;
5104 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5106 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5107 return STG_E_DOCFILECORRUPT
;
5108 blockNoInSequence
--;
5111 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5112 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5114 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5117 * Start reading the buffer.
5120 bufferWalker
= buffer
;
5122 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5124 ULARGE_INTEGER ulOffset
;
5127 * Calculate how many bytes we can copy from this big block.
5129 bytesToReadInBuffer
=
5130 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5132 TRACE("block %i\n",blockIndex
);
5133 ulOffset
.u
.HighPart
= 0;
5134 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5137 StorageImpl_ReadAt(This
->parentStorage
,
5140 bytesToReadInBuffer
,
5143 * Step to the next big block.
5145 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5146 return STG_E_DOCFILECORRUPT
;
5148 bufferWalker
+= bytesReadAt
;
5149 size
-= bytesReadAt
;
5150 *bytesRead
+= bytesReadAt
;
5151 offsetInBlock
= 0; /* There is no offset on the next block */
5153 if (bytesToReadInBuffer
!= bytesReadAt
)
5157 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5160 /******************************************************************************
5161 * BlockChainStream_WriteAt
5163 * Writes the specified number of bytes to this chain at the specified offset.
5164 * Will fail if not all specified number of bytes have been written.
5166 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5167 ULARGE_INTEGER offset
,
5170 ULONG
* bytesWritten
)
5172 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5173 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5176 const BYTE
* bufferWalker
;
5179 * Find the first block in the stream that contains part of the buffer.
5181 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5182 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5183 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5185 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5186 This
->lastBlockNoInSequence
= blockNoInSequence
;
5190 ULONG temp
= blockNoInSequence
;
5192 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5193 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5194 This
->lastBlockNoInSequence
= temp
;
5197 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5199 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5201 return STG_E_DOCFILECORRUPT
;
5202 blockNoInSequence
--;
5205 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5207 /* BlockChainStream_SetSize should have already been called to ensure we have
5208 * enough blocks in the chain to write into */
5209 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5211 ERR("not enough blocks in chain to write data\n");
5212 return STG_E_DOCFILECORRUPT
;
5216 bufferWalker
= buffer
;
5218 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5220 ULARGE_INTEGER ulOffset
;
5221 DWORD bytesWrittenAt
;
5223 * Calculate how many bytes we can copy from this big block.
5226 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5228 TRACE("block %i\n",blockIndex
);
5229 ulOffset
.u
.HighPart
= 0;
5230 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5233 StorageImpl_WriteAt(This
->parentStorage
,
5240 * Step to the next big block.
5242 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5244 return STG_E_DOCFILECORRUPT
;
5246 bufferWalker
+= bytesWrittenAt
;
5247 size
-= bytesWrittenAt
;
5248 *bytesWritten
+= bytesWrittenAt
;
5249 offsetInBlock
= 0; /* There is no offset on the next block */
5251 if (bytesWrittenAt
!= bytesToWrite
)
5255 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5258 /******************************************************************************
5259 * BlockChainStream_Shrink
5261 * Shrinks this chain in the big block depot.
5263 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5264 ULARGE_INTEGER newSize
)
5266 ULONG blockIndex
, extraBlock
;
5271 * Reset the last accessed block cache.
5273 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5274 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5277 * Figure out how many blocks are needed to contain the new size
5279 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5281 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5284 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5287 * Go to the new end of chain
5289 while (count
< numBlocks
)
5291 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5297 /* Get the next block before marking the new end */
5298 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5302 /* Mark the new end of chain */
5303 StorageImpl_SetNextBlockInChain(
5304 This
->parentStorage
,
5306 BLOCK_END_OF_CHAIN
);
5308 This
->tailIndex
= blockIndex
;
5309 This
->numBlocks
= numBlocks
;
5312 * Mark the extra blocks as free
5314 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5316 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5319 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5320 extraBlock
= blockIndex
;
5326 /******************************************************************************
5327 * BlockChainStream_Enlarge
5329 * Grows this chain in the big block depot.
5331 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5332 ULARGE_INTEGER newSize
)
5334 ULONG blockIndex
, currentBlock
;
5336 ULONG oldNumBlocks
= 0;
5338 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5341 * Empty chain. Create the head.
5343 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5345 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5346 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5348 BLOCK_END_OF_CHAIN
);
5350 if (This
->headOfStreamPlaceHolder
!= 0)
5352 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5356 DirEntry chainEntry
;
5357 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5359 StorageImpl_ReadDirEntry(
5360 This
->parentStorage
,
5361 This
->ownerDirEntry
,
5364 chainEntry
.startingBlock
= blockIndex
;
5366 StorageImpl_WriteDirEntry(
5367 This
->parentStorage
,
5368 This
->ownerDirEntry
,
5372 This
->tailIndex
= blockIndex
;
5373 This
->numBlocks
= 1;
5377 * Figure out how many blocks are needed to contain this stream
5379 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5381 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5385 * Go to the current end of chain
5387 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5389 currentBlock
= blockIndex
;
5391 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5394 currentBlock
= blockIndex
;
5396 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5401 This
->tailIndex
= currentBlock
;
5404 currentBlock
= This
->tailIndex
;
5405 oldNumBlocks
= This
->numBlocks
;
5408 * Add new blocks to the chain
5410 if (oldNumBlocks
< newNumBlocks
)
5412 while (oldNumBlocks
< newNumBlocks
)
5414 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5416 StorageImpl_SetNextBlockInChain(
5417 This
->parentStorage
,
5421 StorageImpl_SetNextBlockInChain(
5422 This
->parentStorage
,
5424 BLOCK_END_OF_CHAIN
);
5426 currentBlock
= blockIndex
;
5430 This
->tailIndex
= blockIndex
;
5431 This
->numBlocks
= newNumBlocks
;
5437 /******************************************************************************
5438 * BlockChainStream_SetSize
5440 * Sets the size of this stream. The big block depot will be updated.
5441 * The file will grow if we grow the chain.
5443 * TODO: Free the actual blocks in the file when we shrink the chain.
5444 * Currently, the blocks are still in the file. So the file size
5445 * doesn't shrink even if we shrink streams.
5447 BOOL
BlockChainStream_SetSize(
5448 BlockChainStream
* This
,
5449 ULARGE_INTEGER newSize
)
5451 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5453 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5456 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5458 BlockChainStream_Shrink(This
, newSize
);
5462 BlockChainStream_Enlarge(This
, newSize
);
5468 /******************************************************************************
5469 * BlockChainStream_GetSize
5471 * Returns the size of this chain.
5472 * Will return the block count if this chain doesn't have a directory entry.
5474 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5476 DirEntry chainEntry
;
5478 if(This
->headOfStreamPlaceHolder
== NULL
)
5481 * This chain has a directory entry so use the size value from there.
5483 StorageImpl_ReadDirEntry(
5484 This
->parentStorage
,
5485 This
->ownerDirEntry
,
5488 return chainEntry
.size
;
5493 * this chain is a chain that does not have a directory entry, figure out the
5494 * size by making the product number of used blocks times the
5497 ULARGE_INTEGER result
;
5498 result
.u
.HighPart
= 0;
5501 BlockChainStream_GetCount(This
) *
5502 This
->parentStorage
->bigBlockSize
;
5508 /******************************************************************************
5509 ** SmallBlockChainStream implementation
5512 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5513 StorageImpl
* parentStorage
,
5514 ULONG
* headOfStreamPlaceHolder
,
5517 SmallBlockChainStream
* newStream
;
5519 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5521 newStream
->parentStorage
= parentStorage
;
5522 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5523 newStream
->ownerDirEntry
= dirEntry
;
5528 void SmallBlockChainStream_Destroy(
5529 SmallBlockChainStream
* This
)
5531 HeapFree(GetProcessHeap(), 0, This
);
5534 /******************************************************************************
5535 * SmallBlockChainStream_GetHeadOfChain
5537 * Returns the head of this chain of small blocks.
5539 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5540 SmallBlockChainStream
* This
)
5542 DirEntry chainEntry
;
5545 if (This
->headOfStreamPlaceHolder
!= NULL
)
5546 return *(This
->headOfStreamPlaceHolder
);
5548 if (This
->ownerDirEntry
)
5550 hr
= StorageImpl_ReadDirEntry(
5551 This
->parentStorage
,
5552 This
->ownerDirEntry
,
5557 return chainEntry
.startingBlock
;
5562 return BLOCK_END_OF_CHAIN
;
5565 /******************************************************************************
5566 * SmallBlockChainStream_GetNextBlockInChain
5568 * Returns the index of the next small block in this chain.
5571 * - BLOCK_END_OF_CHAIN: end of this chain
5572 * - BLOCK_UNUSED: small block 'blockIndex' is free
5574 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5575 SmallBlockChainStream
* This
,
5577 ULONG
* nextBlockInChain
)
5579 ULARGE_INTEGER offsetOfBlockInDepot
;
5584 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5586 offsetOfBlockInDepot
.u
.HighPart
= 0;
5587 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5590 * Read those bytes in the buffer from the small block file.
5592 res
= BlockChainStream_ReadAt(
5593 This
->parentStorage
->smallBlockDepotChain
,
5594 offsetOfBlockInDepot
,
5601 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5608 /******************************************************************************
5609 * SmallBlockChainStream_SetNextBlockInChain
5611 * Writes the index of the next block of the specified block in the small
5613 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5614 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5616 static void SmallBlockChainStream_SetNextBlockInChain(
5617 SmallBlockChainStream
* This
,
5621 ULARGE_INTEGER offsetOfBlockInDepot
;
5625 offsetOfBlockInDepot
.u
.HighPart
= 0;
5626 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5628 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5631 * Read those bytes in the buffer from the small block file.
5633 BlockChainStream_WriteAt(
5634 This
->parentStorage
->smallBlockDepotChain
,
5635 offsetOfBlockInDepot
,
5641 /******************************************************************************
5642 * SmallBlockChainStream_FreeBlock
5644 * Flag small block 'blockIndex' as free in the small block depot.
5646 static void SmallBlockChainStream_FreeBlock(
5647 SmallBlockChainStream
* This
,
5650 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5653 /******************************************************************************
5654 * SmallBlockChainStream_GetNextFreeBlock
5656 * Returns the index of a free small block. The small block depot will be
5657 * enlarged if necessary. The small block chain will also be enlarged if
5660 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5661 SmallBlockChainStream
* This
)
5663 ULARGE_INTEGER offsetOfBlockInDepot
;
5666 ULONG blockIndex
= 0;
5667 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5669 ULONG smallBlocksPerBigBlock
;
5671 offsetOfBlockInDepot
.u
.HighPart
= 0;
5674 * Scan the small block depot for a free block
5676 while (nextBlockIndex
!= BLOCK_UNUSED
)
5678 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5680 res
= BlockChainStream_ReadAt(
5681 This
->parentStorage
->smallBlockDepotChain
,
5682 offsetOfBlockInDepot
,
5688 * If we run out of space for the small block depot, enlarge it
5692 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5694 if (nextBlockIndex
!= BLOCK_UNUSED
)
5700 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5702 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5703 ULONG nextBlock
, newsbdIndex
;
5704 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5706 nextBlock
= sbdIndex
;
5707 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5709 sbdIndex
= nextBlock
;
5710 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5713 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5714 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5715 StorageImpl_SetNextBlockInChain(
5716 This
->parentStorage
,
5720 StorageImpl_SetNextBlockInChain(
5721 This
->parentStorage
,
5723 BLOCK_END_OF_CHAIN
);
5726 * Initialize all the small blocks to free
5728 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5729 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5734 * We have just created the small block depot.
5740 * Save it in the header
5742 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5743 StorageImpl_SaveFileHeader(This
->parentStorage
);
5746 * And allocate the first big block that will contain small blocks
5749 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5751 StorageImpl_SetNextBlockInChain(
5752 This
->parentStorage
,
5754 BLOCK_END_OF_CHAIN
);
5756 StorageImpl_ReadDirEntry(
5757 This
->parentStorage
,
5758 This
->parentStorage
->base
.storageDirEntry
,
5761 rootEntry
.startingBlock
= sbStartIndex
;
5762 rootEntry
.size
.u
.HighPart
= 0;
5763 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5765 StorageImpl_WriteDirEntry(
5766 This
->parentStorage
,
5767 This
->parentStorage
->base
.storageDirEntry
,
5771 StorageImpl_SaveFileHeader(This
->parentStorage
);
5775 smallBlocksPerBigBlock
=
5776 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5779 * Verify if we have to allocate big blocks to contain small blocks
5781 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5784 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5786 StorageImpl_ReadDirEntry(
5787 This
->parentStorage
,
5788 This
->parentStorage
->base
.storageDirEntry
,
5791 if (rootEntry
.size
.u
.LowPart
<
5792 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5794 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5796 BlockChainStream_SetSize(
5797 This
->parentStorage
->smallBlockRootChain
,
5800 StorageImpl_WriteDirEntry(
5801 This
->parentStorage
,
5802 This
->parentStorage
->base
.storageDirEntry
,
5810 /******************************************************************************
5811 * SmallBlockChainStream_ReadAt
5813 * Reads a specified number of bytes from this chain at the specified offset.
5814 * bytesRead may be NULL.
5815 * Failure will be returned if the specified number of bytes has not been read.
5817 HRESULT
SmallBlockChainStream_ReadAt(
5818 SmallBlockChainStream
* This
,
5819 ULARGE_INTEGER offset
,
5825 ULARGE_INTEGER offsetInBigBlockFile
;
5826 ULONG blockNoInSequence
=
5827 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5829 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5830 ULONG bytesToReadInBuffer
;
5832 ULONG bytesReadFromBigBlockFile
;
5836 * This should never happen on a small block file.
5838 assert(offset
.u
.HighPart
==0);
5841 * Find the first block in the stream that contains part of the buffer.
5843 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5845 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5847 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5850 blockNoInSequence
--;
5854 * Start reading the buffer.
5857 bufferWalker
= buffer
;
5859 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5862 * Calculate how many bytes we can copy from this small block.
5864 bytesToReadInBuffer
=
5865 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5868 * Calculate the offset of the small block in the small block file.
5870 offsetInBigBlockFile
.u
.HighPart
= 0;
5871 offsetInBigBlockFile
.u
.LowPart
=
5872 blockIndex
* This
->parentStorage
->smallBlockSize
;
5874 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5877 * Read those bytes in the buffer from the small block file.
5878 * The small block has already been identified so it shouldn't fail
5879 * unless the file is corrupt.
5881 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5882 offsetInBigBlockFile
,
5883 bytesToReadInBuffer
,
5885 &bytesReadFromBigBlockFile
);
5891 * Step to the next big block.
5893 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5895 return STG_E_DOCFILECORRUPT
;
5897 bufferWalker
+= bytesReadFromBigBlockFile
;
5898 size
-= bytesReadFromBigBlockFile
;
5899 *bytesRead
+= bytesReadFromBigBlockFile
;
5900 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5903 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5906 /******************************************************************************
5907 * SmallBlockChainStream_WriteAt
5909 * Writes the specified number of bytes to this chain at the specified offset.
5910 * Will fail if not all specified number of bytes have been written.
5912 HRESULT
SmallBlockChainStream_WriteAt(
5913 SmallBlockChainStream
* This
,
5914 ULARGE_INTEGER offset
,
5917 ULONG
* bytesWritten
)
5919 ULARGE_INTEGER offsetInBigBlockFile
;
5920 ULONG blockNoInSequence
=
5921 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5923 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5924 ULONG bytesToWriteInBuffer
;
5926 ULONG bytesWrittenToBigBlockFile
;
5927 const BYTE
* bufferWalker
;
5931 * This should never happen on a small block file.
5933 assert(offset
.u
.HighPart
==0);
5936 * Find the first block in the stream that contains part of the buffer.
5938 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5940 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5942 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5943 return STG_E_DOCFILECORRUPT
;
5944 blockNoInSequence
--;
5948 * Start writing the buffer.
5951 bufferWalker
= buffer
;
5952 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5955 * Calculate how many bytes we can copy to this small block.
5957 bytesToWriteInBuffer
=
5958 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5961 * Calculate the offset of the small block in the small block file.
5963 offsetInBigBlockFile
.u
.HighPart
= 0;
5964 offsetInBigBlockFile
.u
.LowPart
=
5965 blockIndex
* This
->parentStorage
->smallBlockSize
;
5967 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5970 * Write those bytes in the buffer to the small block file.
5972 res
= BlockChainStream_WriteAt(
5973 This
->parentStorage
->smallBlockRootChain
,
5974 offsetInBigBlockFile
,
5975 bytesToWriteInBuffer
,
5977 &bytesWrittenToBigBlockFile
);
5982 * Step to the next big block.
5984 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5987 bufferWalker
+= bytesWrittenToBigBlockFile
;
5988 size
-= bytesWrittenToBigBlockFile
;
5989 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5990 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5993 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5996 /******************************************************************************
5997 * SmallBlockChainStream_Shrink
5999 * Shrinks this chain in the small block depot.
6001 static BOOL
SmallBlockChainStream_Shrink(
6002 SmallBlockChainStream
* This
,
6003 ULARGE_INTEGER newSize
)
6005 ULONG blockIndex
, extraBlock
;
6009 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6011 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6014 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6017 * Go to the new end of chain
6019 while (count
< numBlocks
)
6021 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6028 * If the count is 0, we have a special case, the head of the chain was
6033 DirEntry chainEntry
;
6035 StorageImpl_ReadDirEntry(This
->parentStorage
,
6036 This
->ownerDirEntry
,
6039 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6041 StorageImpl_WriteDirEntry(This
->parentStorage
,
6042 This
->ownerDirEntry
,
6046 * We start freeing the chain at the head block.
6048 extraBlock
= blockIndex
;
6052 /* Get the next block before marking the new end */
6053 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6057 /* Mark the new end of chain */
6058 SmallBlockChainStream_SetNextBlockInChain(
6061 BLOCK_END_OF_CHAIN
);
6065 * Mark the extra blocks as free
6067 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6072 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6073 extraBlock
= blockIndex
;
6079 /******************************************************************************
6080 * SmallBlockChainStream_Enlarge
6082 * Grows this chain in the small block depot.
6084 static BOOL
SmallBlockChainStream_Enlarge(
6085 SmallBlockChainStream
* This
,
6086 ULARGE_INTEGER newSize
)
6088 ULONG blockIndex
, currentBlock
;
6090 ULONG oldNumBlocks
= 0;
6092 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6095 * Empty chain. Create the head.
6097 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6099 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6100 SmallBlockChainStream_SetNextBlockInChain(
6103 BLOCK_END_OF_CHAIN
);
6105 if (This
->headOfStreamPlaceHolder
!= NULL
)
6107 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6111 DirEntry chainEntry
;
6113 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6116 chainEntry
.startingBlock
= blockIndex
;
6118 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6123 currentBlock
= blockIndex
;
6126 * Figure out how many blocks are needed to contain this stream
6128 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6130 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6134 * Go to the current end of chain
6136 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6139 currentBlock
= blockIndex
;
6140 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6145 * Add new blocks to the chain
6147 while (oldNumBlocks
< newNumBlocks
)
6149 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6150 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6152 SmallBlockChainStream_SetNextBlockInChain(
6155 BLOCK_END_OF_CHAIN
);
6157 currentBlock
= blockIndex
;
6164 /******************************************************************************
6165 * SmallBlockChainStream_SetSize
6167 * Sets the size of this stream.
6168 * The file will grow if we grow the chain.
6170 * TODO: Free the actual blocks in the file when we shrink the chain.
6171 * Currently, the blocks are still in the file. So the file size
6172 * doesn't shrink even if we shrink streams.
6174 BOOL
SmallBlockChainStream_SetSize(
6175 SmallBlockChainStream
* This
,
6176 ULARGE_INTEGER newSize
)
6178 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6180 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6183 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6185 SmallBlockChainStream_Shrink(This
, newSize
);
6189 SmallBlockChainStream_Enlarge(This
, newSize
);
6195 /******************************************************************************
6196 * SmallBlockChainStream_GetCount
6198 * Returns the number of small blocks that comprises this chain.
6199 * This is not the size of the stream as the last block may not be full!
6202 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6207 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6209 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6213 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6214 blockIndex
, &blockIndex
)))
6221 /******************************************************************************
6222 * SmallBlockChainStream_GetSize
6224 * Returns the size of this chain.
6226 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6228 DirEntry chainEntry
;
6230 if(This
->headOfStreamPlaceHolder
!= NULL
)
6232 ULARGE_INTEGER result
;
6233 result
.u
.HighPart
= 0;
6235 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6236 This
->parentStorage
->smallBlockSize
;
6241 StorageImpl_ReadDirEntry(
6242 This
->parentStorage
,
6243 This
->ownerDirEntry
,
6246 return chainEntry
.size
;
6249 /******************************************************************************
6250 * StgCreateDocfile [OLE32.@]
6251 * Creates a new compound file storage object
6254 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6255 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6256 * reserved [ ?] unused?, usually 0
6257 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6260 * S_OK if the file was successfully created
6261 * some STG_E_ value if error
6263 * if pwcsName is NULL, create file with new unique name
6264 * the function can returns
6265 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6268 HRESULT WINAPI
StgCreateDocfile(
6272 IStorage
**ppstgOpen
)
6274 StorageBaseImpl
* newStorage
= 0;
6275 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6276 HRESULT hr
= STG_E_INVALIDFLAG
;
6280 DWORD fileAttributes
;
6281 WCHAR tempFileName
[MAX_PATH
];
6283 TRACE("(%s, %x, %d, %p)\n",
6284 debugstr_w(pwcsName
), grfMode
,
6285 reserved
, ppstgOpen
);
6288 return STG_E_INVALIDPOINTER
;
6290 return STG_E_INVALIDPARAMETER
;
6292 /* if no share mode given then DENY_NONE is the default */
6293 if (STGM_SHARE_MODE(grfMode
) == 0)
6294 grfMode
|= STGM_SHARE_DENY_NONE
;
6296 if ( FAILED( validateSTGM(grfMode
) ))
6299 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6300 switch(STGM_ACCESS_MODE(grfMode
))
6303 case STGM_READWRITE
:
6309 /* in direct mode, can only use SHARE_EXCLUSIVE */
6310 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6313 /* but in transacted mode, any share mode is valid */
6316 * Generate a unique name.
6320 WCHAR tempPath
[MAX_PATH
];
6321 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6323 memset(tempPath
, 0, sizeof(tempPath
));
6324 memset(tempFileName
, 0, sizeof(tempFileName
));
6326 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6329 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6330 pwcsName
= tempFileName
;
6333 hr
= STG_E_INSUFFICIENTMEMORY
;
6337 creationMode
= TRUNCATE_EXISTING
;
6341 creationMode
= GetCreationModeFromSTGM(grfMode
);
6345 * Interpret the STGM value grfMode
6347 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6348 accessMode
= GetAccessModeFromSTGM(grfMode
);
6350 if (grfMode
& STGM_DELETEONRELEASE
)
6351 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6353 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6355 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6356 FIXME("Storage share mode not implemented.\n");
6360 hFile
= CreateFileW(pwcsName
,
6368 if (hFile
== INVALID_HANDLE_VALUE
)
6370 if(GetLastError() == ERROR_FILE_EXISTS
)
6371 hr
= STG_E_FILEALREADYEXISTS
;
6378 * Allocate and initialize the new IStorage32object.
6380 hr
= Storage_Construct(
6395 * Get an "out" pointer for the caller.
6397 *ppstgOpen
= (IStorage
*)newStorage
;
6400 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6405 /******************************************************************************
6406 * StgCreateStorageEx [OLE32.@]
6408 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6410 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6411 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6413 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6415 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6416 return STG_E_INVALIDPARAMETER
;
6419 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6421 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6422 return STG_E_INVALIDPARAMETER
;
6425 if (stgfmt
== STGFMT_FILE
)
6427 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6428 return STG_E_INVALIDPARAMETER
;
6431 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6433 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6434 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6437 ERR("Invalid stgfmt argument\n");
6438 return STG_E_INVALIDPARAMETER
;
6441 /******************************************************************************
6442 * StgCreatePropSetStg [OLE32.@]
6444 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6445 IPropertySetStorage
**ppPropSetStg
)
6449 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6451 hr
= STG_E_INVALIDPARAMETER
;
6453 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6454 (void**)ppPropSetStg
);
6458 /******************************************************************************
6459 * StgOpenStorageEx [OLE32.@]
6461 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6463 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6464 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6466 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6468 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6469 return STG_E_INVALIDPARAMETER
;
6475 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6476 return STG_E_INVALIDPARAMETER
;
6478 case STGFMT_STORAGE
:
6481 case STGFMT_DOCFILE
:
6482 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6484 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6485 return STG_E_INVALIDPARAMETER
;
6487 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6491 WARN("STGFMT_ANY assuming storage\n");
6495 return STG_E_INVALIDPARAMETER
;
6498 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6502 /******************************************************************************
6503 * StgOpenStorage [OLE32.@]
6505 HRESULT WINAPI
StgOpenStorage(
6506 const OLECHAR
*pwcsName
,
6507 IStorage
*pstgPriority
,
6511 IStorage
**ppstgOpen
)
6513 StorageBaseImpl
* newStorage
= 0;
6519 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6520 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6521 snbExclude
, reserved
, ppstgOpen
);
6525 hr
= STG_E_INVALIDNAME
;
6531 hr
= STG_E_INVALIDPOINTER
;
6537 hr
= STG_E_INVALIDPARAMETER
;
6541 if (grfMode
& STGM_PRIORITY
)
6543 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6544 return STG_E_INVALIDFLAG
;
6545 if (grfMode
& STGM_DELETEONRELEASE
)
6546 return STG_E_INVALIDFUNCTION
;
6547 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6548 return STG_E_INVALIDFLAG
;
6549 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6550 grfMode
|= STGM_SHARE_DENY_NONE
;
6552 /* STGM_PRIORITY stops other IStorage objects on the same file from
6553 * committing until the STGM_PRIORITY IStorage is closed. it also
6554 * stops non-transacted mode StgOpenStorage calls with write access from
6555 * succeeding. obviously, both of these cannot be achieved through just
6556 * file share flags */
6557 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6561 * Validate the sharing mode
6563 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6564 switch(STGM_SHARE_MODE(grfMode
))
6566 case STGM_SHARE_EXCLUSIVE
:
6567 case STGM_SHARE_DENY_WRITE
:
6570 hr
= STG_E_INVALIDFLAG
;
6574 if ( FAILED( validateSTGM(grfMode
) ) ||
6575 (grfMode
&STGM_CREATE
))
6577 hr
= STG_E_INVALIDFLAG
;
6581 /* shared reading requires transacted mode */
6582 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6583 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6584 !(grfMode
&STGM_TRANSACTED
) )
6586 hr
= STG_E_INVALIDFLAG
;
6591 * Interpret the STGM value grfMode
6593 shareMode
= GetShareModeFromSTGM(grfMode
);
6594 accessMode
= GetAccessModeFromSTGM(grfMode
);
6598 hFile
= CreateFileW( pwcsName
,
6603 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6606 if (hFile
==INVALID_HANDLE_VALUE
)
6608 DWORD last_error
= GetLastError();
6614 case ERROR_FILE_NOT_FOUND
:
6615 hr
= STG_E_FILENOTFOUND
;
6618 case ERROR_PATH_NOT_FOUND
:
6619 hr
= STG_E_PATHNOTFOUND
;
6622 case ERROR_ACCESS_DENIED
:
6623 case ERROR_WRITE_PROTECT
:
6624 hr
= STG_E_ACCESSDENIED
;
6627 case ERROR_SHARING_VIOLATION
:
6628 hr
= STG_E_SHAREVIOLATION
;
6639 * Refuse to open the file if it's too small to be a structured storage file
6640 * FIXME: verify the file when reading instead of here
6642 if (GetFileSize(hFile
, NULL
) < 0x100)
6645 hr
= STG_E_FILEALREADYEXISTS
;
6650 * Allocate and initialize the new IStorage32object.
6652 hr
= Storage_Construct(
6664 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6666 if(hr
== STG_E_INVALIDHEADER
)
6667 hr
= STG_E_FILEALREADYEXISTS
;
6672 * Get an "out" pointer for the caller.
6674 *ppstgOpen
= (IStorage
*)newStorage
;
6677 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6681 /******************************************************************************
6682 * StgCreateDocfileOnILockBytes [OLE32.@]
6684 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6688 IStorage
** ppstgOpen
)
6690 StorageBaseImpl
* newStorage
= 0;
6693 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6694 return STG_E_INVALIDPOINTER
;
6697 * Allocate and initialize the new IStorage object.
6699 hr
= Storage_Construct(
6714 * Get an "out" pointer for the caller.
6716 *ppstgOpen
= (IStorage
*)newStorage
;
6721 /******************************************************************************
6722 * StgOpenStorageOnILockBytes [OLE32.@]
6724 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6726 IStorage
*pstgPriority
,
6730 IStorage
**ppstgOpen
)
6732 StorageBaseImpl
* newStorage
= 0;
6735 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6736 return STG_E_INVALIDPOINTER
;
6738 if ( FAILED( validateSTGM(grfMode
) ))
6739 return STG_E_INVALIDFLAG
;
6744 * Allocate and initialize the new IStorage object.
6746 hr
= Storage_Construct(
6761 * Get an "out" pointer for the caller.
6763 *ppstgOpen
= (IStorage
*)newStorage
;
6768 /******************************************************************************
6769 * StgSetTimes [ole32.@]
6770 * StgSetTimes [OLE32.@]
6774 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6775 FILETIME
const *patime
, FILETIME
const *pmtime
)
6777 IStorage
*stg
= NULL
;
6780 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6782 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6786 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6787 IStorage_Release(stg
);
6793 /******************************************************************************
6794 * StgIsStorageILockBytes [OLE32.@]
6796 * Determines if the ILockBytes contains a storage object.
6798 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6801 ULARGE_INTEGER offset
;
6803 offset
.u
.HighPart
= 0;
6804 offset
.u
.LowPart
= 0;
6806 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6808 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6814 /******************************************************************************
6815 * WriteClassStg [OLE32.@]
6817 * This method will store the specified CLSID in the specified storage object
6819 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6824 return E_INVALIDARG
;
6827 return STG_E_INVALIDPOINTER
;
6829 hRes
= IStorage_SetClass(pStg
, rclsid
);
6834 /***********************************************************************
6835 * ReadClassStg (OLE32.@)
6837 * This method reads the CLSID previously written to a storage object with
6838 * the WriteClassStg.
6841 * pstg [I] IStorage pointer
6842 * pclsid [O] Pointer to where the CLSID is written
6846 * Failure: HRESULT code.
6848 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6853 TRACE("(%p, %p)\n", pstg
, pclsid
);
6855 if(!pstg
|| !pclsid
)
6856 return E_INVALIDARG
;
6859 * read a STATSTG structure (contains the clsid) from the storage
6861 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6864 *pclsid
=pstatstg
.clsid
;
6869 /***********************************************************************
6870 * OleLoadFromStream (OLE32.@)
6872 * This function loads an object from stream
6874 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6878 LPPERSISTSTREAM xstm
;
6880 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6882 res
=ReadClassStm(pStm
,&clsid
);
6885 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6888 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6890 IUnknown_Release((IUnknown
*)*ppvObj
);
6893 res
=IPersistStream_Load(xstm
,pStm
);
6894 IPersistStream_Release(xstm
);
6895 /* FIXME: all refcounts ok at this point? I think they should be:
6898 * xstm : 0 (released)
6903 /***********************************************************************
6904 * OleSaveToStream (OLE32.@)
6906 * This function saves an object with the IPersistStream interface on it
6907 * to the specified stream.
6909 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6915 TRACE("(%p,%p)\n",pPStm
,pStm
);
6917 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6919 if (SUCCEEDED(res
)){
6921 res
=WriteClassStm(pStm
,&clsid
);
6925 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6928 TRACE("Finished Save\n");
6932 /****************************************************************************
6933 * This method validate a STGM parameter that can contain the values below
6935 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6936 * The stgm values contained in 0xffff0000 are bitmasks.
6938 * STGM_DIRECT 0x00000000
6939 * STGM_TRANSACTED 0x00010000
6940 * STGM_SIMPLE 0x08000000
6942 * STGM_READ 0x00000000
6943 * STGM_WRITE 0x00000001
6944 * STGM_READWRITE 0x00000002
6946 * STGM_SHARE_DENY_NONE 0x00000040
6947 * STGM_SHARE_DENY_READ 0x00000030
6948 * STGM_SHARE_DENY_WRITE 0x00000020
6949 * STGM_SHARE_EXCLUSIVE 0x00000010
6951 * STGM_PRIORITY 0x00040000
6952 * STGM_DELETEONRELEASE 0x04000000
6954 * STGM_CREATE 0x00001000
6955 * STGM_CONVERT 0x00020000
6956 * STGM_FAILIFTHERE 0x00000000
6958 * STGM_NOSCRATCH 0x00100000
6959 * STGM_NOSNAPSHOT 0x00200000
6961 static HRESULT
validateSTGM(DWORD stgm
)
6963 DWORD access
= STGM_ACCESS_MODE(stgm
);
6964 DWORD share
= STGM_SHARE_MODE(stgm
);
6965 DWORD create
= STGM_CREATE_MODE(stgm
);
6967 if (stgm
&~STGM_KNOWN_FLAGS
)
6969 ERR("unknown flags %08x\n", stgm
);
6977 case STGM_READWRITE
:
6985 case STGM_SHARE_DENY_NONE
:
6986 case STGM_SHARE_DENY_READ
:
6987 case STGM_SHARE_DENY_WRITE
:
6988 case STGM_SHARE_EXCLUSIVE
:
6997 case STGM_FAILIFTHERE
:
7004 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7006 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7010 * STGM_CREATE | STGM_CONVERT
7011 * if both are false, STGM_FAILIFTHERE is set to TRUE
7013 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7017 * STGM_NOSCRATCH requires STGM_TRANSACTED
7019 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7023 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7024 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7026 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7027 (!(stgm
& STGM_TRANSACTED
) ||
7028 share
== STGM_SHARE_EXCLUSIVE
||
7029 share
== STGM_SHARE_DENY_WRITE
) )
7035 /****************************************************************************
7036 * GetShareModeFromSTGM
7038 * This method will return a share mode flag from a STGM value.
7039 * The STGM value is assumed valid.
7041 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7043 switch (STGM_SHARE_MODE(stgm
))
7045 case STGM_SHARE_DENY_NONE
:
7046 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7047 case STGM_SHARE_DENY_READ
:
7048 return FILE_SHARE_WRITE
;
7049 case STGM_SHARE_DENY_WRITE
:
7050 return FILE_SHARE_READ
;
7051 case STGM_SHARE_EXCLUSIVE
:
7054 ERR("Invalid share mode!\n");
7059 /****************************************************************************
7060 * GetAccessModeFromSTGM
7062 * This method will return an access mode flag from a STGM value.
7063 * The STGM value is assumed valid.
7065 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7067 switch (STGM_ACCESS_MODE(stgm
))
7070 return GENERIC_READ
;
7072 case STGM_READWRITE
:
7073 return GENERIC_READ
| GENERIC_WRITE
;
7075 ERR("Invalid access mode!\n");
7080 /****************************************************************************
7081 * GetCreationModeFromSTGM
7083 * This method will return a creation mode flag from a STGM value.
7084 * The STGM value is assumed valid.
7086 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7088 switch(STGM_CREATE_MODE(stgm
))
7091 return CREATE_ALWAYS
;
7093 FIXME("STGM_CONVERT not implemented!\n");
7095 case STGM_FAILIFTHERE
:
7098 ERR("Invalid create mode!\n");
7104 /*************************************************************************
7105 * OLECONVERT_LoadOLE10 [Internal]
7107 * Loads the OLE10 STREAM to memory
7110 * pOleStream [I] The OLESTREAM
7111 * pData [I] Data Structure for the OLESTREAM Data
7115 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7116 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7119 * This function is used by OleConvertOLESTREAMToIStorage only.
7121 * Memory allocated for pData must be freed by the caller
7123 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7126 HRESULT hRes
= S_OK
;
7130 pData
->pData
= NULL
;
7131 pData
->pstrOleObjFileName
= NULL
;
7133 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7136 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7137 if(dwSize
!= sizeof(pData
->dwOleID
))
7139 hRes
= CONVERT10_E_OLESTREAM_GET
;
7141 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7143 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7154 /* Get the TypeID... more info needed for this field */
7155 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7156 if(dwSize
!= sizeof(pData
->dwTypeID
))
7158 hRes
= CONVERT10_E_OLESTREAM_GET
;
7163 if(pData
->dwTypeID
!= 0)
7165 /* Get the length of the OleTypeName */
7166 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7167 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7169 hRes
= CONVERT10_E_OLESTREAM_GET
;
7174 if(pData
->dwOleTypeNameLength
> 0)
7176 /* Get the OleTypeName */
7177 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7178 if(dwSize
!= pData
->dwOleTypeNameLength
)
7180 hRes
= CONVERT10_E_OLESTREAM_GET
;
7186 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7187 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7189 hRes
= CONVERT10_E_OLESTREAM_GET
;
7193 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7194 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7195 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7196 if(pData
->pstrOleObjFileName
)
7198 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7199 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7201 hRes
= CONVERT10_E_OLESTREAM_GET
;
7205 hRes
= CONVERT10_E_OLESTREAM_GET
;
7210 /* Get the Width of the Metafile */
7211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7212 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7214 hRes
= CONVERT10_E_OLESTREAM_GET
;
7218 /* Get the Height of the Metafile */
7219 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7220 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7222 hRes
= CONVERT10_E_OLESTREAM_GET
;
7228 /* Get the Length of the Data */
7229 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7230 if(dwSize
!= sizeof(pData
->dwDataLength
))
7232 hRes
= CONVERT10_E_OLESTREAM_GET
;
7236 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7238 if(!bStrem1
) /* if it is a second OLE stream data */
7240 pData
->dwDataLength
-= 8;
7241 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7242 if(dwSize
!= sizeof(pData
->strUnknown
))
7244 hRes
= CONVERT10_E_OLESTREAM_GET
;
7250 if(pData
->dwDataLength
> 0)
7252 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7254 /* Get Data (ex. IStorage, Metafile, or BMP) */
7257 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7258 if(dwSize
!= pData
->dwDataLength
)
7260 hRes
= CONVERT10_E_OLESTREAM_GET
;
7265 hRes
= CONVERT10_E_OLESTREAM_GET
;
7274 /*************************************************************************
7275 * OLECONVERT_SaveOLE10 [Internal]
7277 * Saves the OLE10 STREAM From memory
7280 * pData [I] Data Structure for the OLESTREAM Data
7281 * pOleStream [I] The OLESTREAM to save
7285 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7288 * This function is used by OleConvertIStorageToOLESTREAM only.
7291 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7294 HRESULT hRes
= S_OK
;
7298 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7299 if(dwSize
!= sizeof(pData
->dwOleID
))
7301 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7306 /* Set the TypeID */
7307 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7308 if(dwSize
!= sizeof(pData
->dwTypeID
))
7310 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7314 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7316 /* Set the Length of the OleTypeName */
7317 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7318 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7320 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7325 if(pData
->dwOleTypeNameLength
> 0)
7327 /* Set the OleTypeName */
7328 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7329 if(dwSize
!= pData
->dwOleTypeNameLength
)
7331 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7338 /* Set the width of the Metafile */
7339 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7340 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7342 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7348 /* Set the height of the Metafile */
7349 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7350 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7352 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7358 /* Set the length of the Data */
7359 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7360 if(dwSize
!= sizeof(pData
->dwDataLength
))
7362 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7368 if(pData
->dwDataLength
> 0)
7370 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7371 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7372 if(dwSize
!= pData
->dwDataLength
)
7374 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7382 /*************************************************************************
7383 * OLECONVERT_GetOLE20FromOLE10[Internal]
7385 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7386 * opens it, and copies the content to the dest IStorage for
7387 * OleConvertOLESTREAMToIStorage
7391 * pDestStorage [I] The IStorage to copy the data to
7392 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7393 * nBufferLength [I] The size of the buffer
7402 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7406 IStorage
*pTempStorage
;
7407 DWORD dwNumOfBytesWritten
;
7408 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7409 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7411 /* Create a temp File */
7412 GetTempPathW(MAX_PATH
, wstrTempDir
);
7413 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7414 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7416 if(hFile
!= INVALID_HANDLE_VALUE
)
7418 /* Write IStorage Data to File */
7419 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7422 /* Open and copy temp storage to the Dest Storage */
7423 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7426 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7427 IStorage_Release(pTempStorage
);
7429 DeleteFileW(wstrTempFile
);
7434 /*************************************************************************
7435 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7437 * Saves the OLE10 STREAM From memory
7440 * pStorage [I] The Src IStorage to copy
7441 * pData [I] The Dest Memory to write to.
7444 * The size in bytes allocated for pData
7447 * Memory allocated for pData must be freed by the caller
7449 * Used by OleConvertIStorageToOLESTREAM only.
7452 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7456 DWORD nDataLength
= 0;
7457 IStorage
*pTempStorage
;
7458 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7459 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7463 /* Create temp Storage */
7464 GetTempPathW(MAX_PATH
, wstrTempDir
);
7465 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7466 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7470 /* Copy Src Storage to the Temp Storage */
7471 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7472 IStorage_Release(pTempStorage
);
7474 /* Open Temp Storage as a file and copy to memory */
7475 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7476 if(hFile
!= INVALID_HANDLE_VALUE
)
7478 nDataLength
= GetFileSize(hFile
, NULL
);
7479 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7480 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7483 DeleteFileW(wstrTempFile
);
7488 /*************************************************************************
7489 * OLECONVERT_CreateOleStream [Internal]
7491 * Creates the "\001OLE" stream in the IStorage if necessary.
7494 * pStorage [I] Dest storage to create the stream in
7500 * This function is used by OleConvertOLESTREAMToIStorage only.
7502 * This stream is still unknown, MS Word seems to have extra data
7503 * but since the data is stored in the OLESTREAM there should be
7504 * no need to recreate the stream. If the stream is manually
7505 * deleted it will create it with this default data.
7508 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7512 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7513 BYTE pOleStreamHeader
[] =
7515 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7516 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7517 0x00, 0x00, 0x00, 0x00
7520 /* Create stream if not present */
7521 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7522 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7526 /* Write default Data */
7527 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7528 IStream_Release(pStream
);
7532 /* write a string to a stream, preceded by its length */
7533 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7540 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7541 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7546 str
= CoTaskMemAlloc( len
);
7547 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7548 r
= IStream_Write( stm
, str
, len
, NULL
);
7549 CoTaskMemFree( str
);
7553 /* read a string preceded by its length from a stream */
7554 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7557 DWORD len
, count
= 0;
7561 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7564 if( count
!= sizeof(len
) )
7565 return E_OUTOFMEMORY
;
7567 TRACE("%d bytes\n",len
);
7569 str
= CoTaskMemAlloc( len
);
7571 return E_OUTOFMEMORY
;
7573 r
= IStream_Read( stm
, str
, len
, &count
);
7578 CoTaskMemFree( str
);
7579 return E_OUTOFMEMORY
;
7582 TRACE("Read string %s\n",debugstr_an(str
,len
));
7584 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7585 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7587 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7588 CoTaskMemFree( str
);
7596 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7597 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7601 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7603 static const BYTE unknown1
[12] =
7604 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7605 0xFF, 0xFF, 0xFF, 0xFF};
7606 static const BYTE unknown2
[16] =
7607 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7610 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7611 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7612 debugstr_w(szProgIDName
));
7614 /* Create a CompObj stream */
7615 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7616 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7620 /* Write CompObj Structure to stream */
7621 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7623 if( SUCCEEDED( r
) )
7624 r
= WriteClassStm( pstm
, clsid
);
7626 if( SUCCEEDED( r
) )
7627 r
= STREAM_WriteString( pstm
, lpszUserType
);
7628 if( SUCCEEDED( r
) )
7629 r
= STREAM_WriteString( pstm
, szClipName
);
7630 if( SUCCEEDED( r
) )
7631 r
= STREAM_WriteString( pstm
, szProgIDName
);
7632 if( SUCCEEDED( r
) )
7633 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7635 IStream_Release( pstm
);
7640 /***********************************************************************
7641 * WriteFmtUserTypeStg (OLE32.@)
7643 HRESULT WINAPI
WriteFmtUserTypeStg(
7644 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7647 WCHAR szwClipName
[0x40];
7648 CLSID clsid
= CLSID_NULL
;
7649 LPWSTR wstrProgID
= NULL
;
7652 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7654 /* get the clipboard format name */
7655 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7658 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7660 /* FIXME: There's room to save a CLSID and its ProgID, but
7661 the CLSID is not looked up in the registry and in all the
7662 tests I wrote it was CLSID_NULL. Where does it come from?
7665 /* get the real program ID. This may fail, but that's fine */
7666 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7668 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7670 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7671 lpszUserType
, szwClipName
, wstrProgID
);
7673 CoTaskMemFree(wstrProgID
);
7679 /******************************************************************************
7680 * ReadFmtUserTypeStg [OLE32.@]
7682 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7686 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7687 unsigned char unknown1
[12];
7688 unsigned char unknown2
[16];
7690 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7693 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7695 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7696 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7699 WARN("Failed to open stream r = %08x\n", r
);
7703 /* read the various parts of the structure */
7704 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7705 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7707 r
= ReadClassStm( stm
, &clsid
);
7711 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7715 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7719 r
= STREAM_ReadString( stm
, &szProgIDName
);
7723 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7724 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7727 /* ok, success... now we just need to store what we found */
7729 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7730 CoTaskMemFree( szOleTypeName
);
7732 if( lplpszUserType
)
7733 *lplpszUserType
= szCLSIDName
;
7734 CoTaskMemFree( szProgIDName
);
7737 IStream_Release( stm
);
7743 /*************************************************************************
7744 * OLECONVERT_CreateCompObjStream [Internal]
7746 * Creates a "\001CompObj" is the destination IStorage if necessary.
7749 * pStorage [I] The dest IStorage to create the CompObj Stream
7751 * strOleTypeName [I] The ProgID
7755 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7758 * This function is used by OleConvertOLESTREAMToIStorage only.
7760 * The stream data is stored in the OLESTREAM and there should be
7761 * no need to recreate the stream. If the stream is manually
7762 * deleted it will attempt to create it by querying the registry.
7766 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7769 HRESULT hStorageRes
, hRes
= S_OK
;
7770 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7771 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7772 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7774 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7775 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7777 /* Initialize the CompObj structure */
7778 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7779 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7780 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7783 /* Create a CompObj stream if it doesn't exist */
7784 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7785 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7786 if(hStorageRes
== S_OK
)
7788 /* copy the OleTypeName to the compobj struct */
7789 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7790 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7792 /* copy the OleTypeName to the compobj struct */
7793 /* Note: in the test made, these were Identical */
7794 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7795 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7798 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7799 bufferW
, OLESTREAM_MAX_STR_LEN
);
7800 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7806 /* Get the CLSID Default Name from the Registry */
7807 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7808 if(hErr
== ERROR_SUCCESS
)
7810 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7811 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7812 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7813 if(hErr
== ERROR_SUCCESS
)
7815 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7821 /* Write CompObj Structure to stream */
7822 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7824 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7826 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7827 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7829 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7831 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7832 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7834 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7836 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7837 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7839 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7841 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7842 IStream_Release(pStream
);
7848 /*************************************************************************
7849 * OLECONVERT_CreateOlePresStream[Internal]
7851 * Creates the "\002OlePres000" Stream with the Metafile data
7854 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7855 * dwExtentX [I] Width of the Metafile
7856 * dwExtentY [I] Height of the Metafile
7857 * pData [I] Metafile data
7858 * dwDataLength [I] Size of the Metafile data
7862 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7865 * This function is used by OleConvertOLESTREAMToIStorage only.
7868 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7872 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7873 BYTE pOlePresStreamHeader
[] =
7875 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7876 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7877 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7878 0x00, 0x00, 0x00, 0x00
7881 BYTE pOlePresStreamHeaderEmpty
[] =
7883 0x00, 0x00, 0x00, 0x00,
7884 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7885 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7886 0x00, 0x00, 0x00, 0x00
7889 /* Create the OlePres000 Stream */
7890 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7891 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7896 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7898 memset(&OlePres
, 0, sizeof(OlePres
));
7899 /* Do we have any metafile data to save */
7900 if(dwDataLength
> 0)
7902 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7903 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7907 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7908 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7910 /* Set width and height of the metafile */
7911 OlePres
.dwExtentX
= dwExtentX
;
7912 OlePres
.dwExtentY
= -dwExtentY
;
7914 /* Set Data and Length */
7915 if(dwDataLength
> sizeof(METAFILEPICT16
))
7917 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7918 OlePres
.pData
= &(pData
[8]);
7920 /* Save OlePres000 Data to Stream */
7921 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7922 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7923 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7924 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7925 if(OlePres
.dwSize
> 0)
7927 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7929 IStream_Release(pStream
);
7933 /*************************************************************************
7934 * OLECONVERT_CreateOle10NativeStream [Internal]
7936 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7939 * pStorage [I] Dest storage to create the stream in
7940 * pData [I] Ole10 Native Data (ex. bmp)
7941 * dwDataLength [I] Size of the Ole10 Native Data
7947 * This function is used by OleConvertOLESTREAMToIStorage only.
7949 * Might need to verify the data and return appropriate error message
7952 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7956 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7958 /* Create the Ole10Native Stream */
7959 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7960 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7964 /* Write info to stream */
7965 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7966 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7967 IStream_Release(pStream
);
7972 /*************************************************************************
7973 * OLECONVERT_GetOLE10ProgID [Internal]
7975 * Finds the ProgID (or OleTypeID) from the IStorage
7978 * pStorage [I] The Src IStorage to get the ProgID
7979 * strProgID [I] the ProgID string to get
7980 * dwSize [I] the size of the string
7984 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7987 * This function is used by OleConvertIStorageToOLESTREAM only.
7991 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7995 LARGE_INTEGER iSeekPos
;
7996 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7997 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7999 /* Open the CompObj Stream */
8000 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8001 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8005 /*Get the OleType from the CompObj Stream */
8006 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8007 iSeekPos
.u
.HighPart
= 0;
8009 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8010 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8011 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8012 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8013 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8014 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8015 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8017 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8020 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8022 IStream_Release(pStream
);
8027 LPOLESTR wstrProgID
;
8029 /* Get the OleType from the registry */
8030 REFCLSID clsid
= &(stat
.clsid
);
8031 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8032 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8035 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8042 /*************************************************************************
8043 * OLECONVERT_GetOle10PresData [Internal]
8045 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8048 * pStorage [I] Src IStroage
8049 * pOleStream [I] Dest OleStream Mem Struct
8055 * This function is used by OleConvertIStorageToOLESTREAM only.
8057 * Memory allocated for pData must be freed by the caller
8061 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8066 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8068 /* Initialize Default data for OLESTREAM */
8069 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8070 pOleStreamData
[0].dwTypeID
= 2;
8071 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8072 pOleStreamData
[1].dwTypeID
= 0;
8073 pOleStreamData
[0].dwMetaFileWidth
= 0;
8074 pOleStreamData
[0].dwMetaFileHeight
= 0;
8075 pOleStreamData
[0].pData
= NULL
;
8076 pOleStreamData
[1].pData
= NULL
;
8078 /* Open Ole10Native Stream */
8079 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8080 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8084 /* Read Size and Data */
8085 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8086 if(pOleStreamData
->dwDataLength
> 0)
8088 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8089 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8091 IStream_Release(pStream
);
8097 /*************************************************************************
8098 * OLECONVERT_GetOle20PresData[Internal]
8100 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8103 * pStorage [I] Src IStroage
8104 * pOleStreamData [I] Dest OleStream Mem Struct
8110 * This function is used by OleConvertIStorageToOLESTREAM only.
8112 * Memory allocated for pData must be freed by the caller
8114 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8118 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8119 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8121 /* Initialize Default data for OLESTREAM */
8122 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8123 pOleStreamData
[0].dwTypeID
= 2;
8124 pOleStreamData
[0].dwMetaFileWidth
= 0;
8125 pOleStreamData
[0].dwMetaFileHeight
= 0;
8126 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8127 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8128 pOleStreamData
[1].dwTypeID
= 0;
8129 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8130 pOleStreamData
[1].strOleTypeName
[0] = 0;
8131 pOleStreamData
[1].dwMetaFileWidth
= 0;
8132 pOleStreamData
[1].dwMetaFileHeight
= 0;
8133 pOleStreamData
[1].pData
= NULL
;
8134 pOleStreamData
[1].dwDataLength
= 0;
8137 /* Open OlePress000 stream */
8138 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8139 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8142 LARGE_INTEGER iSeekPos
;
8143 METAFILEPICT16 MetaFilePict
;
8144 static const char strMetafilePictName
[] = "METAFILEPICT";
8146 /* Set the TypeID for a Metafile */
8147 pOleStreamData
[1].dwTypeID
= 5;
8149 /* Set the OleTypeName to Metafile */
8150 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8151 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8153 iSeekPos
.u
.HighPart
= 0;
8154 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8156 /* Get Presentation Data */
8157 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8158 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8159 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8160 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8162 /*Set width and Height */
8163 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8164 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8165 if(olePress
.dwSize
> 0)
8168 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8170 /* Set MetaFilePict struct */
8171 MetaFilePict
.mm
= 8;
8172 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8173 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8174 MetaFilePict
.hMF
= 0;
8176 /* Get Metafile Data */
8177 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8178 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8179 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8181 IStream_Release(pStream
);
8185 /*************************************************************************
8186 * OleConvertOLESTREAMToIStorage [OLE32.@]
8191 * DVTARGETDEVICE parameter is not handled
8192 * Still unsure of some mem fields for OLE 10 Stream
8193 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8194 * and "\001OLE" streams
8197 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8198 LPOLESTREAM pOleStream
,
8200 const DVTARGETDEVICE
* ptd
)
8204 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8206 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8208 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8212 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8215 if(pstg
== NULL
|| pOleStream
== NULL
)
8217 hRes
= E_INVALIDARG
;
8222 /* Load the OLESTREAM to Memory */
8223 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8228 /* Load the OLESTREAM to Memory (part 2)*/
8229 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8235 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8237 /* Do we have the IStorage Data in the OLESTREAM */
8238 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8240 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8241 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8245 /* It must be an original OLE 1.0 source */
8246 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8251 /* It must be an original OLE 1.0 source */
8252 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8255 /* Create CompObj Stream if necessary */
8256 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8259 /*Create the Ole Stream if necessary */
8260 OLECONVERT_CreateOleStream(pstg
);
8265 /* Free allocated memory */
8266 for(i
=0; i
< 2; i
++)
8268 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8269 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8270 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8275 /*************************************************************************
8276 * OleConvertIStorageToOLESTREAM [OLE32.@]
8283 * Still unsure of some mem fields for OLE 10 Stream
8284 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8285 * and "\001OLE" streams.
8288 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8290 LPOLESTREAM pOleStream
)
8293 HRESULT hRes
= S_OK
;
8295 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8296 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8298 TRACE("%p %p\n", pstg
, pOleStream
);
8300 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8302 if(pstg
== NULL
|| pOleStream
== NULL
)
8304 hRes
= E_INVALIDARG
;
8308 /* Get the ProgID */
8309 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8310 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8314 /* Was it originally Ole10 */
8315 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8318 IStream_Release(pStream
);
8319 /* Get Presentation Data for Ole10Native */
8320 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8324 /* Get Presentation Data (OLE20) */
8325 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8328 /* Save OLESTREAM */
8329 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8332 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8337 /* Free allocated memory */
8338 for(i
=0; i
< 2; i
++)
8340 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8346 /***********************************************************************
8347 * GetConvertStg (OLE32.@)
8349 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8350 FIXME("unimplemented stub!\n");
8354 /******************************************************************************
8355 * StgIsStorageFile [OLE32.@]
8356 * Verify if the file contains a storage object
8362 * S_OK if file has magic bytes as a storage object
8363 * S_FALSE if file is not storage
8366 StgIsStorageFile(LPCOLESTR fn
)
8372 TRACE("%s\n", debugstr_w(fn
));
8373 hf
= CreateFileW(fn
, GENERIC_READ
,
8374 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8375 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8377 if (hf
== INVALID_HANDLE_VALUE
)
8378 return STG_E_FILENOTFOUND
;
8380 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8382 WARN(" unable to read file\n");
8389 if (bytes_read
!= 8) {
8390 TRACE(" too short\n");
8394 if (!memcmp(magic
,STORAGE_magic
,8)) {
8399 TRACE(" -> Invalid header.\n");
8403 /***********************************************************************
8404 * WriteClassStm (OLE32.@)
8406 * Writes a CLSID to a stream.
8409 * pStm [I] Stream to write to.
8410 * rclsid [I] CLSID to write.
8414 * Failure: HRESULT code.
8416 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8418 TRACE("(%p,%p)\n",pStm
,rclsid
);
8420 if (!pStm
|| !rclsid
)
8421 return E_INVALIDARG
;
8423 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8426 /***********************************************************************
8427 * ReadClassStm (OLE32.@)
8429 * Reads a CLSID from a stream.
8432 * pStm [I] Stream to read from.
8433 * rclsid [O] CLSID to read.
8437 * Failure: HRESULT code.
8439 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8444 TRACE("(%p,%p)\n",pStm
,pclsid
);
8446 if (!pStm
|| !pclsid
)
8447 return E_INVALIDARG
;
8449 /* clear the output args */
8450 *pclsid
= CLSID_NULL
;
8452 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8457 if (nbByte
!= sizeof(CLSID
))
8458 return STG_E_READFAULT
;