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
;
839 TRACE("(%p, %s, %x, %d, %d, %p)\n",
840 iface
, debugstr_w(pwcsName
), grfMode
,
841 reserved1
, reserved2
, ppstm
);
844 return STG_E_INVALIDPOINTER
;
847 return STG_E_INVALIDNAME
;
849 if (reserved1
|| reserved2
)
850 return STG_E_INVALIDPARAMETER
;
852 if ( FAILED( validateSTGM(grfMode
) ))
853 return STG_E_INVALIDFLAG
;
855 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
856 return STG_E_INVALIDFLAG
;
859 return STG_E_REVERTED
;
864 if ((grfMode
& STGM_DELETEONRELEASE
) ||
865 (grfMode
& STGM_TRANSACTED
))
866 return STG_E_INVALIDFUNCTION
;
869 * Don't worry about permissions in transacted mode, as we can always write
870 * changes; we just can't always commit them.
872 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
873 /* Can't create a stream on read-only storage */
874 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
875 return STG_E_ACCESSDENIED
;
877 /* Can't create a stream with greater access than the parent. */
878 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
879 return STG_E_ACCESSDENIED
;
882 if(This
->openFlags
& STGM_SIMPLE
)
883 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
887 currentEntryRef
= findElement(This
,
888 This
->storageDirEntry
,
892 if (currentEntryRef
!= DIRENTRY_NULL
)
895 * An element with this name already exists
897 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
899 IStorage_DestroyElement(iface
, pwcsName
);
902 return STG_E_FILEALREADYEXISTS
;
906 * memset the empty entry
908 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
910 newStreamEntry
.sizeOfNameString
=
911 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
913 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
914 return STG_E_INVALIDNAME
;
916 strcpyW(newStreamEntry
.name
, pwcsName
);
918 newStreamEntry
.stgType
= STGTY_STREAM
;
919 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
920 newStreamEntry
.size
.u
.LowPart
= 0;
921 newStreamEntry
.size
.u
.HighPart
= 0;
923 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
924 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
925 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
927 /* call CoFileTime to get the current time
932 /* newStreamEntry.clsid */
935 * Create an entry with the new data
937 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
942 * Insert the new entry in the parent storage's tree.
946 This
->storageDirEntry
,
950 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
955 * Open the stream to return it.
957 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
961 *ppstm
= (IStream
*)newStream
;
963 IStream_AddRef(*ppstm
);
967 return STG_E_INSUFFICIENTMEMORY
;
973 /************************************************************************
974 * Storage32BaseImpl_SetClass (IStorage)
976 * This method will write the specified CLSID in the directory entry of this
979 * See Windows documentation for more details on IStorage methods.
981 static HRESULT WINAPI
StorageBaseImpl_SetClass(
983 REFCLSID clsid
) /* [in] */
985 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
987 DirEntry currentEntry
;
989 TRACE("(%p, %p)\n", iface
, clsid
);
992 return STG_E_REVERTED
;
994 hRes
= StorageBaseImpl_ReadDirEntry(This
,
995 This
->storageDirEntry
,
999 currentEntry
.clsid
= *clsid
;
1001 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1002 This
->storageDirEntry
,
1009 /************************************************************************
1010 ** Storage32Impl implementation
1013 /************************************************************************
1014 * Storage32BaseImpl_CreateStorage (IStorage)
1016 * This method will create the storage object within the provided storage.
1018 * See Windows documentation for more details on IStorage methods.
1020 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1022 const OLECHAR
*pwcsName
, /* [string][in] */
1023 DWORD grfMode
, /* [in] */
1024 DWORD reserved1
, /* [in] */
1025 DWORD reserved2
, /* [in] */
1026 IStorage
**ppstg
) /* [out] */
1028 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1030 DirEntry currentEntry
;
1032 DirRef currentEntryRef
;
1036 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1037 iface
, debugstr_w(pwcsName
), grfMode
,
1038 reserved1
, reserved2
, ppstg
);
1041 return STG_E_INVALIDPOINTER
;
1043 if (This
->openFlags
& STGM_SIMPLE
)
1045 return STG_E_INVALIDFUNCTION
;
1049 return STG_E_INVALIDNAME
;
1053 if ( FAILED( validateSTGM(grfMode
) ) ||
1054 (grfMode
& STGM_DELETEONRELEASE
) )
1056 WARN("bad grfMode: 0x%x\n", grfMode
);
1057 return STG_E_INVALIDFLAG
;
1061 return STG_E_REVERTED
;
1064 * Check that we're compatible with the parent's storage mode
1066 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1067 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1069 WARN("access denied\n");
1070 return STG_E_ACCESSDENIED
;
1073 currentEntryRef
= findElement(This
,
1074 This
->storageDirEntry
,
1078 if (currentEntryRef
!= DIRENTRY_NULL
)
1081 * An element with this name already exists
1083 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1084 ((This
->openFlags
& STGM_TRANSACTED
) ||
1085 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1087 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1093 WARN("file already exists\n");
1094 return STG_E_FILEALREADYEXISTS
;
1097 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1098 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1100 WARN("read-only storage\n");
1101 return STG_E_ACCESSDENIED
;
1104 memset(&newEntry
, 0, sizeof(DirEntry
));
1106 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1108 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1110 FIXME("name too long\n");
1111 return STG_E_INVALIDNAME
;
1114 strcpyW(newEntry
.name
, pwcsName
);
1116 newEntry
.stgType
= STGTY_STORAGE
;
1117 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1118 newEntry
.size
.u
.LowPart
= 0;
1119 newEntry
.size
.u
.HighPart
= 0;
1121 newEntry
.leftChild
= DIRENTRY_NULL
;
1122 newEntry
.rightChild
= DIRENTRY_NULL
;
1123 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1125 /* call CoFileTime to get the current time
1130 /* newEntry.clsid */
1133 * Create a new directory entry for the storage
1135 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1140 * Insert the new directory entry into the parent storage's tree
1142 hr
= insertIntoTree(
1144 This
->storageDirEntry
,
1148 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1153 * Open it to get a pointer to return.
1155 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1157 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1167 /***************************************************************************
1171 * Reserve a directory entry in the file and initialize it.
1173 static HRESULT
StorageImpl_CreateDirEntry(
1174 StorageBaseImpl
*base
,
1175 const DirEntry
*newData
,
1178 StorageImpl
*storage
= (StorageImpl
*)base
;
1179 ULONG currentEntryIndex
= 0;
1180 ULONG newEntryIndex
= DIRENTRY_NULL
;
1182 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1183 WORD sizeOfNameString
;
1187 hr
= StorageImpl_ReadRawDirEntry(storage
,
1193 StorageUtl_ReadWord(
1195 OFFSET_PS_NAMELENGTH
,
1198 if (sizeOfNameString
== 0)
1201 * The entry exists and is available, we found it.
1203 newEntryIndex
= currentEntryIndex
;
1209 * We exhausted the directory entries, we will create more space below
1211 newEntryIndex
= currentEntryIndex
;
1213 currentEntryIndex
++;
1215 } while (newEntryIndex
== DIRENTRY_NULL
);
1218 * grow the directory stream
1222 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1223 ULARGE_INTEGER newSize
;
1225 ULONG lastEntry
= 0;
1226 ULONG blockCount
= 0;
1229 * obtain the new count of blocks in the directory stream
1231 blockCount
= BlockChainStream_GetCount(
1232 storage
->rootBlockChain
)+1;
1235 * initialize the size used by the directory stream
1237 newSize
.u
.HighPart
= 0;
1238 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1241 * add a block to the directory stream
1243 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1246 * memset the empty entry in order to initialize the unused newly
1249 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1254 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1257 entryIndex
= newEntryIndex
+ 1;
1258 entryIndex
< lastEntry
;
1261 StorageImpl_WriteRawDirEntry(
1268 UpdateRawDirEntry(currentData
, newData
);
1270 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1273 *index
= newEntryIndex
;
1278 /***************************************************************************
1282 * Mark a directory entry in the file as free.
1284 static HRESULT
StorageImpl_DestroyDirEntry(
1285 StorageBaseImpl
*base
,
1289 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1290 StorageImpl
*storage
= (StorageImpl
*)base
;
1292 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1294 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1300 /***************************************************************************
1304 * Destroy an entry, its attached data, and all entries reachable from it.
1306 static HRESULT
DestroyReachableEntries(
1307 StorageBaseImpl
*base
,
1312 ULARGE_INTEGER zero
;
1316 if (index
!= DIRENTRY_NULL
)
1318 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1321 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1324 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1327 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1330 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1333 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1340 /****************************************************************************
1344 * Case insensitive comparison of DirEntry.name by first considering
1347 * Returns <0 when name1 < name2
1348 * >0 when name1 > name2
1349 * 0 when name1 == name2
1351 static LONG
entryNameCmp(
1352 const OLECHAR
*name1
,
1353 const OLECHAR
*name2
)
1355 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1357 while (diff
== 0 && *name1
!= 0)
1360 * We compare the string themselves only when they are of the same length
1362 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1368 /****************************************************************************
1372 * Add a directory entry to a storage
1374 static HRESULT
insertIntoTree(
1375 StorageBaseImpl
*This
,
1376 DirRef parentStorageIndex
,
1377 DirRef newEntryIndex
)
1379 DirEntry currentEntry
;
1383 * Read the inserted entry
1385 StorageBaseImpl_ReadDirEntry(This
,
1390 * Read the storage entry
1392 StorageBaseImpl_ReadDirEntry(This
,
1396 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1399 * The root storage contains some element, therefore, start the research
1400 * for the appropriate location.
1403 DirRef current
, next
, previous
, currentEntryId
;
1406 * Keep a reference to the root of the storage's element tree
1408 currentEntryId
= currentEntry
.dirRootEntry
;
1413 StorageBaseImpl_ReadDirEntry(This
,
1414 currentEntry
.dirRootEntry
,
1417 previous
= currentEntry
.leftChild
;
1418 next
= currentEntry
.rightChild
;
1419 current
= currentEntryId
;
1423 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1427 if (previous
!= DIRENTRY_NULL
)
1429 StorageBaseImpl_ReadDirEntry(This
,
1436 currentEntry
.leftChild
= newEntryIndex
;
1437 StorageBaseImpl_WriteDirEntry(This
,
1445 if (next
!= DIRENTRY_NULL
)
1447 StorageBaseImpl_ReadDirEntry(This
,
1454 currentEntry
.rightChild
= newEntryIndex
;
1455 StorageBaseImpl_WriteDirEntry(This
,
1464 * Trying to insert an item with the same name in the
1465 * subtree structure.
1467 return STG_E_FILEALREADYEXISTS
;
1470 previous
= currentEntry
.leftChild
;
1471 next
= currentEntry
.rightChild
;
1477 * The storage is empty, make the new entry the root of its element tree
1479 currentEntry
.dirRootEntry
= newEntryIndex
;
1480 StorageBaseImpl_WriteDirEntry(This
,
1488 /****************************************************************************
1492 * Find and read the element of a storage with the given name.
1494 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1495 const OLECHAR
*name
, DirEntry
*data
)
1497 DirRef currentEntry
;
1499 /* Read the storage entry to find the root of the tree. */
1500 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1502 currentEntry
= data
->dirRootEntry
;
1504 while (currentEntry
!= DIRENTRY_NULL
)
1508 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1510 cmp
= entryNameCmp(name
, data
->name
);
1517 currentEntry
= data
->leftChild
;
1520 currentEntry
= data
->rightChild
;
1523 return currentEntry
;
1526 /****************************************************************************
1530 * Find and read the binary tree parent of the element with the given name.
1532 * If there is no such element, find a place where it could be inserted and
1533 * return STG_E_FILENOTFOUND.
1535 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1536 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1542 /* Read the storage entry to find the root of the tree. */
1543 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1545 *parentEntry
= storageEntry
;
1546 *relation
= DIRENTRY_RELATION_DIR
;
1548 childEntry
= parentData
->dirRootEntry
;
1550 while (childEntry
!= DIRENTRY_NULL
)
1554 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1556 cmp
= entryNameCmp(childName
, childData
.name
);
1564 *parentData
= childData
;
1565 *parentEntry
= childEntry
;
1566 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1568 childEntry
= parentData
->leftChild
;
1573 *parentData
= childData
;
1574 *parentEntry
= childEntry
;
1575 *relation
= DIRENTRY_RELATION_NEXT
;
1577 childEntry
= parentData
->rightChild
;
1581 if (childEntry
== DIRENTRY_NULL
)
1582 return STG_E_FILENOTFOUND
;
1588 /*************************************************************************
1591 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1593 DWORD ciidExclude
, /* [in] */
1594 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1595 SNB snbExclude
, /* [unique][in] */
1596 IStorage
* pstgDest
) /* [unique][in] */
1598 IEnumSTATSTG
*elements
= 0;
1599 STATSTG curElement
, strStat
;
1601 IStorage
*pstgTmp
, *pstgChild
;
1602 IStream
*pstrTmp
, *pstrChild
;
1603 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1606 TRACE("(%p, %d, %p, %p, %p)\n",
1607 iface
, ciidExclude
, rgiidExclude
,
1608 snbExclude
, pstgDest
);
1610 if ( pstgDest
== 0 )
1611 return STG_E_INVALIDPOINTER
;
1614 * Enumerate the elements
1616 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1624 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1625 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1627 for(i
= 0; i
< ciidExclude
; ++i
)
1629 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1630 skip_storage
= TRUE
;
1631 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1634 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1640 * Obtain the next element
1642 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1644 if ( hr
== S_FALSE
)
1646 hr
= S_OK
; /* done, every element has been copied */
1652 WCHAR
**snb
= snbExclude
;
1654 while ( *snb
!= NULL
&& !skip
)
1656 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1665 if (curElement
.type
== STGTY_STORAGE
)
1671 * open child source storage
1673 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1674 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1675 NULL
, 0, &pstgChild
);
1681 * create a new storage in destination storage
1683 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1684 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1688 * if it already exist, don't create a new one use this one
1690 if (hr
== STG_E_FILEALREADYEXISTS
)
1692 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1693 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1694 NULL
, 0, &pstgTmp
);
1700 * do the copy recursively
1702 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1705 IStorage_Release( pstgTmp
);
1708 IStorage_Release( pstgChild
);
1710 else if (curElement
.type
== STGTY_STREAM
)
1716 * create a new stream in destination storage. If the stream already
1717 * exist, it will be deleted and a new one will be created.
1719 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1720 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1727 * open child stream storage
1729 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1730 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1736 * Get the size of the source stream
1738 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1741 * Set the size of the destination stream.
1743 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1748 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1751 IStream_Release( pstrChild
);
1754 IStream_Release( pstrTmp
);
1758 WARN("unknown element type: %d\n", curElement
.type
);
1762 CoTaskMemFree(curElement
.pwcsName
);
1763 } while (hr
== S_OK
);
1768 IEnumSTATSTG_Release(elements
);
1773 /*************************************************************************
1774 * MoveElementTo (IStorage)
1776 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1778 const OLECHAR
*pwcsName
, /* [string][in] */
1779 IStorage
*pstgDest
, /* [unique][in] */
1780 const OLECHAR
*pwcsNewName
,/* [string][in] */
1781 DWORD grfFlags
) /* [in] */
1783 FIXME("(%p %s %p %s %u): stub\n", iface
,
1784 debugstr_w(pwcsName
), pstgDest
,
1785 debugstr_w(pwcsNewName
), grfFlags
);
1789 /*************************************************************************
1792 * Ensures that any changes made to a storage object open in transacted mode
1793 * are reflected in the parent storage
1796 * Wine doesn't implement transacted mode, which seems to be a basic
1797 * optimization, so we can ignore this stub for now.
1799 static HRESULT WINAPI
StorageImpl_Commit(
1801 DWORD grfCommitFlags
)/* [in] */
1803 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1807 /*************************************************************************
1810 * Discard all changes that have been made since the last commit operation
1812 static HRESULT WINAPI
StorageImpl_Revert(
1815 TRACE("(%p)\n", iface
);
1819 /*************************************************************************
1820 * DestroyElement (IStorage)
1822 * Strategy: This implementation is built this way for simplicity not for speed.
1823 * I always delete the topmost element of the enumeration and adjust
1824 * the deleted element pointer all the time. This takes longer to
1825 * do but allow to reinvoke DestroyElement whenever we encounter a
1826 * storage object. The optimisation resides in the usage of another
1827 * enumeration strategy that would give all the leaves of a storage
1828 * first. (postfix order)
1830 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1832 const OLECHAR
*pwcsName
)/* [string][in] */
1834 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1837 DirEntry entryToDelete
;
1838 DirRef entryToDeleteRef
;
1841 iface
, debugstr_w(pwcsName
));
1844 return STG_E_INVALIDPOINTER
;
1847 return STG_E_REVERTED
;
1849 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1850 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1851 return STG_E_ACCESSDENIED
;
1853 entryToDeleteRef
= findElement(
1855 This
->storageDirEntry
,
1859 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1861 return STG_E_FILENOTFOUND
;
1864 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1866 hr
= deleteStorageContents(
1871 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1873 hr
= deleteStreamContents(
1883 * Remove the entry from its parent storage
1885 hr
= removeFromTree(
1887 This
->storageDirEntry
,
1891 * Invalidate the entry
1894 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1900 /******************************************************************************
1901 * Internal stream list handlers
1904 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1906 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1907 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1910 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1912 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1913 list_remove(&(strm
->StrmListEntry
));
1916 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1918 StgStreamImpl
*strm
;
1920 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1922 if (strm
->dirEntry
== streamEntry
)
1931 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1933 StorageInternalImpl
*childstg
;
1935 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1937 if (childstg
->base
.storageDirEntry
== storageEntry
)
1946 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1948 struct list
*cur
, *cur2
;
1949 StgStreamImpl
*strm
=NULL
;
1950 StorageInternalImpl
*childstg
=NULL
;
1952 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1953 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1954 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1955 strm
->parentStorage
= NULL
;
1959 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1960 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1961 StorageBaseImpl_Invalidate( &childstg
->base
);
1964 if (stg
->transactedChild
)
1966 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1968 stg
->transactedChild
= NULL
;
1973 /*********************************************************************
1977 * Delete the contents of a storage entry.
1980 static HRESULT
deleteStorageContents(
1981 StorageBaseImpl
*parentStorage
,
1982 DirRef indexToDelete
,
1983 DirEntry entryDataToDelete
)
1985 IEnumSTATSTG
*elements
= 0;
1986 IStorage
*childStorage
= 0;
1987 STATSTG currentElement
;
1989 HRESULT destroyHr
= S_OK
;
1990 StorageInternalImpl
*stg
, *stg2
;
1992 /* Invalidate any open storage objects. */
1993 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1995 if (stg
->base
.storageDirEntry
== indexToDelete
)
1997 StorageBaseImpl_Invalidate(&stg
->base
);
2002 * Open the storage and enumerate it
2004 hr
= StorageBaseImpl_OpenStorage(
2005 (IStorage
*)parentStorage
,
2006 entryDataToDelete
.name
,
2008 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2019 * Enumerate the elements
2021 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2026 * Obtain the next element
2028 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2031 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2033 CoTaskMemFree(currentElement
.pwcsName
);
2037 * We need to Reset the enumeration every time because we delete elements
2038 * and the enumeration could be invalid
2040 IEnumSTATSTG_Reset(elements
);
2042 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2044 IStorage_Release(childStorage
);
2045 IEnumSTATSTG_Release(elements
);
2050 /*********************************************************************
2054 * Perform the deletion of a stream's data
2057 static HRESULT
deleteStreamContents(
2058 StorageBaseImpl
*parentStorage
,
2059 DirRef indexToDelete
,
2060 DirEntry entryDataToDelete
)
2064 ULARGE_INTEGER size
;
2065 StgStreamImpl
*strm
, *strm2
;
2067 /* Invalidate any open stream objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2070 if (strm
->dirEntry
== indexToDelete
)
2072 TRACE("Stream deleted %p\n", strm
);
2073 strm
->parentStorage
= NULL
;
2074 list_remove(&strm
->StrmListEntry
);
2078 size
.u
.HighPart
= 0;
2081 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2082 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2092 hr
= IStream_SetSize(pis
, size
);
2100 * Release the stream object.
2102 IStream_Release(pis
);
2107 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2111 case DIRENTRY_RELATION_PREVIOUS
:
2112 entry
->leftChild
= new_target
;
2114 case DIRENTRY_RELATION_NEXT
:
2115 entry
->rightChild
= new_target
;
2117 case DIRENTRY_RELATION_DIR
:
2118 entry
->dirRootEntry
= new_target
;
2125 /*************************************************************************
2129 * This method removes a directory entry from its parent storage tree without
2130 * freeing any resources attached to it.
2132 static HRESULT
removeFromTree(
2133 StorageBaseImpl
*This
,
2134 DirRef parentStorageIndex
,
2135 DirRef deletedIndex
)
2138 DirEntry entryToDelete
;
2139 DirEntry parentEntry
;
2140 DirRef parentEntryRef
;
2141 ULONG typeOfRelation
;
2143 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2149 * Find the element that links to the one we want to delete.
2151 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2152 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2157 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2160 * Replace the deleted entry with its left child
2162 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2164 hr
= StorageBaseImpl_WriteDirEntry(
2173 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2176 * We need to reinsert the right child somewhere. We already know it and
2177 * its children are greater than everything in the left tree, so we
2178 * insert it at the rightmost point in the left tree.
2180 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2181 DirEntry newRightChildParentEntry
;
2185 hr
= StorageBaseImpl_ReadDirEntry(
2187 newRightChildParent
,
2188 &newRightChildParentEntry
);
2194 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2195 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2196 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2198 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2200 hr
= StorageBaseImpl_WriteDirEntry(
2202 newRightChildParent
,
2203 &newRightChildParentEntry
);
2213 * Replace the deleted entry with its right child
2215 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2217 hr
= StorageBaseImpl_WriteDirEntry(
2231 /******************************************************************************
2232 * SetElementTimes (IStorage)
2234 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2236 const OLECHAR
*pwcsName
,/* [string][in] */
2237 const FILETIME
*pctime
, /* [in] */
2238 const FILETIME
*patime
, /* [in] */
2239 const FILETIME
*pmtime
) /* [in] */
2241 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2245 /******************************************************************************
2246 * SetStateBits (IStorage)
2248 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2250 DWORD grfStateBits
,/* [in] */
2251 DWORD grfMask
) /* [in] */
2253 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2256 return STG_E_REVERTED
;
2258 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2262 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2263 DirRef index
, const DirEntry
*data
)
2265 StorageImpl
*This
= (StorageImpl
*)base
;
2266 return StorageImpl_WriteDirEntry(This
, index
, data
);
2269 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2270 DirRef index
, DirEntry
*data
)
2272 StorageImpl
*This
= (StorageImpl
*)base
;
2273 return StorageImpl_ReadDirEntry(This
, index
, data
);
2276 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2280 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2282 if (!This
->blockChainCache
[i
])
2284 return &This
->blockChainCache
[i
];
2288 i
= This
->blockChainToEvict
;
2290 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2291 This
->blockChainCache
[i
] = NULL
;
2293 This
->blockChainToEvict
++;
2294 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2295 This
->blockChainToEvict
= 0;
2297 return &This
->blockChainCache
[i
];
2300 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2303 int i
, free_index
=-1;
2305 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2307 if (!This
->blockChainCache
[i
])
2309 if (free_index
== -1) free_index
= i
;
2311 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2313 return &This
->blockChainCache
[i
];
2317 if (free_index
== -1)
2319 free_index
= This
->blockChainToEvict
;
2321 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2322 This
->blockChainCache
[free_index
] = NULL
;
2324 This
->blockChainToEvict
++;
2325 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2326 This
->blockChainToEvict
= 0;
2329 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2330 return &This
->blockChainCache
[free_index
];
2333 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2334 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2336 StorageImpl
*This
= (StorageImpl
*)base
;
2341 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2342 if (FAILED(hr
)) return hr
;
2344 if (data
.size
.QuadPart
== 0)
2350 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2352 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2359 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2361 SmallBlockChainStream
*stream
;
2363 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2364 if (!stream
) return E_OUTOFMEMORY
;
2366 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2368 SmallBlockChainStream_Destroy(stream
);
2374 BlockChainStream
*stream
= NULL
;
2376 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2377 if (!stream
) return E_OUTOFMEMORY
;
2379 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2385 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2386 ULARGE_INTEGER newsize
)
2388 StorageImpl
*This
= (StorageImpl
*)base
;
2391 SmallBlockChainStream
*smallblock
=NULL
;
2392 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2394 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2395 if (FAILED(hr
)) return hr
;
2397 /* In simple mode keep the stream size above the small block limit */
2398 if (This
->base
.openFlags
& STGM_SIMPLE
)
2399 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2401 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2404 /* Create a block chain object of the appropriate type */
2405 if (data
.size
.QuadPart
== 0)
2407 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2409 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2410 if (!smallblock
) return E_OUTOFMEMORY
;
2414 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2415 bigblock
= *pbigblock
;
2416 if (!bigblock
) return E_OUTOFMEMORY
;
2419 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2421 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2422 if (!smallblock
) return E_OUTOFMEMORY
;
2426 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2427 bigblock
= *pbigblock
;
2428 if (!bigblock
) return E_OUTOFMEMORY
;
2431 /* Change the block chain type if necessary. */
2432 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2434 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2437 SmallBlockChainStream_Destroy(smallblock
);
2441 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2442 *pbigblock
= bigblock
;
2444 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2446 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2451 /* Set the size of the block chain. */
2454 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2455 SmallBlockChainStream_Destroy(smallblock
);
2459 BlockChainStream_SetSize(bigblock
, newsize
);
2462 /* Set the size in the directory entry. */
2463 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2466 data
.size
= newsize
;
2468 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2473 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2474 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2476 StorageImpl
*This
= (StorageImpl
*)base
;
2479 ULARGE_INTEGER newSize
;
2481 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2482 if (FAILED(hr
)) return hr
;
2484 /* Grow the stream if necessary */
2485 newSize
.QuadPart
= 0;
2486 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2488 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2490 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2494 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2495 if (FAILED(hr
)) return hr
;
2498 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2500 SmallBlockChainStream
*stream
;
2502 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2503 if (!stream
) return E_OUTOFMEMORY
;
2505 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2507 SmallBlockChainStream_Destroy(stream
);
2513 BlockChainStream
*stream
;
2515 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2516 if (!stream
) return E_OUTOFMEMORY
;
2518 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2525 * Virtual function table for the IStorage32Impl class.
2527 static const IStorageVtbl Storage32Impl_Vtbl
=
2529 StorageBaseImpl_QueryInterface
,
2530 StorageBaseImpl_AddRef
,
2531 StorageBaseImpl_Release
,
2532 StorageBaseImpl_CreateStream
,
2533 StorageBaseImpl_OpenStream
,
2534 StorageBaseImpl_CreateStorage
,
2535 StorageBaseImpl_OpenStorage
,
2536 StorageBaseImpl_CopyTo
,
2537 StorageBaseImpl_MoveElementTo
,
2540 StorageBaseImpl_EnumElements
,
2541 StorageBaseImpl_DestroyElement
,
2542 StorageBaseImpl_RenameElement
,
2543 StorageBaseImpl_SetElementTimes
,
2544 StorageBaseImpl_SetClass
,
2545 StorageBaseImpl_SetStateBits
,
2546 StorageBaseImpl_Stat
2549 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2551 StorageImpl_Destroy
,
2552 StorageImpl_Invalidate
,
2553 StorageImpl_CreateDirEntry
,
2554 StorageImpl_BaseWriteDirEntry
,
2555 StorageImpl_BaseReadDirEntry
,
2556 StorageImpl_DestroyDirEntry
,
2557 StorageImpl_StreamReadAt
,
2558 StorageImpl_StreamWriteAt
,
2559 StorageImpl_StreamSetSize
2562 static HRESULT
StorageImpl_Construct(
2569 StorageImpl
** result
)
2573 DirEntry currentEntry
;
2574 DirRef currentEntryRef
;
2575 WCHAR fullpath
[MAX_PATH
];
2577 if ( FAILED( validateSTGM(openFlags
) ))
2578 return STG_E_INVALIDFLAG
;
2580 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2582 return E_OUTOFMEMORY
;
2584 memset(This
, 0, sizeof(StorageImpl
));
2586 list_init(&This
->base
.strmHead
);
2588 list_init(&This
->base
.storageHead
);
2590 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2591 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2592 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2593 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2595 This
->base
.create
= create
;
2597 This
->base
.reverted
= 0;
2599 This
->hFile
= hFile
;
2602 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2604 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2606 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2607 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2608 if (!This
->pwcsName
)
2610 hr
= STG_E_INSUFFICIENTMEMORY
;
2613 strcpyW(This
->pwcsName
, fullpath
);
2614 This
->base
.filename
= This
->pwcsName
;
2618 * Initialize the big block cache.
2620 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2621 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2622 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2628 if (This
->bigBlockFile
== 0)
2636 ULARGE_INTEGER size
;
2637 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2640 * Initialize all header variables:
2641 * - The big block depot consists of one block and it is at block 0
2642 * - The directory table starts at block 1
2643 * - There is no small block depot
2645 memset( This
->bigBlockDepotStart
,
2647 sizeof(This
->bigBlockDepotStart
));
2649 This
->bigBlockDepotCount
= 1;
2650 This
->bigBlockDepotStart
[0] = 0;
2651 This
->rootStartBlock
= 1;
2652 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2653 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2654 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2655 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2656 This
->extBigBlockDepotCount
= 0;
2658 StorageImpl_SaveFileHeader(This
);
2661 * Add one block for the big block depot and one block for the directory table
2663 size
.u
.HighPart
= 0;
2664 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2665 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2668 * Initialize the big block depot
2670 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2671 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2672 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2673 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2678 * Load the header for the file.
2680 hr
= StorageImpl_LoadFileHeader(This
);
2689 * There is no block depot cached yet.
2691 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2694 * Start searching for free blocks with block 0.
2696 This
->prevFreeBlock
= 0;
2699 * Create the block chain abstractions.
2701 if(!(This
->rootBlockChain
=
2702 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2704 hr
= STG_E_READFAULT
;
2708 if(!(This
->smallBlockDepotChain
=
2709 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2712 hr
= STG_E_READFAULT
;
2717 * Write the root storage entry (memory only)
2723 * Initialize the directory table
2725 memset(&rootEntry
, 0, sizeof(rootEntry
));
2726 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2727 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2728 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2729 rootEntry
.stgType
= STGTY_ROOT
;
2730 rootEntry
.leftChild
= DIRENTRY_NULL
;
2731 rootEntry
.rightChild
= DIRENTRY_NULL
;
2732 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2733 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2734 rootEntry
.size
.u
.HighPart
= 0;
2735 rootEntry
.size
.u
.LowPart
= 0;
2737 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2741 * Find the ID of the root storage.
2743 currentEntryRef
= 0;
2747 hr
= StorageImpl_ReadDirEntry(
2754 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2755 (currentEntry
.stgType
== STGTY_ROOT
) )
2757 This
->base
.storageDirEntry
= currentEntryRef
;
2763 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2767 hr
= STG_E_READFAULT
;
2772 * Create the block chain abstraction for the small block root chain.
2774 if(!(This
->smallBlockRootChain
=
2775 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2777 hr
= STG_E_READFAULT
;
2783 IStorage_Release((IStorage
*)This
);
2792 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2794 StorageImpl
*This
= (StorageImpl
*) iface
;
2796 StorageBaseImpl_DeleteAll(&This
->base
);
2798 This
->base
.reverted
= 1;
2801 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2803 StorageImpl
*This
= (StorageImpl
*) iface
;
2805 TRACE("(%p)\n", This
);
2807 StorageImpl_Invalidate(iface
);
2809 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2811 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2812 BlockChainStream_Destroy(This
->rootBlockChain
);
2813 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2815 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2816 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2818 if (This
->bigBlockFile
)
2819 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2820 HeapFree(GetProcessHeap(), 0, This
);
2823 /******************************************************************************
2824 * Storage32Impl_GetNextFreeBigBlock
2826 * Returns the index of the next free big block.
2827 * If the big block depot is filled, this method will enlarge it.
2830 static ULONG
StorageImpl_GetNextFreeBigBlock(
2833 ULONG depotBlockIndexPos
;
2834 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2836 ULONG depotBlockOffset
;
2837 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2838 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2840 ULONG freeBlock
= BLOCK_UNUSED
;
2842 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2843 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2846 * Scan the entire big block depot until we find a block marked free
2848 while (nextBlockIndex
!= BLOCK_UNUSED
)
2850 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2852 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2855 * Grow the primary depot.
2857 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2859 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2862 * Add a block depot.
2864 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2865 This
->bigBlockDepotCount
++;
2866 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2869 * Flag it as a block depot.
2871 StorageImpl_SetNextBlockInChain(This
,
2875 /* Save new header information.
2877 StorageImpl_SaveFileHeader(This
);
2882 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2884 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2887 * Grow the extended depot.
2889 ULONG extIndex
= BLOCK_UNUSED
;
2890 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2891 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2893 if (extBlockOffset
== 0)
2895 /* We need an extended block.
2897 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2898 This
->extBigBlockDepotCount
++;
2899 depotBlockIndexPos
= extIndex
+ 1;
2902 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2905 * Add a block depot and mark it in the extended block.
2907 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2908 This
->bigBlockDepotCount
++;
2909 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2911 /* Flag the block depot.
2913 StorageImpl_SetNextBlockInChain(This
,
2917 /* If necessary, flag the extended depot block.
2919 if (extIndex
!= BLOCK_UNUSED
)
2920 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2922 /* Save header information.
2924 StorageImpl_SaveFileHeader(This
);
2928 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2932 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2933 ( nextBlockIndex
!= BLOCK_UNUSED
))
2935 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2937 if (nextBlockIndex
== BLOCK_UNUSED
)
2939 freeBlock
= (depotIndex
* blocksPerDepot
) +
2940 (depotBlockOffset
/sizeof(ULONG
));
2943 depotBlockOffset
+= sizeof(ULONG
);
2948 depotBlockOffset
= 0;
2952 * make sure that the block physically exists before using it
2954 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2956 This
->prevFreeBlock
= freeBlock
;
2961 /******************************************************************************
2962 * Storage32Impl_AddBlockDepot
2964 * This will create a depot block, essentially it is a block initialized
2967 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2969 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2972 * Initialize blocks as free
2974 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2975 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2978 /******************************************************************************
2979 * Storage32Impl_GetExtDepotBlock
2981 * Returns the index of the block that corresponds to the specified depot
2982 * index. This method is only for depot indexes equal or greater than
2983 * COUNT_BBDEPOTINHEADER.
2985 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2987 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2988 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2989 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2990 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2991 ULONG blockIndex
= BLOCK_UNUSED
;
2992 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2994 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2996 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2997 return BLOCK_UNUSED
;
2999 while (extBlockCount
> 0)
3001 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3005 if (extBlockIndex
!= BLOCK_UNUSED
)
3006 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3007 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3012 /******************************************************************************
3013 * Storage32Impl_SetExtDepotBlock
3015 * Associates the specified block index to the specified depot index.
3016 * This method is only for depot indexes equal or greater than
3017 * COUNT_BBDEPOTINHEADER.
3019 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3021 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3022 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3023 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3024 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3025 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3027 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3029 while (extBlockCount
> 0)
3031 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3035 if (extBlockIndex
!= BLOCK_UNUSED
)
3037 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3038 extBlockOffset
* sizeof(ULONG
),
3043 /******************************************************************************
3044 * Storage32Impl_AddExtBlockDepot
3046 * Creates an extended depot block.
3048 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3050 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3051 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3052 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3053 ULONG index
= BLOCK_UNUSED
;
3054 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3055 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3056 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3058 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3059 blocksPerDepotBlock
;
3061 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3064 * The first extended block.
3066 This
->extBigBlockDepotStart
= index
;
3072 * Follow the chain to the last one.
3074 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3076 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3080 * Add the new extended block to the chain.
3082 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3087 * Initialize this block.
3089 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3090 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3095 /******************************************************************************
3096 * Storage32Impl_FreeBigBlock
3098 * This method will flag the specified block as free in the big block depot.
3100 static void StorageImpl_FreeBigBlock(
3104 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3106 if (blockIndex
< This
->prevFreeBlock
)
3107 This
->prevFreeBlock
= blockIndex
;
3110 /************************************************************************
3111 * Storage32Impl_GetNextBlockInChain
3113 * This method will retrieve the block index of the next big block in
3116 * Params: This - Pointer to the Storage object.
3117 * blockIndex - Index of the block to retrieve the chain
3119 * nextBlockIndex - receives the return value.
3121 * Returns: This method returns the index of the next block in the chain.
3122 * It will return the constants:
3123 * BLOCK_SPECIAL - If the block given was not part of a
3125 * BLOCK_END_OF_CHAIN - If the block given was the last in
3127 * BLOCK_UNUSED - If the block given was not past of a chain
3129 * BLOCK_EXTBBDEPOT - This block is part of the extended
3132 * See Windows documentation for more details on IStorage methods.
3134 static HRESULT
StorageImpl_GetNextBlockInChain(
3137 ULONG
* nextBlockIndex
)
3139 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3140 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3141 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3142 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3144 ULONG depotBlockIndexPos
;
3147 *nextBlockIndex
= BLOCK_SPECIAL
;
3149 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3151 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3152 This
->bigBlockDepotCount
);
3153 return STG_E_READFAULT
;
3157 * Cache the currently accessed depot block.
3159 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3161 This
->indexBlockDepotCached
= depotBlockCount
;
3163 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3165 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3170 * We have to look in the extended depot.
3172 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3175 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3178 return STG_E_READFAULT
;
3180 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3182 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3183 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3187 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3192 /******************************************************************************
3193 * Storage32Impl_GetNextExtendedBlock
3195 * Given an extended block this method will return the next extended block.
3198 * The last ULONG of an extended block is the block index of the next
3199 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3203 * - The index of the next extended block
3204 * - BLOCK_UNUSED: there is no next extended block.
3205 * - Any other return values denotes failure.
3207 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3209 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3210 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3212 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3215 return nextBlockIndex
;
3218 /******************************************************************************
3219 * Storage32Impl_SetNextBlockInChain
3221 * This method will write the index of the specified block's next block
3222 * in the big block depot.
3224 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3227 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3228 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3229 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3232 static void StorageImpl_SetNextBlockInChain(
3237 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3238 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3239 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3240 ULONG depotBlockIndexPos
;
3242 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3243 assert(blockIndex
!= nextBlock
);
3245 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3247 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3252 * We have to look in the extended depot.
3254 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3257 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3260 * Update the cached block depot, if necessary.
3262 if (depotBlockCount
== This
->indexBlockDepotCached
)
3264 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3268 /******************************************************************************
3269 * Storage32Impl_LoadFileHeader
3271 * This method will read in the file header, i.e. big block index -1.
3273 static HRESULT
StorageImpl_LoadFileHeader(
3276 HRESULT hr
= STG_E_FILENOTFOUND
;
3277 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3283 * Get a pointer to the big block of data containing the header.
3285 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3288 * Extract the information from the header.
3293 * Check for the "magic number" signature and return an error if it is not
3296 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3298 return STG_E_OLDFORMAT
;
3301 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3303 return STG_E_INVALIDHEADER
;
3306 StorageUtl_ReadWord(
3308 OFFSET_BIGBLOCKSIZEBITS
,
3309 &This
->bigBlockSizeBits
);
3311 StorageUtl_ReadWord(
3313 OFFSET_SMALLBLOCKSIZEBITS
,
3314 &This
->smallBlockSizeBits
);
3316 StorageUtl_ReadDWord(
3318 OFFSET_BBDEPOTCOUNT
,
3319 &This
->bigBlockDepotCount
);
3321 StorageUtl_ReadDWord(
3323 OFFSET_ROOTSTARTBLOCK
,
3324 &This
->rootStartBlock
);
3326 StorageUtl_ReadDWord(
3328 OFFSET_SBDEPOTSTART
,
3329 &This
->smallBlockDepotStart
);
3331 StorageUtl_ReadDWord(
3333 OFFSET_EXTBBDEPOTSTART
,
3334 &This
->extBigBlockDepotStart
);
3336 StorageUtl_ReadDWord(
3338 OFFSET_EXTBBDEPOTCOUNT
,
3339 &This
->extBigBlockDepotCount
);
3341 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3343 StorageUtl_ReadDWord(
3345 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3346 &(This
->bigBlockDepotStart
[index
]));
3350 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3352 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3353 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3356 * Right now, the code is making some assumptions about the size of the
3357 * blocks, just make sure they are what we're expecting.
3359 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3360 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3362 WARN("Broken OLE storage file\n");
3363 hr
= STG_E_INVALIDHEADER
;
3372 /******************************************************************************
3373 * Storage32Impl_SaveFileHeader
3375 * This method will save to the file the header, i.e. big block -1.
3377 static void StorageImpl_SaveFileHeader(
3380 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3385 * Get a pointer to the big block of data containing the header.
3387 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3390 * If the block read failed, the file is probably new.
3395 * Initialize for all unknown fields.
3397 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3400 * Initialize the magic number.
3402 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3405 * And a bunch of things we don't know what they mean
3407 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3408 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3409 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3410 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3414 * Write the information to the header.
3416 StorageUtl_WriteWord(
3418 OFFSET_BIGBLOCKSIZEBITS
,
3419 This
->bigBlockSizeBits
);
3421 StorageUtl_WriteWord(
3423 OFFSET_SMALLBLOCKSIZEBITS
,
3424 This
->smallBlockSizeBits
);
3426 StorageUtl_WriteDWord(
3428 OFFSET_BBDEPOTCOUNT
,
3429 This
->bigBlockDepotCount
);
3431 StorageUtl_WriteDWord(
3433 OFFSET_ROOTSTARTBLOCK
,
3434 This
->rootStartBlock
);
3436 StorageUtl_WriteDWord(
3438 OFFSET_SBDEPOTSTART
,
3439 This
->smallBlockDepotStart
);
3441 StorageUtl_WriteDWord(
3443 OFFSET_SBDEPOTCOUNT
,
3444 This
->smallBlockDepotChain
?
3445 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3447 StorageUtl_WriteDWord(
3449 OFFSET_EXTBBDEPOTSTART
,
3450 This
->extBigBlockDepotStart
);
3452 StorageUtl_WriteDWord(
3454 OFFSET_EXTBBDEPOTCOUNT
,
3455 This
->extBigBlockDepotCount
);
3457 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3459 StorageUtl_WriteDWord(
3461 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3462 (This
->bigBlockDepotStart
[index
]));
3466 * Write the big block back to the file.
3468 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3471 /******************************************************************************
3472 * StorageImpl_ReadRawDirEntry
3474 * This method will read the raw data from a directory entry in the file.
3476 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3478 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3480 ULARGE_INTEGER offset
;
3484 offset
.u
.HighPart
= 0;
3485 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3487 hr
= BlockChainStream_ReadAt(
3488 This
->rootBlockChain
,
3497 /******************************************************************************
3498 * StorageImpl_WriteRawDirEntry
3500 * This method will write the raw data from a directory entry in the file.
3502 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3504 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3506 ULARGE_INTEGER offset
;
3510 offset
.u
.HighPart
= 0;
3511 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3513 hr
= BlockChainStream_WriteAt(
3514 This
->rootBlockChain
,
3523 /******************************************************************************
3526 * Update raw directory entry data from the fields in newData.
3528 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3530 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3532 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3535 buffer
+ OFFSET_PS_NAME
,
3537 DIRENTRY_NAME_BUFFER_LEN
);
3539 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3541 StorageUtl_WriteWord(
3543 OFFSET_PS_NAMELENGTH
,
3544 newData
->sizeOfNameString
);
3546 StorageUtl_WriteDWord(
3548 OFFSET_PS_LEFTCHILD
,
3549 newData
->leftChild
);
3551 StorageUtl_WriteDWord(
3553 OFFSET_PS_RIGHTCHILD
,
3554 newData
->rightChild
);
3556 StorageUtl_WriteDWord(
3559 newData
->dirRootEntry
);
3561 StorageUtl_WriteGUID(
3566 StorageUtl_WriteDWord(
3569 newData
->ctime
.dwLowDateTime
);
3571 StorageUtl_WriteDWord(
3573 OFFSET_PS_CTIMEHIGH
,
3574 newData
->ctime
.dwHighDateTime
);
3576 StorageUtl_WriteDWord(
3579 newData
->mtime
.dwLowDateTime
);
3581 StorageUtl_WriteDWord(
3583 OFFSET_PS_MTIMEHIGH
,
3584 newData
->ctime
.dwHighDateTime
);
3586 StorageUtl_WriteDWord(
3588 OFFSET_PS_STARTBLOCK
,
3589 newData
->startingBlock
);
3591 StorageUtl_WriteDWord(
3594 newData
->size
.u
.LowPart
);
3597 /******************************************************************************
3598 * Storage32Impl_ReadDirEntry
3600 * This method will read the specified directory entry.
3602 HRESULT
StorageImpl_ReadDirEntry(
3607 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3610 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3612 if (SUCCEEDED(readRes
))
3614 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3617 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3618 DIRENTRY_NAME_BUFFER_LEN
);
3619 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3621 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3623 StorageUtl_ReadWord(
3625 OFFSET_PS_NAMELENGTH
,
3626 &buffer
->sizeOfNameString
);
3628 StorageUtl_ReadDWord(
3630 OFFSET_PS_LEFTCHILD
,
3631 &buffer
->leftChild
);
3633 StorageUtl_ReadDWord(
3635 OFFSET_PS_RIGHTCHILD
,
3636 &buffer
->rightChild
);
3638 StorageUtl_ReadDWord(
3641 &buffer
->dirRootEntry
);
3643 StorageUtl_ReadGUID(
3648 StorageUtl_ReadDWord(
3651 &buffer
->ctime
.dwLowDateTime
);
3653 StorageUtl_ReadDWord(
3655 OFFSET_PS_CTIMEHIGH
,
3656 &buffer
->ctime
.dwHighDateTime
);
3658 StorageUtl_ReadDWord(
3661 &buffer
->mtime
.dwLowDateTime
);
3663 StorageUtl_ReadDWord(
3665 OFFSET_PS_MTIMEHIGH
,
3666 &buffer
->mtime
.dwHighDateTime
);
3668 StorageUtl_ReadDWord(
3670 OFFSET_PS_STARTBLOCK
,
3671 &buffer
->startingBlock
);
3673 StorageUtl_ReadDWord(
3676 &buffer
->size
.u
.LowPart
);
3678 buffer
->size
.u
.HighPart
= 0;
3684 /*********************************************************************
3685 * Write the specified directory entry to the file
3687 HRESULT
StorageImpl_WriteDirEntry(
3690 const DirEntry
* buffer
)
3692 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3695 UpdateRawDirEntry(currentEntry
, buffer
);
3697 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3701 static BOOL
StorageImpl_ReadBigBlock(
3706 ULARGE_INTEGER ulOffset
;
3709 ulOffset
.u
.HighPart
= 0;
3710 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3712 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3713 return (read
== This
->bigBlockSize
);
3716 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3722 ULARGE_INTEGER ulOffset
;
3726 ulOffset
.u
.HighPart
= 0;
3727 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3728 ulOffset
.u
.LowPart
+= offset
;
3730 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3731 *value
= lendian32toh(tmp
);
3732 return (read
== sizeof(DWORD
));
3735 static BOOL
StorageImpl_WriteBigBlock(
3740 ULARGE_INTEGER ulOffset
;
3743 ulOffset
.u
.HighPart
= 0;
3744 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3746 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3747 return (wrote
== This
->bigBlockSize
);
3750 static BOOL
StorageImpl_WriteDWordToBigBlock(
3756 ULARGE_INTEGER ulOffset
;
3759 ulOffset
.u
.HighPart
= 0;
3760 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3761 ulOffset
.u
.LowPart
+= offset
;
3763 value
= htole32(value
);
3764 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3765 return (wrote
== sizeof(DWORD
));
3768 /******************************************************************************
3769 * Storage32Impl_SmallBlocksToBigBlocks
3771 * This method will convert a small block chain to a big block chain.
3772 * The small block chain will be destroyed.
3774 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3776 SmallBlockChainStream
** ppsbChain
)
3778 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3779 ULARGE_INTEGER size
, offset
;
3780 ULONG cbRead
, cbWritten
;
3781 ULARGE_INTEGER cbTotalRead
;
3782 DirRef streamEntryRef
;
3783 HRESULT resWrite
= S_OK
;
3785 DirEntry streamEntry
;
3787 BlockChainStream
*bbTempChain
= NULL
;
3788 BlockChainStream
*bigBlockChain
= NULL
;
3791 * Create a temporary big block chain that doesn't have
3792 * an associated directory entry. This temporary chain will be
3793 * used to copy data from small blocks to big blocks.
3795 bbTempChain
= BlockChainStream_Construct(This
,
3798 if(!bbTempChain
) return NULL
;
3800 * Grow the big block chain.
3802 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3803 BlockChainStream_SetSize(bbTempChain
, size
);
3806 * Copy the contents of the small block chain to the big block chain
3807 * by small block size increments.
3809 offset
.u
.LowPart
= 0;
3810 offset
.u
.HighPart
= 0;
3811 cbTotalRead
.QuadPart
= 0;
3813 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3816 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3818 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3821 if (FAILED(resRead
))
3826 cbTotalRead
.QuadPart
+= cbRead
;
3828 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3834 if (FAILED(resWrite
))
3837 offset
.u
.LowPart
+= cbRead
;
3839 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3840 HeapFree(GetProcessHeap(),0,buffer
);
3842 size
.u
.HighPart
= 0;
3845 if (FAILED(resRead
) || FAILED(resWrite
))
3847 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3848 BlockChainStream_SetSize(bbTempChain
, size
);
3849 BlockChainStream_Destroy(bbTempChain
);
3854 * Destroy the small block chain.
3856 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3857 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3858 SmallBlockChainStream_Destroy(*ppsbChain
);
3862 * Change the directory entry. This chain is now a big block chain
3863 * and it doesn't reside in the small blocks chain anymore.
3865 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3867 streamEntry
.startingBlock
= bbHeadOfChain
;
3869 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3872 * Destroy the temporary entryless big block chain.
3873 * Create a new big block chain associated with this entry.
3875 BlockChainStream_Destroy(bbTempChain
);
3876 bigBlockChain
= BlockChainStream_Construct(This
,
3880 return bigBlockChain
;
3883 /******************************************************************************
3884 * Storage32Impl_BigBlocksToSmallBlocks
3886 * This method will convert a big block chain to a small block chain.
3887 * The big block chain will be destroyed on success.
3889 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3891 BlockChainStream
** ppbbChain
)
3893 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3894 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3895 DirRef streamEntryRef
;
3896 HRESULT resWrite
= S_OK
, resRead
;
3897 DirEntry streamEntry
;
3899 SmallBlockChainStream
* sbTempChain
;
3901 TRACE("%p %p\n", This
, ppbbChain
);
3903 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3909 size
= BlockChainStream_GetSize(*ppbbChain
);
3910 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3912 offset
.u
.HighPart
= 0;
3913 offset
.u
.LowPart
= 0;
3914 cbTotalRead
.QuadPart
= 0;
3915 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3918 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3919 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3927 cbTotalRead
.QuadPart
+= cbRead
;
3929 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3930 cbRead
, buffer
, &cbWritten
);
3932 if(FAILED(resWrite
))
3935 offset
.u
.LowPart
+= cbRead
;
3937 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3938 HeapFree(GetProcessHeap(), 0, buffer
);
3940 size
.u
.HighPart
= 0;
3943 if(FAILED(resRead
) || FAILED(resWrite
))
3945 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3946 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3947 SmallBlockChainStream_Destroy(sbTempChain
);
3951 /* destroy the original big block chain */
3952 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3953 BlockChainStream_SetSize(*ppbbChain
, size
);
3954 BlockChainStream_Destroy(*ppbbChain
);
3957 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3958 streamEntry
.startingBlock
= sbHeadOfChain
;
3959 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3961 SmallBlockChainStream_Destroy(sbTempChain
);
3962 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3965 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3968 DirEntry parentData
, snapshotData
;
3970 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3971 0, (IStorage
**)snapshot
);
3975 hr
= StorageBaseImpl_ReadDirEntry(original
,
3976 original
->storageDirEntry
, &parentData
);
3979 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3980 (*snapshot
)->storageDirEntry
, &snapshotData
);
3984 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3985 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3986 snapshotData
.stgType
= parentData
.stgType
;
3987 snapshotData
.clsid
= parentData
.clsid
;
3988 snapshotData
.ctime
= parentData
.ctime
;
3989 snapshotData
.mtime
= parentData
.mtime
;
3990 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3991 (*snapshot
)->storageDirEntry
, &snapshotData
);
3995 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
3996 (IStorage
*)(*snapshot
));
3998 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
4004 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4006 DWORD grfCommitFlags
) /* [in] */
4008 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4010 DirEntry data
, tempStorageData
, snapshotRootData
;
4011 DirRef tempStorageEntry
, oldDirRoot
;
4012 StorageInternalImpl
*tempStorage
;
4014 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4016 /* Cannot commit a read-only transacted storage */
4017 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4018 return STG_E_ACCESSDENIED
;
4020 /* To prevent data loss, we create the new structure in the file before we
4021 * delete the old one, so that in case of errors the old data is intact. We
4022 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4023 * needed in the rare situation where we have just enough free disk space to
4024 * overwrite the existing data. */
4026 /* Create an orphaned storage in the parent for the new directory structure. */
4027 memset(&data
, 0, sizeof(data
));
4029 data
.sizeOfNameString
= 1;
4030 data
.stgType
= STGTY_STORAGE
;
4031 data
.leftChild
= DIRENTRY_NULL
;
4032 data
.rightChild
= DIRENTRY_NULL
;
4033 data
.dirRootEntry
= DIRENTRY_NULL
;
4034 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4036 if (FAILED(hr
)) return hr
;
4038 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4039 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4042 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4043 (IStorage
*)tempStorage
);
4045 list_init(&tempStorage
->ParentListEntry
);
4047 IStorage_Release((IStorage
*) tempStorage
);
4054 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4058 /* Update the storage to use the new data in one step. */
4059 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4060 This
->transactedParent
->storageDirEntry
, &data
);
4064 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4065 tempStorageEntry
, &tempStorageData
);
4070 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4071 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4076 oldDirRoot
= data
.dirRootEntry
;
4077 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4078 data
.clsid
= snapshotRootData
.clsid
;
4079 data
.ctime
= snapshotRootData
.ctime
;
4080 data
.mtime
= snapshotRootData
.mtime
;
4082 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4083 This
->transactedParent
->storageDirEntry
, &data
);
4088 /* Destroy the old now-orphaned data. */
4089 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4090 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4094 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4100 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4103 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4104 StorageBaseImpl
*newSnapshot
;
4107 TRACE("(%p)\n", iface
);
4109 /* Create a new copy of the parent data. */
4110 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4111 if (FAILED(hr
)) return hr
;
4113 /* Destroy the open objects. */
4114 StorageBaseImpl_DeleteAll(&This
->base
);
4116 /* Replace our current snapshot. */
4117 IStorage_Release((IStorage
*)This
->snapshot
);
4118 This
->snapshot
= newSnapshot
;
4123 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4125 if (!This
->reverted
)
4127 TRACE("Storage invalidated (stg=%p)\n", This
);
4131 StorageBaseImpl_DeleteAll(This
);
4135 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4137 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4139 TransactedSnapshotImpl_Invalidate(iface
);
4141 IStorage_Release((IStorage
*)This
->transactedParent
);
4143 IStorage_Release((IStorage
*)This
->snapshot
);
4145 HeapFree(GetProcessHeap(), 0, This
);
4148 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4149 const DirEntry
*newData
, DirRef
*index
)
4151 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4153 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4157 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4158 DirRef index
, const DirEntry
*data
)
4160 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4162 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4166 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4167 DirRef index
, DirEntry
*data
)
4169 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4171 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4175 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4178 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4180 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4184 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4185 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4187 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4189 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4190 index
, offset
, size
, buffer
, bytesRead
);
4193 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4194 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4196 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4198 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4199 index
, offset
, size
, buffer
, bytesWritten
);
4202 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4203 DirRef index
, ULARGE_INTEGER newsize
)
4205 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4207 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4211 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4213 StorageBaseImpl_QueryInterface
,
4214 StorageBaseImpl_AddRef
,
4215 StorageBaseImpl_Release
,
4216 StorageBaseImpl_CreateStream
,
4217 StorageBaseImpl_OpenStream
,
4218 StorageBaseImpl_CreateStorage
,
4219 StorageBaseImpl_OpenStorage
,
4220 StorageBaseImpl_CopyTo
,
4221 StorageBaseImpl_MoveElementTo
,
4222 TransactedSnapshotImpl_Commit
,
4223 TransactedSnapshotImpl_Revert
,
4224 StorageBaseImpl_EnumElements
,
4225 StorageBaseImpl_DestroyElement
,
4226 StorageBaseImpl_RenameElement
,
4227 StorageBaseImpl_SetElementTimes
,
4228 StorageBaseImpl_SetClass
,
4229 StorageBaseImpl_SetStateBits
,
4230 StorageBaseImpl_Stat
4233 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4235 TransactedSnapshotImpl_Destroy
,
4236 TransactedSnapshotImpl_Invalidate
,
4237 TransactedSnapshotImpl_CreateDirEntry
,
4238 TransactedSnapshotImpl_WriteDirEntry
,
4239 TransactedSnapshotImpl_ReadDirEntry
,
4240 TransactedSnapshotImpl_DestroyDirEntry
,
4241 TransactedSnapshotImpl_StreamReadAt
,
4242 TransactedSnapshotImpl_StreamWriteAt
,
4243 TransactedSnapshotImpl_StreamSetSize
4246 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4247 TransactedSnapshotImpl
** result
)
4251 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4254 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4256 /* This is OK because the property set storage functions use the IStorage functions. */
4257 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4259 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4261 list_init(&(*result
)->base
.strmHead
);
4263 list_init(&(*result
)->base
.storageHead
);
4265 (*result
)->base
.ref
= 1;
4267 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4269 (*result
)->base
.filename
= parentStorage
->filename
;
4271 /* Create a new temporary storage to act as the snapshot */
4272 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4276 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4278 /* parentStorage already has 1 reference, which we take over here. */
4279 (*result
)->transactedParent
= parentStorage
;
4281 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4284 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4289 return E_OUTOFMEMORY
;
4292 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4293 StorageBaseImpl
** result
)
4297 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4299 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4302 return TransactedSnapshotImpl_Construct(parentStorage
,
4303 (TransactedSnapshotImpl
**)result
);
4306 static HRESULT
Storage_Construct(
4313 StorageBaseImpl
** result
)
4315 StorageImpl
*newStorage
;
4316 StorageBaseImpl
*newTransactedStorage
;
4319 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4320 if (FAILED(hr
)) goto end
;
4322 if (openFlags
& STGM_TRANSACTED
)
4324 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4326 IStorage_Release((IStorage
*)newStorage
);
4328 *result
= newTransactedStorage
;
4331 *result
= &newStorage
->base
;
4337 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4339 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4341 if (!This
->base
.reverted
)
4343 TRACE("Storage invalidated (stg=%p)\n", This
);
4345 This
->base
.reverted
= 1;
4347 This
->parentStorage
= NULL
;
4349 StorageBaseImpl_DeleteAll(&This
->base
);
4351 list_remove(&This
->ParentListEntry
);
4355 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4357 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4359 StorageInternalImpl_Invalidate(&This
->base
);
4361 HeapFree(GetProcessHeap(), 0, This
);
4364 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4365 const DirEntry
*newData
, DirRef
*index
)
4367 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4369 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4373 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4374 DirRef index
, const DirEntry
*data
)
4376 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4378 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4382 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4383 DirRef index
, DirEntry
*data
)
4385 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4387 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4391 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4394 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4396 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4400 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4401 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4403 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4405 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4406 index
, offset
, size
, buffer
, bytesRead
);
4409 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4410 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4412 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4414 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4415 index
, offset
, size
, buffer
, bytesWritten
);
4418 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4419 DirRef index
, ULARGE_INTEGER newsize
)
4421 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4423 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4427 /******************************************************************************
4429 ** Storage32InternalImpl_Commit
4432 static HRESULT WINAPI
StorageInternalImpl_Commit(
4434 DWORD grfCommitFlags
) /* [in] */
4436 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4440 /******************************************************************************
4442 ** Storage32InternalImpl_Revert
4445 static HRESULT WINAPI
StorageInternalImpl_Revert(
4448 FIXME("(%p): stub\n", iface
);
4452 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4454 IStorage_Release((IStorage
*)This
->parentStorage
);
4455 HeapFree(GetProcessHeap(), 0, This
);
4458 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4459 IEnumSTATSTG
* iface
,
4463 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4466 return E_INVALIDARG
;
4470 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4471 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4474 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4478 return E_NOINTERFACE
;
4481 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4482 IEnumSTATSTG
* iface
)
4484 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4485 return InterlockedIncrement(&This
->ref
);
4488 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4489 IEnumSTATSTG
* iface
)
4491 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4495 newRef
= InterlockedDecrement(&This
->ref
);
4499 IEnumSTATSTGImpl_Destroy(This
);
4505 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4506 IEnumSTATSTGImpl
* This
,
4509 DirRef result
= DIRENTRY_NULL
;
4513 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4515 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4516 This
->parentStorage
->storageDirEntry
, &entry
);
4517 searchNode
= entry
.dirRootEntry
;
4519 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4521 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4525 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4529 searchNode
= entry
.rightChild
;
4533 result
= searchNode
;
4534 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4535 searchNode
= entry
.leftChild
;
4543 if (result
!= DIRENTRY_NULL
)
4544 memcpy(This
->name
, result_name
, sizeof(result_name
));
4550 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4551 IEnumSTATSTG
* iface
,
4554 ULONG
* pceltFetched
)
4556 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4558 DirEntry currentEntry
;
4559 STATSTG
* currentReturnStruct
= rgelt
;
4560 ULONG objectFetched
= 0;
4561 DirRef currentSearchNode
;
4564 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4565 return E_INVALIDARG
;
4567 if (This
->parentStorage
->reverted
)
4568 return STG_E_REVERTED
;
4571 * To avoid the special case, get another pointer to a ULONG value if
4572 * the caller didn't supply one.
4574 if (pceltFetched
==0)
4575 pceltFetched
= &objectFetched
;
4578 * Start the iteration, we will iterate until we hit the end of the
4579 * linked list or until we hit the number of items to iterate through
4583 while ( *pceltFetched
< celt
)
4585 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4587 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4591 * Read the entry from the storage.
4593 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4598 * Copy the information to the return buffer.
4600 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4601 currentReturnStruct
,
4606 * Step to the next item in the iteration
4609 currentReturnStruct
++;
4612 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4619 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4620 IEnumSTATSTG
* iface
,
4623 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4625 ULONG objectFetched
= 0;
4626 DirRef currentSearchNode
;
4629 if (This
->parentStorage
->reverted
)
4630 return STG_E_REVERTED
;
4632 while ( (objectFetched
< celt
) )
4634 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4636 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4642 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4648 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4649 IEnumSTATSTG
* iface
)
4651 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4653 if (This
->parentStorage
->reverted
)
4654 return STG_E_REVERTED
;
4661 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4662 IEnumSTATSTG
* iface
,
4663 IEnumSTATSTG
** ppenum
)
4665 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4667 IEnumSTATSTGImpl
* newClone
;
4669 if (This
->parentStorage
->reverted
)
4670 return STG_E_REVERTED
;
4673 * Perform a sanity check on the parameters.
4676 return E_INVALIDARG
;
4678 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4679 This
->storageDirEntry
);
4683 * The new clone enumeration must point to the same current node as
4686 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4688 *ppenum
= (IEnumSTATSTG
*)newClone
;
4691 * Don't forget to nail down a reference to the clone before
4694 IEnumSTATSTGImpl_AddRef(*ppenum
);
4700 * Virtual function table for the IEnumSTATSTGImpl class.
4702 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4704 IEnumSTATSTGImpl_QueryInterface
,
4705 IEnumSTATSTGImpl_AddRef
,
4706 IEnumSTATSTGImpl_Release
,
4707 IEnumSTATSTGImpl_Next
,
4708 IEnumSTATSTGImpl_Skip
,
4709 IEnumSTATSTGImpl_Reset
,
4710 IEnumSTATSTGImpl_Clone
4713 /******************************************************************************
4714 ** IEnumSTATSTGImpl implementation
4717 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4718 StorageBaseImpl
* parentStorage
,
4719 DirRef storageDirEntry
)
4721 IEnumSTATSTGImpl
* newEnumeration
;
4723 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4725 if (newEnumeration
!=0)
4728 * Set-up the virtual function table and reference count.
4730 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4731 newEnumeration
->ref
= 0;
4734 * We want to nail-down the reference to the storage in case the
4735 * enumeration out-lives the storage in the client application.
4737 newEnumeration
->parentStorage
= parentStorage
;
4738 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4740 newEnumeration
->storageDirEntry
= storageDirEntry
;
4743 * Make sure the current node of the iterator is the first one.
4745 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4748 return newEnumeration
;
4752 * Virtual function table for the Storage32InternalImpl class.
4754 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4756 StorageBaseImpl_QueryInterface
,
4757 StorageBaseImpl_AddRef
,
4758 StorageBaseImpl_Release
,
4759 StorageBaseImpl_CreateStream
,
4760 StorageBaseImpl_OpenStream
,
4761 StorageBaseImpl_CreateStorage
,
4762 StorageBaseImpl_OpenStorage
,
4763 StorageBaseImpl_CopyTo
,
4764 StorageBaseImpl_MoveElementTo
,
4765 StorageInternalImpl_Commit
,
4766 StorageInternalImpl_Revert
,
4767 StorageBaseImpl_EnumElements
,
4768 StorageBaseImpl_DestroyElement
,
4769 StorageBaseImpl_RenameElement
,
4770 StorageBaseImpl_SetElementTimes
,
4771 StorageBaseImpl_SetClass
,
4772 StorageBaseImpl_SetStateBits
,
4773 StorageBaseImpl_Stat
4776 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4778 StorageInternalImpl_Destroy
,
4779 StorageInternalImpl_Invalidate
,
4780 StorageInternalImpl_CreateDirEntry
,
4781 StorageInternalImpl_WriteDirEntry
,
4782 StorageInternalImpl_ReadDirEntry
,
4783 StorageInternalImpl_DestroyDirEntry
,
4784 StorageInternalImpl_StreamReadAt
,
4785 StorageInternalImpl_StreamWriteAt
,
4786 StorageInternalImpl_StreamSetSize
4789 /******************************************************************************
4790 ** Storage32InternalImpl implementation
4793 static StorageInternalImpl
* StorageInternalImpl_Construct(
4794 StorageBaseImpl
* parentStorage
,
4796 DirRef storageDirEntry
)
4798 StorageInternalImpl
* newStorage
;
4800 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4804 list_init(&newStorage
->base
.strmHead
);
4806 list_init(&newStorage
->base
.storageHead
);
4809 * Initialize the virtual function table.
4811 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4812 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4813 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4815 newStorage
->base
.reverted
= 0;
4817 newStorage
->base
.ref
= 1;
4819 newStorage
->parentStorage
= parentStorage
;
4822 * Keep a reference to the directory entry of this storage
4824 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4826 newStorage
->base
.create
= 0;
4834 /******************************************************************************
4835 ** StorageUtl implementation
4838 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4842 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4843 *value
= lendian16toh(tmp
);
4846 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4848 value
= htole16(value
);
4849 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4852 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4856 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4857 *value
= lendian32toh(tmp
);
4860 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4862 value
= htole32(value
);
4863 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4866 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4867 ULARGE_INTEGER
* value
)
4869 #ifdef WORDS_BIGENDIAN
4872 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4873 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4874 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4876 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4880 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4881 const ULARGE_INTEGER
*value
)
4883 #ifdef WORDS_BIGENDIAN
4886 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4887 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4888 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4890 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4894 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4896 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4897 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4898 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4900 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4903 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4905 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4906 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4907 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4909 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4912 void StorageUtl_CopyDirEntryToSTATSTG(
4913 StorageBaseImpl
* storage
,
4914 STATSTG
* destination
,
4915 const DirEntry
* source
,
4920 if (source
->stgType
== STGTY_ROOT
)
4922 /* replace the name of root entry (often "Root Entry") by the file name */
4923 entryName
= storage
->filename
;
4927 entryName
= source
->name
;
4931 * The copy of the string occurs only when the flag is not set
4933 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4934 (entryName
== NULL
) ||
4935 (entryName
[0] == 0) )
4937 destination
->pwcsName
= 0;
4941 destination
->pwcsName
=
4942 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4944 strcpyW(destination
->pwcsName
, entryName
);
4947 switch (source
->stgType
)
4951 destination
->type
= STGTY_STORAGE
;
4954 destination
->type
= STGTY_STREAM
;
4957 destination
->type
= STGTY_STREAM
;
4961 destination
->cbSize
= source
->size
;
4963 currentReturnStruct->mtime = {0}; TODO
4964 currentReturnStruct->ctime = {0};
4965 currentReturnStruct->atime = {0};
4967 destination
->grfMode
= 0;
4968 destination
->grfLocksSupported
= 0;
4969 destination
->clsid
= source
->clsid
;
4970 destination
->grfStateBits
= 0;
4971 destination
->reserved
= 0;
4974 /******************************************************************************
4975 ** BlockChainStream implementation
4978 BlockChainStream
* BlockChainStream_Construct(
4979 StorageImpl
* parentStorage
,
4980 ULONG
* headOfStreamPlaceHolder
,
4983 BlockChainStream
* newStream
;
4986 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4988 newStream
->parentStorage
= parentStorage
;
4989 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4990 newStream
->ownerDirEntry
= dirEntry
;
4991 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4992 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4993 newStream
->numBlocks
= 0;
4995 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4997 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4999 newStream
->numBlocks
++;
5000 newStream
->tailIndex
= blockIndex
;
5002 if(FAILED(StorageImpl_GetNextBlockInChain(
5007 HeapFree(GetProcessHeap(), 0, newStream
);
5015 void BlockChainStream_Destroy(BlockChainStream
* This
)
5017 HeapFree(GetProcessHeap(), 0, This
);
5020 /******************************************************************************
5021 * BlockChainStream_GetHeadOfChain
5023 * Returns the head of this stream chain.
5024 * Some special chains don't have directory entries, their heads are kept in
5025 * This->headOfStreamPlaceHolder.
5028 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5030 DirEntry chainEntry
;
5033 if (This
->headOfStreamPlaceHolder
!= 0)
5034 return *(This
->headOfStreamPlaceHolder
);
5036 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5038 hr
= StorageImpl_ReadDirEntry(
5039 This
->parentStorage
,
5040 This
->ownerDirEntry
,
5045 return chainEntry
.startingBlock
;
5049 return BLOCK_END_OF_CHAIN
;
5052 /******************************************************************************
5053 * BlockChainStream_GetCount
5055 * Returns the number of blocks that comprises this chain.
5056 * This is not the size of the stream as the last block may not be full!
5059 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5064 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5066 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5070 if(FAILED(StorageImpl_GetNextBlockInChain(
5071 This
->parentStorage
,
5080 /******************************************************************************
5081 * BlockChainStream_ReadAt
5083 * Reads a specified number of bytes from this chain at the specified offset.
5084 * bytesRead may be NULL.
5085 * Failure will be returned if the specified number of bytes has not been read.
5087 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5088 ULARGE_INTEGER offset
,
5093 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5094 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5095 ULONG bytesToReadInBuffer
;
5099 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5102 * Find the first block in the stream that contains part of the buffer.
5104 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5105 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5106 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5108 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5109 This
->lastBlockNoInSequence
= blockNoInSequence
;
5113 ULONG temp
= blockNoInSequence
;
5115 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5116 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5117 This
->lastBlockNoInSequence
= temp
;
5120 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5122 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5123 return STG_E_DOCFILECORRUPT
;
5124 blockNoInSequence
--;
5127 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5128 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5130 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5133 * Start reading the buffer.
5136 bufferWalker
= buffer
;
5138 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5140 ULARGE_INTEGER ulOffset
;
5143 * Calculate how many bytes we can copy from this big block.
5145 bytesToReadInBuffer
=
5146 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5148 TRACE("block %i\n",blockIndex
);
5149 ulOffset
.u
.HighPart
= 0;
5150 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5153 StorageImpl_ReadAt(This
->parentStorage
,
5156 bytesToReadInBuffer
,
5159 * Step to the next big block.
5161 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5162 return STG_E_DOCFILECORRUPT
;
5164 bufferWalker
+= bytesReadAt
;
5165 size
-= bytesReadAt
;
5166 *bytesRead
+= bytesReadAt
;
5167 offsetInBlock
= 0; /* There is no offset on the next block */
5169 if (bytesToReadInBuffer
!= bytesReadAt
)
5173 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5176 /******************************************************************************
5177 * BlockChainStream_WriteAt
5179 * Writes the specified number of bytes to this chain at the specified offset.
5180 * Will fail if not all specified number of bytes have been written.
5182 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5183 ULARGE_INTEGER offset
,
5186 ULONG
* bytesWritten
)
5188 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5189 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5192 const BYTE
* bufferWalker
;
5195 * Find the first block in the stream that contains part of the buffer.
5197 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5198 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5199 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5201 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5202 This
->lastBlockNoInSequence
= blockNoInSequence
;
5206 ULONG temp
= blockNoInSequence
;
5208 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5209 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5210 This
->lastBlockNoInSequence
= temp
;
5213 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5215 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5217 return STG_E_DOCFILECORRUPT
;
5218 blockNoInSequence
--;
5221 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5223 /* BlockChainStream_SetSize should have already been called to ensure we have
5224 * enough blocks in the chain to write into */
5225 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5227 ERR("not enough blocks in chain to write data\n");
5228 return STG_E_DOCFILECORRUPT
;
5232 bufferWalker
= buffer
;
5234 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5236 ULARGE_INTEGER ulOffset
;
5237 DWORD bytesWrittenAt
;
5239 * Calculate how many bytes we can copy from this big block.
5242 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5244 TRACE("block %i\n",blockIndex
);
5245 ulOffset
.u
.HighPart
= 0;
5246 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5249 StorageImpl_WriteAt(This
->parentStorage
,
5256 * Step to the next big block.
5258 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5260 return STG_E_DOCFILECORRUPT
;
5262 bufferWalker
+= bytesWrittenAt
;
5263 size
-= bytesWrittenAt
;
5264 *bytesWritten
+= bytesWrittenAt
;
5265 offsetInBlock
= 0; /* There is no offset on the next block */
5267 if (bytesWrittenAt
!= bytesToWrite
)
5271 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5274 /******************************************************************************
5275 * BlockChainStream_Shrink
5277 * Shrinks this chain in the big block depot.
5279 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5280 ULARGE_INTEGER newSize
)
5282 ULONG blockIndex
, extraBlock
;
5287 * Reset the last accessed block cache.
5289 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5290 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5293 * Figure out how many blocks are needed to contain the new size
5295 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5297 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5300 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5303 * Go to the new end of chain
5305 while (count
< numBlocks
)
5307 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5313 /* Get the next block before marking the new end */
5314 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5318 /* Mark the new end of chain */
5319 StorageImpl_SetNextBlockInChain(
5320 This
->parentStorage
,
5322 BLOCK_END_OF_CHAIN
);
5324 This
->tailIndex
= blockIndex
;
5325 This
->numBlocks
= numBlocks
;
5328 * Mark the extra blocks as free
5330 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5332 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5335 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5336 extraBlock
= blockIndex
;
5342 /******************************************************************************
5343 * BlockChainStream_Enlarge
5345 * Grows this chain in the big block depot.
5347 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5348 ULARGE_INTEGER newSize
)
5350 ULONG blockIndex
, currentBlock
;
5352 ULONG oldNumBlocks
= 0;
5354 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5357 * Empty chain. Create the head.
5359 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5361 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5362 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5364 BLOCK_END_OF_CHAIN
);
5366 if (This
->headOfStreamPlaceHolder
!= 0)
5368 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5372 DirEntry chainEntry
;
5373 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5375 StorageImpl_ReadDirEntry(
5376 This
->parentStorage
,
5377 This
->ownerDirEntry
,
5380 chainEntry
.startingBlock
= blockIndex
;
5382 StorageImpl_WriteDirEntry(
5383 This
->parentStorage
,
5384 This
->ownerDirEntry
,
5388 This
->tailIndex
= blockIndex
;
5389 This
->numBlocks
= 1;
5393 * Figure out how many blocks are needed to contain this stream
5395 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5397 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5401 * Go to the current end of chain
5403 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5405 currentBlock
= blockIndex
;
5407 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5410 currentBlock
= blockIndex
;
5412 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5417 This
->tailIndex
= currentBlock
;
5420 currentBlock
= This
->tailIndex
;
5421 oldNumBlocks
= This
->numBlocks
;
5424 * Add new blocks to the chain
5426 if (oldNumBlocks
< newNumBlocks
)
5428 while (oldNumBlocks
< newNumBlocks
)
5430 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5432 StorageImpl_SetNextBlockInChain(
5433 This
->parentStorage
,
5437 StorageImpl_SetNextBlockInChain(
5438 This
->parentStorage
,
5440 BLOCK_END_OF_CHAIN
);
5442 currentBlock
= blockIndex
;
5446 This
->tailIndex
= blockIndex
;
5447 This
->numBlocks
= newNumBlocks
;
5453 /******************************************************************************
5454 * BlockChainStream_SetSize
5456 * Sets the size of this stream. The big block depot will be updated.
5457 * The file will grow if we grow the chain.
5459 * TODO: Free the actual blocks in the file when we shrink the chain.
5460 * Currently, the blocks are still in the file. So the file size
5461 * doesn't shrink even if we shrink streams.
5463 BOOL
BlockChainStream_SetSize(
5464 BlockChainStream
* This
,
5465 ULARGE_INTEGER newSize
)
5467 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5469 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5472 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5474 BlockChainStream_Shrink(This
, newSize
);
5478 BlockChainStream_Enlarge(This
, newSize
);
5484 /******************************************************************************
5485 * BlockChainStream_GetSize
5487 * Returns the size of this chain.
5488 * Will return the block count if this chain doesn't have a directory entry.
5490 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5492 DirEntry chainEntry
;
5494 if(This
->headOfStreamPlaceHolder
== NULL
)
5497 * This chain has a directory entry so use the size value from there.
5499 StorageImpl_ReadDirEntry(
5500 This
->parentStorage
,
5501 This
->ownerDirEntry
,
5504 return chainEntry
.size
;
5509 * this chain is a chain that does not have a directory entry, figure out the
5510 * size by making the product number of used blocks times the
5513 ULARGE_INTEGER result
;
5514 result
.u
.HighPart
= 0;
5517 BlockChainStream_GetCount(This
) *
5518 This
->parentStorage
->bigBlockSize
;
5524 /******************************************************************************
5525 ** SmallBlockChainStream implementation
5528 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5529 StorageImpl
* parentStorage
,
5530 ULONG
* headOfStreamPlaceHolder
,
5533 SmallBlockChainStream
* newStream
;
5535 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5537 newStream
->parentStorage
= parentStorage
;
5538 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5539 newStream
->ownerDirEntry
= dirEntry
;
5544 void SmallBlockChainStream_Destroy(
5545 SmallBlockChainStream
* This
)
5547 HeapFree(GetProcessHeap(), 0, This
);
5550 /******************************************************************************
5551 * SmallBlockChainStream_GetHeadOfChain
5553 * Returns the head of this chain of small blocks.
5555 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5556 SmallBlockChainStream
* This
)
5558 DirEntry chainEntry
;
5561 if (This
->headOfStreamPlaceHolder
!= NULL
)
5562 return *(This
->headOfStreamPlaceHolder
);
5564 if (This
->ownerDirEntry
)
5566 hr
= StorageImpl_ReadDirEntry(
5567 This
->parentStorage
,
5568 This
->ownerDirEntry
,
5573 return chainEntry
.startingBlock
;
5578 return BLOCK_END_OF_CHAIN
;
5581 /******************************************************************************
5582 * SmallBlockChainStream_GetNextBlockInChain
5584 * Returns the index of the next small block in this chain.
5587 * - BLOCK_END_OF_CHAIN: end of this chain
5588 * - BLOCK_UNUSED: small block 'blockIndex' is free
5590 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5591 SmallBlockChainStream
* This
,
5593 ULONG
* nextBlockInChain
)
5595 ULARGE_INTEGER offsetOfBlockInDepot
;
5600 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5602 offsetOfBlockInDepot
.u
.HighPart
= 0;
5603 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5606 * Read those bytes in the buffer from the small block file.
5608 res
= BlockChainStream_ReadAt(
5609 This
->parentStorage
->smallBlockDepotChain
,
5610 offsetOfBlockInDepot
,
5617 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5624 /******************************************************************************
5625 * SmallBlockChainStream_SetNextBlockInChain
5627 * Writes the index of the next block of the specified block in the small
5629 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5630 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5632 static void SmallBlockChainStream_SetNextBlockInChain(
5633 SmallBlockChainStream
* This
,
5637 ULARGE_INTEGER offsetOfBlockInDepot
;
5641 offsetOfBlockInDepot
.u
.HighPart
= 0;
5642 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5644 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5647 * Read those bytes in the buffer from the small block file.
5649 BlockChainStream_WriteAt(
5650 This
->parentStorage
->smallBlockDepotChain
,
5651 offsetOfBlockInDepot
,
5657 /******************************************************************************
5658 * SmallBlockChainStream_FreeBlock
5660 * Flag small block 'blockIndex' as free in the small block depot.
5662 static void SmallBlockChainStream_FreeBlock(
5663 SmallBlockChainStream
* This
,
5666 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5669 /******************************************************************************
5670 * SmallBlockChainStream_GetNextFreeBlock
5672 * Returns the index of a free small block. The small block depot will be
5673 * enlarged if necessary. The small block chain will also be enlarged if
5676 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5677 SmallBlockChainStream
* This
)
5679 ULARGE_INTEGER offsetOfBlockInDepot
;
5682 ULONG blockIndex
= 0;
5683 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5685 ULONG smallBlocksPerBigBlock
;
5687 offsetOfBlockInDepot
.u
.HighPart
= 0;
5690 * Scan the small block depot for a free block
5692 while (nextBlockIndex
!= BLOCK_UNUSED
)
5694 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5696 res
= BlockChainStream_ReadAt(
5697 This
->parentStorage
->smallBlockDepotChain
,
5698 offsetOfBlockInDepot
,
5704 * If we run out of space for the small block depot, enlarge it
5708 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5710 if (nextBlockIndex
!= BLOCK_UNUSED
)
5716 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5718 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5719 ULONG nextBlock
, newsbdIndex
;
5720 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5722 nextBlock
= sbdIndex
;
5723 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5725 sbdIndex
= nextBlock
;
5726 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5729 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5730 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5731 StorageImpl_SetNextBlockInChain(
5732 This
->parentStorage
,
5736 StorageImpl_SetNextBlockInChain(
5737 This
->parentStorage
,
5739 BLOCK_END_OF_CHAIN
);
5742 * Initialize all the small blocks to free
5744 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5745 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5750 * We have just created the small block depot.
5756 * Save it in the header
5758 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5759 StorageImpl_SaveFileHeader(This
->parentStorage
);
5762 * And allocate the first big block that will contain small blocks
5765 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5767 StorageImpl_SetNextBlockInChain(
5768 This
->parentStorage
,
5770 BLOCK_END_OF_CHAIN
);
5772 StorageImpl_ReadDirEntry(
5773 This
->parentStorage
,
5774 This
->parentStorage
->base
.storageDirEntry
,
5777 rootEntry
.startingBlock
= sbStartIndex
;
5778 rootEntry
.size
.u
.HighPart
= 0;
5779 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5781 StorageImpl_WriteDirEntry(
5782 This
->parentStorage
,
5783 This
->parentStorage
->base
.storageDirEntry
,
5787 StorageImpl_SaveFileHeader(This
->parentStorage
);
5791 smallBlocksPerBigBlock
=
5792 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5795 * Verify if we have to allocate big blocks to contain small blocks
5797 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5800 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5802 StorageImpl_ReadDirEntry(
5803 This
->parentStorage
,
5804 This
->parentStorage
->base
.storageDirEntry
,
5807 if (rootEntry
.size
.u
.LowPart
<
5808 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5810 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5812 BlockChainStream_SetSize(
5813 This
->parentStorage
->smallBlockRootChain
,
5816 StorageImpl_WriteDirEntry(
5817 This
->parentStorage
,
5818 This
->parentStorage
->base
.storageDirEntry
,
5826 /******************************************************************************
5827 * SmallBlockChainStream_ReadAt
5829 * Reads a specified number of bytes from this chain at the specified offset.
5830 * bytesRead may be NULL.
5831 * Failure will be returned if the specified number of bytes has not been read.
5833 HRESULT
SmallBlockChainStream_ReadAt(
5834 SmallBlockChainStream
* This
,
5835 ULARGE_INTEGER offset
,
5841 ULARGE_INTEGER offsetInBigBlockFile
;
5842 ULONG blockNoInSequence
=
5843 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5845 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5846 ULONG bytesToReadInBuffer
;
5848 ULONG bytesReadFromBigBlockFile
;
5852 * This should never happen on a small block file.
5854 assert(offset
.u
.HighPart
==0);
5857 * Find the first block in the stream that contains part of the buffer.
5859 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5861 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5863 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5866 blockNoInSequence
--;
5870 * Start reading the buffer.
5873 bufferWalker
= buffer
;
5875 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5878 * Calculate how many bytes we can copy from this small block.
5880 bytesToReadInBuffer
=
5881 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5884 * Calculate the offset of the small block in the small block file.
5886 offsetInBigBlockFile
.u
.HighPart
= 0;
5887 offsetInBigBlockFile
.u
.LowPart
=
5888 blockIndex
* This
->parentStorage
->smallBlockSize
;
5890 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5893 * Read those bytes in the buffer from the small block file.
5894 * The small block has already been identified so it shouldn't fail
5895 * unless the file is corrupt.
5897 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5898 offsetInBigBlockFile
,
5899 bytesToReadInBuffer
,
5901 &bytesReadFromBigBlockFile
);
5907 * Step to the next big block.
5909 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5911 return STG_E_DOCFILECORRUPT
;
5913 bufferWalker
+= bytesReadFromBigBlockFile
;
5914 size
-= bytesReadFromBigBlockFile
;
5915 *bytesRead
+= bytesReadFromBigBlockFile
;
5916 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5919 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5922 /******************************************************************************
5923 * SmallBlockChainStream_WriteAt
5925 * Writes the specified number of bytes to this chain at the specified offset.
5926 * Will fail if not all specified number of bytes have been written.
5928 HRESULT
SmallBlockChainStream_WriteAt(
5929 SmallBlockChainStream
* This
,
5930 ULARGE_INTEGER offset
,
5933 ULONG
* bytesWritten
)
5935 ULARGE_INTEGER offsetInBigBlockFile
;
5936 ULONG blockNoInSequence
=
5937 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5939 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5940 ULONG bytesToWriteInBuffer
;
5942 ULONG bytesWrittenToBigBlockFile
;
5943 const BYTE
* bufferWalker
;
5947 * This should never happen on a small block file.
5949 assert(offset
.u
.HighPart
==0);
5952 * Find the first block in the stream that contains part of the buffer.
5954 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5956 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5958 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5959 return STG_E_DOCFILECORRUPT
;
5960 blockNoInSequence
--;
5964 * Start writing the buffer.
5967 bufferWalker
= buffer
;
5968 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5971 * Calculate how many bytes we can copy to this small block.
5973 bytesToWriteInBuffer
=
5974 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5977 * Calculate the offset of the small block in the small block file.
5979 offsetInBigBlockFile
.u
.HighPart
= 0;
5980 offsetInBigBlockFile
.u
.LowPart
=
5981 blockIndex
* This
->parentStorage
->smallBlockSize
;
5983 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5986 * Write those bytes in the buffer to the small block file.
5988 res
= BlockChainStream_WriteAt(
5989 This
->parentStorage
->smallBlockRootChain
,
5990 offsetInBigBlockFile
,
5991 bytesToWriteInBuffer
,
5993 &bytesWrittenToBigBlockFile
);
5998 * Step to the next big block.
6000 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6003 bufferWalker
+= bytesWrittenToBigBlockFile
;
6004 size
-= bytesWrittenToBigBlockFile
;
6005 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6006 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6009 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6012 /******************************************************************************
6013 * SmallBlockChainStream_Shrink
6015 * Shrinks this chain in the small block depot.
6017 static BOOL
SmallBlockChainStream_Shrink(
6018 SmallBlockChainStream
* This
,
6019 ULARGE_INTEGER newSize
)
6021 ULONG blockIndex
, extraBlock
;
6025 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6027 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6030 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6033 * Go to the new end of chain
6035 while (count
< numBlocks
)
6037 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6044 * If the count is 0, we have a special case, the head of the chain was
6049 DirEntry chainEntry
;
6051 StorageImpl_ReadDirEntry(This
->parentStorage
,
6052 This
->ownerDirEntry
,
6055 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6057 StorageImpl_WriteDirEntry(This
->parentStorage
,
6058 This
->ownerDirEntry
,
6062 * We start freeing the chain at the head block.
6064 extraBlock
= blockIndex
;
6068 /* Get the next block before marking the new end */
6069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6073 /* Mark the new end of chain */
6074 SmallBlockChainStream_SetNextBlockInChain(
6077 BLOCK_END_OF_CHAIN
);
6081 * Mark the extra blocks as free
6083 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6085 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6088 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6089 extraBlock
= blockIndex
;
6095 /******************************************************************************
6096 * SmallBlockChainStream_Enlarge
6098 * Grows this chain in the small block depot.
6100 static BOOL
SmallBlockChainStream_Enlarge(
6101 SmallBlockChainStream
* This
,
6102 ULARGE_INTEGER newSize
)
6104 ULONG blockIndex
, currentBlock
;
6106 ULONG oldNumBlocks
= 0;
6108 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6111 * Empty chain. Create the head.
6113 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6115 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6116 SmallBlockChainStream_SetNextBlockInChain(
6119 BLOCK_END_OF_CHAIN
);
6121 if (This
->headOfStreamPlaceHolder
!= NULL
)
6123 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6127 DirEntry chainEntry
;
6129 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6132 chainEntry
.startingBlock
= blockIndex
;
6134 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6139 currentBlock
= blockIndex
;
6142 * Figure out how many blocks are needed to contain this stream
6144 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6146 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6150 * Go to the current end of chain
6152 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6155 currentBlock
= blockIndex
;
6156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6161 * Add new blocks to the chain
6163 while (oldNumBlocks
< newNumBlocks
)
6165 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6166 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6168 SmallBlockChainStream_SetNextBlockInChain(
6171 BLOCK_END_OF_CHAIN
);
6173 currentBlock
= blockIndex
;
6180 /******************************************************************************
6181 * SmallBlockChainStream_SetSize
6183 * Sets the size of this stream.
6184 * The file will grow if we grow the chain.
6186 * TODO: Free the actual blocks in the file when we shrink the chain.
6187 * Currently, the blocks are still in the file. So the file size
6188 * doesn't shrink even if we shrink streams.
6190 BOOL
SmallBlockChainStream_SetSize(
6191 SmallBlockChainStream
* This
,
6192 ULARGE_INTEGER newSize
)
6194 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6196 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6199 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6201 SmallBlockChainStream_Shrink(This
, newSize
);
6205 SmallBlockChainStream_Enlarge(This
, newSize
);
6211 /******************************************************************************
6212 * SmallBlockChainStream_GetCount
6214 * Returns the number of small blocks that comprises this chain.
6215 * This is not the size of the stream as the last block may not be full!
6218 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6223 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6225 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6230 blockIndex
, &blockIndex
)))
6237 /******************************************************************************
6238 * SmallBlockChainStream_GetSize
6240 * Returns the size of this chain.
6242 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6244 DirEntry chainEntry
;
6246 if(This
->headOfStreamPlaceHolder
!= NULL
)
6248 ULARGE_INTEGER result
;
6249 result
.u
.HighPart
= 0;
6251 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6252 This
->parentStorage
->smallBlockSize
;
6257 StorageImpl_ReadDirEntry(
6258 This
->parentStorage
,
6259 This
->ownerDirEntry
,
6262 return chainEntry
.size
;
6265 /******************************************************************************
6266 * StgCreateDocfile [OLE32.@]
6267 * Creates a new compound file storage object
6270 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6271 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6272 * reserved [ ?] unused?, usually 0
6273 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6276 * S_OK if the file was successfully created
6277 * some STG_E_ value if error
6279 * if pwcsName is NULL, create file with new unique name
6280 * the function can returns
6281 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6284 HRESULT WINAPI
StgCreateDocfile(
6288 IStorage
**ppstgOpen
)
6290 StorageBaseImpl
* newStorage
= 0;
6291 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6292 HRESULT hr
= STG_E_INVALIDFLAG
;
6296 DWORD fileAttributes
;
6297 WCHAR tempFileName
[MAX_PATH
];
6299 TRACE("(%s, %x, %d, %p)\n",
6300 debugstr_w(pwcsName
), grfMode
,
6301 reserved
, ppstgOpen
);
6304 return STG_E_INVALIDPOINTER
;
6306 return STG_E_INVALIDPARAMETER
;
6308 /* if no share mode given then DENY_NONE is the default */
6309 if (STGM_SHARE_MODE(grfMode
) == 0)
6310 grfMode
|= STGM_SHARE_DENY_NONE
;
6312 if ( FAILED( validateSTGM(grfMode
) ))
6315 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6316 switch(STGM_ACCESS_MODE(grfMode
))
6319 case STGM_READWRITE
:
6325 /* in direct mode, can only use SHARE_EXCLUSIVE */
6326 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6329 /* but in transacted mode, any share mode is valid */
6332 * Generate a unique name.
6336 WCHAR tempPath
[MAX_PATH
];
6337 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6339 memset(tempPath
, 0, sizeof(tempPath
));
6340 memset(tempFileName
, 0, sizeof(tempFileName
));
6342 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6345 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6346 pwcsName
= tempFileName
;
6349 hr
= STG_E_INSUFFICIENTMEMORY
;
6353 creationMode
= TRUNCATE_EXISTING
;
6357 creationMode
= GetCreationModeFromSTGM(grfMode
);
6361 * Interpret the STGM value grfMode
6363 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6364 accessMode
= GetAccessModeFromSTGM(grfMode
);
6366 if (grfMode
& STGM_DELETEONRELEASE
)
6367 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6369 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6371 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6375 FIXME("Storage share mode not implemented.\n");
6380 hFile
= CreateFileW(pwcsName
,
6388 if (hFile
== INVALID_HANDLE_VALUE
)
6390 if(GetLastError() == ERROR_FILE_EXISTS
)
6391 hr
= STG_E_FILEALREADYEXISTS
;
6398 * Allocate and initialize the new IStorage32object.
6400 hr
= Storage_Construct(
6415 * Get an "out" pointer for the caller.
6417 *ppstgOpen
= (IStorage
*)newStorage
;
6420 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6425 /******************************************************************************
6426 * StgCreateStorageEx [OLE32.@]
6428 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6430 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6431 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6433 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6435 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6436 return STG_E_INVALIDPARAMETER
;
6439 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6441 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6442 return STG_E_INVALIDPARAMETER
;
6445 if (stgfmt
== STGFMT_FILE
)
6447 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6448 return STG_E_INVALIDPARAMETER
;
6451 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6453 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6454 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6457 ERR("Invalid stgfmt argument\n");
6458 return STG_E_INVALIDPARAMETER
;
6461 /******************************************************************************
6462 * StgCreatePropSetStg [OLE32.@]
6464 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6465 IPropertySetStorage
**ppPropSetStg
)
6469 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6471 hr
= STG_E_INVALIDPARAMETER
;
6473 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6474 (void**)ppPropSetStg
);
6478 /******************************************************************************
6479 * StgOpenStorageEx [OLE32.@]
6481 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6483 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6484 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6486 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6488 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6489 return STG_E_INVALIDPARAMETER
;
6495 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6496 return STG_E_INVALIDPARAMETER
;
6498 case STGFMT_STORAGE
:
6501 case STGFMT_DOCFILE
:
6502 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6504 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6505 return STG_E_INVALIDPARAMETER
;
6507 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6511 WARN("STGFMT_ANY assuming storage\n");
6515 return STG_E_INVALIDPARAMETER
;
6518 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6522 /******************************************************************************
6523 * StgOpenStorage [OLE32.@]
6525 HRESULT WINAPI
StgOpenStorage(
6526 const OLECHAR
*pwcsName
,
6527 IStorage
*pstgPriority
,
6531 IStorage
**ppstgOpen
)
6533 StorageBaseImpl
* newStorage
= 0;
6539 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6540 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6541 snbExclude
, reserved
, ppstgOpen
);
6545 hr
= STG_E_INVALIDNAME
;
6551 hr
= STG_E_INVALIDPOINTER
;
6557 hr
= STG_E_INVALIDPARAMETER
;
6561 if (grfMode
& STGM_PRIORITY
)
6563 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6564 return STG_E_INVALIDFLAG
;
6565 if (grfMode
& STGM_DELETEONRELEASE
)
6566 return STG_E_INVALIDFUNCTION
;
6567 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6568 return STG_E_INVALIDFLAG
;
6569 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6570 grfMode
|= STGM_SHARE_DENY_NONE
;
6572 /* STGM_PRIORITY stops other IStorage objects on the same file from
6573 * committing until the STGM_PRIORITY IStorage is closed. it also
6574 * stops non-transacted mode StgOpenStorage calls with write access from
6575 * succeeding. obviously, both of these cannot be achieved through just
6576 * file share flags */
6577 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6581 * Validate the sharing mode
6583 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6584 switch(STGM_SHARE_MODE(grfMode
))
6586 case STGM_SHARE_EXCLUSIVE
:
6587 case STGM_SHARE_DENY_WRITE
:
6590 hr
= STG_E_INVALIDFLAG
;
6594 if ( FAILED( validateSTGM(grfMode
) ) ||
6595 (grfMode
&STGM_CREATE
))
6597 hr
= STG_E_INVALIDFLAG
;
6601 /* shared reading requires transacted mode */
6602 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6603 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6604 !(grfMode
&STGM_TRANSACTED
) )
6606 hr
= STG_E_INVALIDFLAG
;
6611 * Interpret the STGM value grfMode
6613 shareMode
= GetShareModeFromSTGM(grfMode
);
6614 accessMode
= GetAccessModeFromSTGM(grfMode
);
6618 hFile
= CreateFileW( pwcsName
,
6623 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6626 if (hFile
==INVALID_HANDLE_VALUE
)
6628 DWORD last_error
= GetLastError();
6634 case ERROR_FILE_NOT_FOUND
:
6635 hr
= STG_E_FILENOTFOUND
;
6638 case ERROR_PATH_NOT_FOUND
:
6639 hr
= STG_E_PATHNOTFOUND
;
6642 case ERROR_ACCESS_DENIED
:
6643 case ERROR_WRITE_PROTECT
:
6644 hr
= STG_E_ACCESSDENIED
;
6647 case ERROR_SHARING_VIOLATION
:
6648 hr
= STG_E_SHAREVIOLATION
;
6659 * Refuse to open the file if it's too small to be a structured storage file
6660 * FIXME: verify the file when reading instead of here
6662 if (GetFileSize(hFile
, NULL
) < 0x100)
6665 hr
= STG_E_FILEALREADYEXISTS
;
6670 * Allocate and initialize the new IStorage32object.
6672 hr
= Storage_Construct(
6684 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6686 if(hr
== STG_E_INVALIDHEADER
)
6687 hr
= STG_E_FILEALREADYEXISTS
;
6692 * Get an "out" pointer for the caller.
6694 *ppstgOpen
= (IStorage
*)newStorage
;
6697 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6701 /******************************************************************************
6702 * StgCreateDocfileOnILockBytes [OLE32.@]
6704 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6708 IStorage
** ppstgOpen
)
6710 StorageBaseImpl
* newStorage
= 0;
6713 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6714 return STG_E_INVALIDPOINTER
;
6717 * Allocate and initialize the new IStorage object.
6719 hr
= Storage_Construct(
6734 * Get an "out" pointer for the caller.
6736 *ppstgOpen
= (IStorage
*)newStorage
;
6741 /******************************************************************************
6742 * StgOpenStorageOnILockBytes [OLE32.@]
6744 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6746 IStorage
*pstgPriority
,
6750 IStorage
**ppstgOpen
)
6752 StorageBaseImpl
* newStorage
= 0;
6755 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6756 return STG_E_INVALIDPOINTER
;
6758 if ( FAILED( validateSTGM(grfMode
) ))
6759 return STG_E_INVALIDFLAG
;
6764 * Allocate and initialize the new IStorage object.
6766 hr
= Storage_Construct(
6781 * Get an "out" pointer for the caller.
6783 *ppstgOpen
= (IStorage
*)newStorage
;
6788 /******************************************************************************
6789 * StgSetTimes [ole32.@]
6790 * StgSetTimes [OLE32.@]
6794 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6795 FILETIME
const *patime
, FILETIME
const *pmtime
)
6797 IStorage
*stg
= NULL
;
6800 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6802 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6806 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6807 IStorage_Release(stg
);
6813 /******************************************************************************
6814 * StgIsStorageILockBytes [OLE32.@]
6816 * Determines if the ILockBytes contains a storage object.
6818 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6821 ULARGE_INTEGER offset
;
6823 offset
.u
.HighPart
= 0;
6824 offset
.u
.LowPart
= 0;
6826 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6828 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6834 /******************************************************************************
6835 * WriteClassStg [OLE32.@]
6837 * This method will store the specified CLSID in the specified storage object
6839 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6844 return E_INVALIDARG
;
6847 return STG_E_INVALIDPOINTER
;
6849 hRes
= IStorage_SetClass(pStg
, rclsid
);
6854 /***********************************************************************
6855 * ReadClassStg (OLE32.@)
6857 * This method reads the CLSID previously written to a storage object with
6858 * the WriteClassStg.
6861 * pstg [I] IStorage pointer
6862 * pclsid [O] Pointer to where the CLSID is written
6866 * Failure: HRESULT code.
6868 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6873 TRACE("(%p, %p)\n", pstg
, pclsid
);
6875 if(!pstg
|| !pclsid
)
6876 return E_INVALIDARG
;
6879 * read a STATSTG structure (contains the clsid) from the storage
6881 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6884 *pclsid
=pstatstg
.clsid
;
6889 /***********************************************************************
6890 * OleLoadFromStream (OLE32.@)
6892 * This function loads an object from stream
6894 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6898 LPPERSISTSTREAM xstm
;
6900 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6902 res
=ReadClassStm(pStm
,&clsid
);
6905 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6908 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6910 IUnknown_Release((IUnknown
*)*ppvObj
);
6913 res
=IPersistStream_Load(xstm
,pStm
);
6914 IPersistStream_Release(xstm
);
6915 /* FIXME: all refcounts ok at this point? I think they should be:
6918 * xstm : 0 (released)
6923 /***********************************************************************
6924 * OleSaveToStream (OLE32.@)
6926 * This function saves an object with the IPersistStream interface on it
6927 * to the specified stream.
6929 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6935 TRACE("(%p,%p)\n",pPStm
,pStm
);
6937 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6939 if (SUCCEEDED(res
)){
6941 res
=WriteClassStm(pStm
,&clsid
);
6945 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6948 TRACE("Finished Save\n");
6952 /****************************************************************************
6953 * This method validate a STGM parameter that can contain the values below
6955 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6956 * The stgm values contained in 0xffff0000 are bitmasks.
6958 * STGM_DIRECT 0x00000000
6959 * STGM_TRANSACTED 0x00010000
6960 * STGM_SIMPLE 0x08000000
6962 * STGM_READ 0x00000000
6963 * STGM_WRITE 0x00000001
6964 * STGM_READWRITE 0x00000002
6966 * STGM_SHARE_DENY_NONE 0x00000040
6967 * STGM_SHARE_DENY_READ 0x00000030
6968 * STGM_SHARE_DENY_WRITE 0x00000020
6969 * STGM_SHARE_EXCLUSIVE 0x00000010
6971 * STGM_PRIORITY 0x00040000
6972 * STGM_DELETEONRELEASE 0x04000000
6974 * STGM_CREATE 0x00001000
6975 * STGM_CONVERT 0x00020000
6976 * STGM_FAILIFTHERE 0x00000000
6978 * STGM_NOSCRATCH 0x00100000
6979 * STGM_NOSNAPSHOT 0x00200000
6981 static HRESULT
validateSTGM(DWORD stgm
)
6983 DWORD access
= STGM_ACCESS_MODE(stgm
);
6984 DWORD share
= STGM_SHARE_MODE(stgm
);
6985 DWORD create
= STGM_CREATE_MODE(stgm
);
6987 if (stgm
&~STGM_KNOWN_FLAGS
)
6989 ERR("unknown flags %08x\n", stgm
);
6997 case STGM_READWRITE
:
7005 case STGM_SHARE_DENY_NONE
:
7006 case STGM_SHARE_DENY_READ
:
7007 case STGM_SHARE_DENY_WRITE
:
7008 case STGM_SHARE_EXCLUSIVE
:
7017 case STGM_FAILIFTHERE
:
7024 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7026 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7030 * STGM_CREATE | STGM_CONVERT
7031 * if both are false, STGM_FAILIFTHERE is set to TRUE
7033 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7037 * STGM_NOSCRATCH requires STGM_TRANSACTED
7039 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7043 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7044 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7046 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7047 (!(stgm
& STGM_TRANSACTED
) ||
7048 share
== STGM_SHARE_EXCLUSIVE
||
7049 share
== STGM_SHARE_DENY_WRITE
) )
7055 /****************************************************************************
7056 * GetShareModeFromSTGM
7058 * This method will return a share mode flag from a STGM value.
7059 * The STGM value is assumed valid.
7061 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7063 switch (STGM_SHARE_MODE(stgm
))
7065 case STGM_SHARE_DENY_NONE
:
7066 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7067 case STGM_SHARE_DENY_READ
:
7068 return FILE_SHARE_WRITE
;
7069 case STGM_SHARE_DENY_WRITE
:
7070 return FILE_SHARE_READ
;
7071 case STGM_SHARE_EXCLUSIVE
:
7074 ERR("Invalid share mode!\n");
7079 /****************************************************************************
7080 * GetAccessModeFromSTGM
7082 * This method will return an access mode flag from a STGM value.
7083 * The STGM value is assumed valid.
7085 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7087 switch (STGM_ACCESS_MODE(stgm
))
7090 return GENERIC_READ
;
7092 case STGM_READWRITE
:
7093 return GENERIC_READ
| GENERIC_WRITE
;
7095 ERR("Invalid access mode!\n");
7100 /****************************************************************************
7101 * GetCreationModeFromSTGM
7103 * This method will return a creation mode flag from a STGM value.
7104 * The STGM value is assumed valid.
7106 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7108 switch(STGM_CREATE_MODE(stgm
))
7111 return CREATE_ALWAYS
;
7113 FIXME("STGM_CONVERT not implemented!\n");
7115 case STGM_FAILIFTHERE
:
7118 ERR("Invalid create mode!\n");
7124 /*************************************************************************
7125 * OLECONVERT_LoadOLE10 [Internal]
7127 * Loads the OLE10 STREAM to memory
7130 * pOleStream [I] The OLESTREAM
7131 * pData [I] Data Structure for the OLESTREAM Data
7135 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7136 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7139 * This function is used by OleConvertOLESTREAMToIStorage only.
7141 * Memory allocated for pData must be freed by the caller
7143 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7146 HRESULT hRes
= S_OK
;
7150 pData
->pData
= NULL
;
7151 pData
->pstrOleObjFileName
= NULL
;
7153 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7156 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7157 if(dwSize
!= sizeof(pData
->dwOleID
))
7159 hRes
= CONVERT10_E_OLESTREAM_GET
;
7161 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7163 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7174 /* Get the TypeID... more info needed for this field */
7175 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7176 if(dwSize
!= sizeof(pData
->dwTypeID
))
7178 hRes
= CONVERT10_E_OLESTREAM_GET
;
7183 if(pData
->dwTypeID
!= 0)
7185 /* Get the length of the OleTypeName */
7186 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7187 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7189 hRes
= CONVERT10_E_OLESTREAM_GET
;
7194 if(pData
->dwOleTypeNameLength
> 0)
7196 /* Get the OleTypeName */
7197 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7198 if(dwSize
!= pData
->dwOleTypeNameLength
)
7200 hRes
= CONVERT10_E_OLESTREAM_GET
;
7206 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7207 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7209 hRes
= CONVERT10_E_OLESTREAM_GET
;
7213 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7214 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7215 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7216 if(pData
->pstrOleObjFileName
)
7218 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7219 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7221 hRes
= CONVERT10_E_OLESTREAM_GET
;
7225 hRes
= CONVERT10_E_OLESTREAM_GET
;
7230 /* Get the Width of the Metafile */
7231 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7232 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7234 hRes
= CONVERT10_E_OLESTREAM_GET
;
7238 /* Get the Height of the Metafile */
7239 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7240 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7242 hRes
= CONVERT10_E_OLESTREAM_GET
;
7248 /* Get the Length of the Data */
7249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7250 if(dwSize
!= sizeof(pData
->dwDataLength
))
7252 hRes
= CONVERT10_E_OLESTREAM_GET
;
7256 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7258 if(!bStrem1
) /* if it is a second OLE stream data */
7260 pData
->dwDataLength
-= 8;
7261 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7262 if(dwSize
!= sizeof(pData
->strUnknown
))
7264 hRes
= CONVERT10_E_OLESTREAM_GET
;
7270 if(pData
->dwDataLength
> 0)
7272 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7274 /* Get Data (ex. IStorage, Metafile, or BMP) */
7277 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7278 if(dwSize
!= pData
->dwDataLength
)
7280 hRes
= CONVERT10_E_OLESTREAM_GET
;
7285 hRes
= CONVERT10_E_OLESTREAM_GET
;
7294 /*************************************************************************
7295 * OLECONVERT_SaveOLE10 [Internal]
7297 * Saves the OLE10 STREAM From memory
7300 * pData [I] Data Structure for the OLESTREAM Data
7301 * pOleStream [I] The OLESTREAM to save
7305 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7308 * This function is used by OleConvertIStorageToOLESTREAM only.
7311 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7314 HRESULT hRes
= S_OK
;
7318 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7319 if(dwSize
!= sizeof(pData
->dwOleID
))
7321 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7326 /* Set the TypeID */
7327 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7328 if(dwSize
!= sizeof(pData
->dwTypeID
))
7330 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7334 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7336 /* Set the Length of the OleTypeName */
7337 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7338 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7340 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7345 if(pData
->dwOleTypeNameLength
> 0)
7347 /* Set the OleTypeName */
7348 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7349 if(dwSize
!= pData
->dwOleTypeNameLength
)
7351 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7358 /* Set the width of the Metafile */
7359 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7360 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7362 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7368 /* Set the height of the Metafile */
7369 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7370 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7372 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7378 /* Set the length of the Data */
7379 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7380 if(dwSize
!= sizeof(pData
->dwDataLength
))
7382 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7388 if(pData
->dwDataLength
> 0)
7390 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7391 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7392 if(dwSize
!= pData
->dwDataLength
)
7394 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7402 /*************************************************************************
7403 * OLECONVERT_GetOLE20FromOLE10[Internal]
7405 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7406 * opens it, and copies the content to the dest IStorage for
7407 * OleConvertOLESTREAMToIStorage
7411 * pDestStorage [I] The IStorage to copy the data to
7412 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7413 * nBufferLength [I] The size of the buffer
7422 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7426 IStorage
*pTempStorage
;
7427 DWORD dwNumOfBytesWritten
;
7428 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7429 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7431 /* Create a temp File */
7432 GetTempPathW(MAX_PATH
, wstrTempDir
);
7433 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7434 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7436 if(hFile
!= INVALID_HANDLE_VALUE
)
7438 /* Write IStorage Data to File */
7439 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7442 /* Open and copy temp storage to the Dest Storage */
7443 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7446 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7447 IStorage_Release(pTempStorage
);
7449 DeleteFileW(wstrTempFile
);
7454 /*************************************************************************
7455 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7457 * Saves the OLE10 STREAM From memory
7460 * pStorage [I] The Src IStorage to copy
7461 * pData [I] The Dest Memory to write to.
7464 * The size in bytes allocated for pData
7467 * Memory allocated for pData must be freed by the caller
7469 * Used by OleConvertIStorageToOLESTREAM only.
7472 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7476 DWORD nDataLength
= 0;
7477 IStorage
*pTempStorage
;
7478 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7479 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7483 /* Create temp Storage */
7484 GetTempPathW(MAX_PATH
, wstrTempDir
);
7485 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7486 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7490 /* Copy Src Storage to the Temp Storage */
7491 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7492 IStorage_Release(pTempStorage
);
7494 /* Open Temp Storage as a file and copy to memory */
7495 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7496 if(hFile
!= INVALID_HANDLE_VALUE
)
7498 nDataLength
= GetFileSize(hFile
, NULL
);
7499 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7500 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7503 DeleteFileW(wstrTempFile
);
7508 /*************************************************************************
7509 * OLECONVERT_CreateOleStream [Internal]
7511 * Creates the "\001OLE" stream in the IStorage if necessary.
7514 * pStorage [I] Dest storage to create the stream in
7520 * This function is used by OleConvertOLESTREAMToIStorage only.
7522 * This stream is still unknown, MS Word seems to have extra data
7523 * but since the data is stored in the OLESTREAM there should be
7524 * no need to recreate the stream. If the stream is manually
7525 * deleted it will create it with this default data.
7528 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7532 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7533 BYTE pOleStreamHeader
[] =
7535 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7536 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7537 0x00, 0x00, 0x00, 0x00
7540 /* Create stream if not present */
7541 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7542 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7546 /* Write default Data */
7547 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7548 IStream_Release(pStream
);
7552 /* write a string to a stream, preceded by its length */
7553 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7560 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7561 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7566 str
= CoTaskMemAlloc( len
);
7567 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7568 r
= IStream_Write( stm
, str
, len
, NULL
);
7569 CoTaskMemFree( str
);
7573 /* read a string preceded by its length from a stream */
7574 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7577 DWORD len
, count
= 0;
7581 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7584 if( count
!= sizeof(len
) )
7585 return E_OUTOFMEMORY
;
7587 TRACE("%d bytes\n",len
);
7589 str
= CoTaskMemAlloc( len
);
7591 return E_OUTOFMEMORY
;
7593 r
= IStream_Read( stm
, str
, len
, &count
);
7598 CoTaskMemFree( str
);
7599 return E_OUTOFMEMORY
;
7602 TRACE("Read string %s\n",debugstr_an(str
,len
));
7604 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7605 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7607 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7608 CoTaskMemFree( str
);
7616 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7617 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7621 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7623 static const BYTE unknown1
[12] =
7624 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7625 0xFF, 0xFF, 0xFF, 0xFF};
7626 static const BYTE unknown2
[16] =
7627 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7630 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7631 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7632 debugstr_w(szProgIDName
));
7634 /* Create a CompObj stream */
7635 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7636 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7640 /* Write CompObj Structure to stream */
7641 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7643 if( SUCCEEDED( r
) )
7644 r
= WriteClassStm( pstm
, clsid
);
7646 if( SUCCEEDED( r
) )
7647 r
= STREAM_WriteString( pstm
, lpszUserType
);
7648 if( SUCCEEDED( r
) )
7649 r
= STREAM_WriteString( pstm
, szClipName
);
7650 if( SUCCEEDED( r
) )
7651 r
= STREAM_WriteString( pstm
, szProgIDName
);
7652 if( SUCCEEDED( r
) )
7653 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7655 IStream_Release( pstm
);
7660 /***********************************************************************
7661 * WriteFmtUserTypeStg (OLE32.@)
7663 HRESULT WINAPI
WriteFmtUserTypeStg(
7664 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7667 WCHAR szwClipName
[0x40];
7668 CLSID clsid
= CLSID_NULL
;
7669 LPWSTR wstrProgID
= NULL
;
7672 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7674 /* get the clipboard format name */
7675 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7678 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7680 /* FIXME: There's room to save a CLSID and its ProgID, but
7681 the CLSID is not looked up in the registry and in all the
7682 tests I wrote it was CLSID_NULL. Where does it come from?
7685 /* get the real program ID. This may fail, but that's fine */
7686 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7688 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7690 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7691 lpszUserType
, szwClipName
, wstrProgID
);
7693 CoTaskMemFree(wstrProgID
);
7699 /******************************************************************************
7700 * ReadFmtUserTypeStg [OLE32.@]
7702 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7706 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7707 unsigned char unknown1
[12];
7708 unsigned char unknown2
[16];
7710 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7713 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7715 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7716 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7719 WARN("Failed to open stream r = %08x\n", r
);
7723 /* read the various parts of the structure */
7724 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7725 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7727 r
= ReadClassStm( stm
, &clsid
);
7731 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7735 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7739 r
= STREAM_ReadString( stm
, &szProgIDName
);
7743 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7744 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7747 /* ok, success... now we just need to store what we found */
7749 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7750 CoTaskMemFree( szOleTypeName
);
7752 if( lplpszUserType
)
7753 *lplpszUserType
= szCLSIDName
;
7754 CoTaskMemFree( szProgIDName
);
7757 IStream_Release( stm
);
7763 /*************************************************************************
7764 * OLECONVERT_CreateCompObjStream [Internal]
7766 * Creates a "\001CompObj" is the destination IStorage if necessary.
7769 * pStorage [I] The dest IStorage to create the CompObj Stream
7771 * strOleTypeName [I] The ProgID
7775 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7778 * This function is used by OleConvertOLESTREAMToIStorage only.
7780 * The stream data is stored in the OLESTREAM and there should be
7781 * no need to recreate the stream. If the stream is manually
7782 * deleted it will attempt to create it by querying the registry.
7786 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7789 HRESULT hStorageRes
, hRes
= S_OK
;
7790 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7791 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7792 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7794 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7795 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7797 /* Initialize the CompObj structure */
7798 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7799 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7800 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7803 /* Create a CompObj stream if it doesn't exist */
7804 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7805 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7806 if(hStorageRes
== S_OK
)
7808 /* copy the OleTypeName to the compobj struct */
7809 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7810 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7812 /* copy the OleTypeName to the compobj struct */
7813 /* Note: in the test made, these were Identical */
7814 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7815 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7818 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7819 bufferW
, OLESTREAM_MAX_STR_LEN
);
7820 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7826 /* Get the CLSID Default Name from the Registry */
7827 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7828 if(hErr
== ERROR_SUCCESS
)
7830 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7831 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7832 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7833 if(hErr
== ERROR_SUCCESS
)
7835 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7841 /* Write CompObj Structure to stream */
7842 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7844 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7846 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7847 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7849 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7851 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7852 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7854 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7856 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7857 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7859 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7861 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7862 IStream_Release(pStream
);
7868 /*************************************************************************
7869 * OLECONVERT_CreateOlePresStream[Internal]
7871 * Creates the "\002OlePres000" Stream with the Metafile data
7874 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7875 * dwExtentX [I] Width of the Metafile
7876 * dwExtentY [I] Height of the Metafile
7877 * pData [I] Metafile data
7878 * dwDataLength [I] Size of the Metafile data
7882 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7885 * This function is used by OleConvertOLESTREAMToIStorage only.
7888 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7892 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7893 BYTE pOlePresStreamHeader
[] =
7895 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7896 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7897 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7898 0x00, 0x00, 0x00, 0x00
7901 BYTE pOlePresStreamHeaderEmpty
[] =
7903 0x00, 0x00, 0x00, 0x00,
7904 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7905 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7906 0x00, 0x00, 0x00, 0x00
7909 /* Create the OlePres000 Stream */
7910 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7911 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7916 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7918 memset(&OlePres
, 0, sizeof(OlePres
));
7919 /* Do we have any metafile data to save */
7920 if(dwDataLength
> 0)
7922 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7923 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7927 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7928 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7930 /* Set width and height of the metafile */
7931 OlePres
.dwExtentX
= dwExtentX
;
7932 OlePres
.dwExtentY
= -dwExtentY
;
7934 /* Set Data and Length */
7935 if(dwDataLength
> sizeof(METAFILEPICT16
))
7937 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7938 OlePres
.pData
= &(pData
[8]);
7940 /* Save OlePres000 Data to Stream */
7941 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7942 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7943 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7944 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7945 if(OlePres
.dwSize
> 0)
7947 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7949 IStream_Release(pStream
);
7953 /*************************************************************************
7954 * OLECONVERT_CreateOle10NativeStream [Internal]
7956 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7959 * pStorage [I] Dest storage to create the stream in
7960 * pData [I] Ole10 Native Data (ex. bmp)
7961 * dwDataLength [I] Size of the Ole10 Native Data
7967 * This function is used by OleConvertOLESTREAMToIStorage only.
7969 * Might need to verify the data and return appropriate error message
7972 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7976 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7978 /* Create the Ole10Native Stream */
7979 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7980 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7984 /* Write info to stream */
7985 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7986 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7987 IStream_Release(pStream
);
7992 /*************************************************************************
7993 * OLECONVERT_GetOLE10ProgID [Internal]
7995 * Finds the ProgID (or OleTypeID) from the IStorage
7998 * pStorage [I] The Src IStorage to get the ProgID
7999 * strProgID [I] the ProgID string to get
8000 * dwSize [I] the size of the string
8004 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8007 * This function is used by OleConvertIStorageToOLESTREAM only.
8011 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8015 LARGE_INTEGER iSeekPos
;
8016 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8017 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8019 /* Open the CompObj Stream */
8020 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8021 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8025 /*Get the OleType from the CompObj Stream */
8026 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8027 iSeekPos
.u
.HighPart
= 0;
8029 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8030 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8031 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8032 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8033 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8034 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8035 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8037 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8040 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8042 IStream_Release(pStream
);
8047 LPOLESTR wstrProgID
;
8049 /* Get the OleType from the registry */
8050 REFCLSID clsid
= &(stat
.clsid
);
8051 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8052 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8055 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8062 /*************************************************************************
8063 * OLECONVERT_GetOle10PresData [Internal]
8065 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8068 * pStorage [I] Src IStroage
8069 * pOleStream [I] Dest OleStream Mem Struct
8075 * This function is used by OleConvertIStorageToOLESTREAM only.
8077 * Memory allocated for pData must be freed by the caller
8081 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8086 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8088 /* Initialize Default data for OLESTREAM */
8089 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8090 pOleStreamData
[0].dwTypeID
= 2;
8091 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8092 pOleStreamData
[1].dwTypeID
= 0;
8093 pOleStreamData
[0].dwMetaFileWidth
= 0;
8094 pOleStreamData
[0].dwMetaFileHeight
= 0;
8095 pOleStreamData
[0].pData
= NULL
;
8096 pOleStreamData
[1].pData
= NULL
;
8098 /* Open Ole10Native Stream */
8099 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8100 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8104 /* Read Size and Data */
8105 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8106 if(pOleStreamData
->dwDataLength
> 0)
8108 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8109 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8111 IStream_Release(pStream
);
8117 /*************************************************************************
8118 * OLECONVERT_GetOle20PresData[Internal]
8120 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8123 * pStorage [I] Src IStroage
8124 * pOleStreamData [I] Dest OleStream Mem Struct
8130 * This function is used by OleConvertIStorageToOLESTREAM only.
8132 * Memory allocated for pData must be freed by the caller
8134 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8138 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8139 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8141 /* Initialize Default data for OLESTREAM */
8142 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8143 pOleStreamData
[0].dwTypeID
= 2;
8144 pOleStreamData
[0].dwMetaFileWidth
= 0;
8145 pOleStreamData
[0].dwMetaFileHeight
= 0;
8146 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8147 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8148 pOleStreamData
[1].dwTypeID
= 0;
8149 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8150 pOleStreamData
[1].strOleTypeName
[0] = 0;
8151 pOleStreamData
[1].dwMetaFileWidth
= 0;
8152 pOleStreamData
[1].dwMetaFileHeight
= 0;
8153 pOleStreamData
[1].pData
= NULL
;
8154 pOleStreamData
[1].dwDataLength
= 0;
8157 /* Open OlePress000 stream */
8158 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8159 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8162 LARGE_INTEGER iSeekPos
;
8163 METAFILEPICT16 MetaFilePict
;
8164 static const char strMetafilePictName
[] = "METAFILEPICT";
8166 /* Set the TypeID for a Metafile */
8167 pOleStreamData
[1].dwTypeID
= 5;
8169 /* Set the OleTypeName to Metafile */
8170 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8171 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8173 iSeekPos
.u
.HighPart
= 0;
8174 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8176 /* Get Presentation Data */
8177 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8178 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8179 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8180 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8182 /*Set width and Height */
8183 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8184 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8185 if(olePress
.dwSize
> 0)
8188 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8190 /* Set MetaFilePict struct */
8191 MetaFilePict
.mm
= 8;
8192 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8193 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8194 MetaFilePict
.hMF
= 0;
8196 /* Get Metafile Data */
8197 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8198 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8199 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8201 IStream_Release(pStream
);
8205 /*************************************************************************
8206 * OleConvertOLESTREAMToIStorage [OLE32.@]
8211 * DVTARGETDEVICE parameter is not handled
8212 * Still unsure of some mem fields for OLE 10 Stream
8213 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8214 * and "\001OLE" streams
8217 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8218 LPOLESTREAM pOleStream
,
8220 const DVTARGETDEVICE
* ptd
)
8224 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8226 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8228 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8232 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8235 if(pstg
== NULL
|| pOleStream
== NULL
)
8237 hRes
= E_INVALIDARG
;
8242 /* Load the OLESTREAM to Memory */
8243 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8248 /* Load the OLESTREAM to Memory (part 2)*/
8249 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8255 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8257 /* Do we have the IStorage Data in the OLESTREAM */
8258 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8260 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8261 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8265 /* It must be an original OLE 1.0 source */
8266 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8271 /* It must be an original OLE 1.0 source */
8272 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8275 /* Create CompObj Stream if necessary */
8276 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8279 /*Create the Ole Stream if necessary */
8280 OLECONVERT_CreateOleStream(pstg
);
8285 /* Free allocated memory */
8286 for(i
=0; i
< 2; i
++)
8288 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8289 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8290 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8295 /*************************************************************************
8296 * OleConvertIStorageToOLESTREAM [OLE32.@]
8303 * Still unsure of some mem fields for OLE 10 Stream
8304 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8305 * and "\001OLE" streams.
8308 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8310 LPOLESTREAM pOleStream
)
8313 HRESULT hRes
= S_OK
;
8315 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8316 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8318 TRACE("%p %p\n", pstg
, pOleStream
);
8320 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8322 if(pstg
== NULL
|| pOleStream
== NULL
)
8324 hRes
= E_INVALIDARG
;
8328 /* Get the ProgID */
8329 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8330 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8334 /* Was it originally Ole10 */
8335 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8338 IStream_Release(pStream
);
8339 /* Get Presentation Data for Ole10Native */
8340 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8344 /* Get Presentation Data (OLE20) */
8345 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8348 /* Save OLESTREAM */
8349 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8352 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8357 /* Free allocated memory */
8358 for(i
=0; i
< 2; i
++)
8360 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8366 /***********************************************************************
8367 * GetConvertStg (OLE32.@)
8369 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8370 FIXME("unimplemented stub!\n");
8374 /******************************************************************************
8375 * StgIsStorageFile [OLE32.@]
8376 * Verify if the file contains a storage object
8382 * S_OK if file has magic bytes as a storage object
8383 * S_FALSE if file is not storage
8386 StgIsStorageFile(LPCOLESTR fn
)
8392 TRACE("%s\n", debugstr_w(fn
));
8393 hf
= CreateFileW(fn
, GENERIC_READ
,
8394 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8395 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8397 if (hf
== INVALID_HANDLE_VALUE
)
8398 return STG_E_FILENOTFOUND
;
8400 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8402 WARN(" unable to read file\n");
8409 if (bytes_read
!= 8) {
8410 TRACE(" too short\n");
8414 if (!memcmp(magic
,STORAGE_magic
,8)) {
8419 TRACE(" -> Invalid header.\n");
8423 /***********************************************************************
8424 * WriteClassStm (OLE32.@)
8426 * Writes a CLSID to a stream.
8429 * pStm [I] Stream to write to.
8430 * rclsid [I] CLSID to write.
8434 * Failure: HRESULT code.
8436 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8438 TRACE("(%p,%p)\n",pStm
,rclsid
);
8440 if (!pStm
|| !rclsid
)
8441 return E_INVALIDARG
;
8443 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8446 /***********************************************************************
8447 * ReadClassStm (OLE32.@)
8449 * Reads a CLSID from a stream.
8452 * pStm [I] Stream to read from.
8453 * rclsid [O] CLSID to read.
8457 * Failure: HRESULT code.
8459 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8464 TRACE("(%p,%p)\n",pStm
,pclsid
);
8466 if (!pStm
|| !pclsid
)
8467 return E_INVALIDARG
;
8469 /* clear the output args */
8470 *pclsid
= CLSID_NULL
;
8472 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8477 if (nbByte
!= sizeof(CLSID
))
8478 return STG_E_READFAULT
;