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 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1138 * Insert the new directory entry into the parent storage's tree
1142 This
->storageDirEntry
,
1146 * Open it to get a pointer to return.
1148 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1150 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1160 /***************************************************************************
1164 * Reserve a directory entry in the file and initialize it.
1166 static HRESULT
StorageImpl_CreateDirEntry(
1167 StorageBaseImpl
*base
,
1168 const DirEntry
*newData
,
1171 StorageImpl
*storage
= (StorageImpl
*)base
;
1172 ULONG currentEntryIndex
= 0;
1173 ULONG newEntryIndex
= DIRENTRY_NULL
;
1175 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1176 WORD sizeOfNameString
;
1180 hr
= StorageImpl_ReadRawDirEntry(storage
,
1186 StorageUtl_ReadWord(
1188 OFFSET_PS_NAMELENGTH
,
1191 if (sizeOfNameString
== 0)
1194 * The entry exists and is available, we found it.
1196 newEntryIndex
= currentEntryIndex
;
1202 * We exhausted the directory entries, we will create more space below
1204 newEntryIndex
= currentEntryIndex
;
1206 currentEntryIndex
++;
1208 } while (newEntryIndex
== DIRENTRY_NULL
);
1211 * grow the directory stream
1215 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1216 ULARGE_INTEGER newSize
;
1218 ULONG lastEntry
= 0;
1219 ULONG blockCount
= 0;
1222 * obtain the new count of blocks in the directory stream
1224 blockCount
= BlockChainStream_GetCount(
1225 storage
->rootBlockChain
)+1;
1228 * initialize the size used by the directory stream
1230 newSize
.u
.HighPart
= 0;
1231 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1234 * add a block to the directory stream
1236 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1239 * memset the empty entry in order to initialize the unused newly
1242 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1247 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1250 entryIndex
= newEntryIndex
+ 1;
1251 entryIndex
< lastEntry
;
1254 StorageImpl_WriteRawDirEntry(
1261 UpdateRawDirEntry(currentData
, newData
);
1263 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1266 *index
= newEntryIndex
;
1271 /***************************************************************************
1275 * Mark a directory entry in the file as free.
1277 static HRESULT
StorageImpl_DestroyDirEntry(
1278 StorageBaseImpl
*base
,
1282 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1283 StorageImpl
*storage
= (StorageImpl
*)base
;
1285 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1287 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1293 /***************************************************************************
1297 * Destroy an entry, its attached data, and all entries reachable from it.
1299 static HRESULT
DestroyReachableEntries(
1300 StorageBaseImpl
*base
,
1305 ULARGE_INTEGER zero
;
1309 if (index
!= DIRENTRY_NULL
)
1311 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1314 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1317 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1320 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1323 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1326 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1333 /****************************************************************************
1337 * Case insensitive comparison of DirEntry.name by first considering
1340 * Returns <0 when name1 < name2
1341 * >0 when name1 > name2
1342 * 0 when name1 == name2
1344 static LONG
entryNameCmp(
1345 const OLECHAR
*name1
,
1346 const OLECHAR
*name2
)
1348 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1350 while (diff
== 0 && *name1
!= 0)
1353 * We compare the string themselves only when they are of the same length
1355 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1361 /****************************************************************************
1365 * Add a directory entry to a storage
1367 static HRESULT
insertIntoTree(
1368 StorageBaseImpl
*This
,
1369 DirRef parentStorageIndex
,
1370 DirRef newEntryIndex
)
1372 DirEntry currentEntry
;
1376 * Read the inserted entry
1378 StorageBaseImpl_ReadDirEntry(This
,
1383 * Read the storage entry
1385 StorageBaseImpl_ReadDirEntry(This
,
1389 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1392 * The root storage contains some element, therefore, start the research
1393 * for the appropriate location.
1396 DirRef current
, next
, previous
, currentEntryId
;
1399 * Keep a reference to the root of the storage's element tree
1401 currentEntryId
= currentEntry
.dirRootEntry
;
1406 StorageBaseImpl_ReadDirEntry(This
,
1407 currentEntry
.dirRootEntry
,
1410 previous
= currentEntry
.leftChild
;
1411 next
= currentEntry
.rightChild
;
1412 current
= currentEntryId
;
1416 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1420 if (previous
!= DIRENTRY_NULL
)
1422 StorageBaseImpl_ReadDirEntry(This
,
1429 currentEntry
.leftChild
= newEntryIndex
;
1430 StorageBaseImpl_WriteDirEntry(This
,
1438 if (next
!= DIRENTRY_NULL
)
1440 StorageBaseImpl_ReadDirEntry(This
,
1447 currentEntry
.rightChild
= newEntryIndex
;
1448 StorageBaseImpl_WriteDirEntry(This
,
1457 * Trying to insert an item with the same name in the
1458 * subtree structure.
1460 return STG_E_FILEALREADYEXISTS
;
1463 previous
= currentEntry
.leftChild
;
1464 next
= currentEntry
.rightChild
;
1470 * The storage is empty, make the new entry the root of its element tree
1472 currentEntry
.dirRootEntry
= newEntryIndex
;
1473 StorageBaseImpl_WriteDirEntry(This
,
1481 /****************************************************************************
1485 * Find and read the element of a storage with the given name.
1487 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1488 const OLECHAR
*name
, DirEntry
*data
)
1490 DirRef currentEntry
;
1492 /* Read the storage entry to find the root of the tree. */
1493 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1495 currentEntry
= data
->dirRootEntry
;
1497 while (currentEntry
!= DIRENTRY_NULL
)
1501 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1503 cmp
= entryNameCmp(name
, data
->name
);
1510 currentEntry
= data
->leftChild
;
1513 currentEntry
= data
->rightChild
;
1516 return currentEntry
;
1519 /****************************************************************************
1523 * Find and read the binary tree parent of the element with the given name.
1525 * If there is no such element, find a place where it could be inserted and
1526 * return STG_E_FILENOTFOUND.
1528 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1529 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1535 /* Read the storage entry to find the root of the tree. */
1536 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1538 *parentEntry
= storageEntry
;
1539 *relation
= DIRENTRY_RELATION_DIR
;
1541 childEntry
= parentData
->dirRootEntry
;
1543 while (childEntry
!= DIRENTRY_NULL
)
1547 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1549 cmp
= entryNameCmp(childName
, childData
.name
);
1557 *parentData
= childData
;
1558 *parentEntry
= childEntry
;
1559 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1561 childEntry
= parentData
->leftChild
;
1566 *parentData
= childData
;
1567 *parentEntry
= childEntry
;
1568 *relation
= DIRENTRY_RELATION_NEXT
;
1570 childEntry
= parentData
->rightChild
;
1574 if (childEntry
== DIRENTRY_NULL
)
1575 return STG_E_FILENOTFOUND
;
1581 /*************************************************************************
1584 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1586 DWORD ciidExclude
, /* [in] */
1587 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1588 SNB snbExclude
, /* [unique][in] */
1589 IStorage
* pstgDest
) /* [unique][in] */
1591 IEnumSTATSTG
*elements
= 0;
1592 STATSTG curElement
, strStat
;
1594 IStorage
*pstgTmp
, *pstgChild
;
1595 IStream
*pstrTmp
, *pstrChild
;
1596 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1599 TRACE("(%p, %d, %p, %p, %p)\n",
1600 iface
, ciidExclude
, rgiidExclude
,
1601 snbExclude
, pstgDest
);
1603 if ( pstgDest
== 0 )
1604 return STG_E_INVALIDPOINTER
;
1607 * Enumerate the elements
1609 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1617 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1618 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1620 for(i
= 0; i
< ciidExclude
; ++i
)
1622 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1623 skip_storage
= TRUE
;
1624 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1627 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1633 * Obtain the next element
1635 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1637 if ( hr
== S_FALSE
)
1639 hr
= S_OK
; /* done, every element has been copied */
1645 WCHAR
**snb
= snbExclude
;
1647 while ( *snb
!= NULL
&& !skip
)
1649 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1658 if (curElement
.type
== STGTY_STORAGE
)
1664 * open child source storage
1666 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1667 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1668 NULL
, 0, &pstgChild
);
1674 * create a new storage in destination storage
1676 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1677 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1681 * if it already exist, don't create a new one use this one
1683 if (hr
== STG_E_FILEALREADYEXISTS
)
1685 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1686 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1687 NULL
, 0, &pstgTmp
);
1693 * do the copy recursively
1695 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1698 IStorage_Release( pstgTmp
);
1701 IStorage_Release( pstgChild
);
1703 else if (curElement
.type
== STGTY_STREAM
)
1709 * create a new stream in destination storage. If the stream already
1710 * exist, it will be deleted and a new one will be created.
1712 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1713 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1720 * open child stream storage
1722 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1723 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1729 * Get the size of the source stream
1731 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1734 * Set the size of the destination stream.
1736 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1741 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1744 IStream_Release( pstrChild
);
1747 IStream_Release( pstrTmp
);
1751 WARN("unknown element type: %d\n", curElement
.type
);
1755 CoTaskMemFree(curElement
.pwcsName
);
1756 } while (hr
== S_OK
);
1761 IEnumSTATSTG_Release(elements
);
1766 /*************************************************************************
1767 * MoveElementTo (IStorage)
1769 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1771 const OLECHAR
*pwcsName
, /* [string][in] */
1772 IStorage
*pstgDest
, /* [unique][in] */
1773 const OLECHAR
*pwcsNewName
,/* [string][in] */
1774 DWORD grfFlags
) /* [in] */
1776 FIXME("(%p %s %p %s %u): stub\n", iface
,
1777 debugstr_w(pwcsName
), pstgDest
,
1778 debugstr_w(pwcsNewName
), grfFlags
);
1782 /*************************************************************************
1785 * Ensures that any changes made to a storage object open in transacted mode
1786 * are reflected in the parent storage
1789 * Wine doesn't implement transacted mode, which seems to be a basic
1790 * optimization, so we can ignore this stub for now.
1792 static HRESULT WINAPI
StorageImpl_Commit(
1794 DWORD grfCommitFlags
)/* [in] */
1796 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1800 /*************************************************************************
1803 * Discard all changes that have been made since the last commit operation
1805 static HRESULT WINAPI
StorageImpl_Revert(
1808 TRACE("(%p)\n", iface
);
1812 /*************************************************************************
1813 * DestroyElement (IStorage)
1815 * Strategy: This implementation is built this way for simplicity not for speed.
1816 * I always delete the topmost element of the enumeration and adjust
1817 * the deleted element pointer all the time. This takes longer to
1818 * do but allow to reinvoke DestroyElement whenever we encounter a
1819 * storage object. The optimisation resides in the usage of another
1820 * enumeration strategy that would give all the leaves of a storage
1821 * first. (postfix order)
1823 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1825 const OLECHAR
*pwcsName
)/* [string][in] */
1827 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1830 DirEntry entryToDelete
;
1831 DirRef entryToDeleteRef
;
1834 iface
, debugstr_w(pwcsName
));
1837 return STG_E_INVALIDPOINTER
;
1840 return STG_E_REVERTED
;
1842 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1843 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1844 return STG_E_ACCESSDENIED
;
1846 entryToDeleteRef
= findElement(
1848 This
->storageDirEntry
,
1852 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1854 return STG_E_FILENOTFOUND
;
1857 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1859 hr
= deleteStorageContents(
1864 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1866 hr
= deleteStreamContents(
1876 * Remove the entry from its parent storage
1878 hr
= removeFromTree(
1880 This
->storageDirEntry
,
1884 * Invalidate the entry
1887 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1893 /******************************************************************************
1894 * Internal stream list handlers
1897 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1899 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1900 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1903 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1905 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1906 list_remove(&(strm
->StrmListEntry
));
1909 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1911 StgStreamImpl
*strm
;
1913 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1915 if (strm
->dirEntry
== streamEntry
)
1924 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1926 StorageInternalImpl
*childstg
;
1928 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1930 if (childstg
->base
.storageDirEntry
== storageEntry
)
1939 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1941 struct list
*cur
, *cur2
;
1942 StgStreamImpl
*strm
=NULL
;
1943 StorageInternalImpl
*childstg
=NULL
;
1945 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1946 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1947 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1948 strm
->parentStorage
= NULL
;
1952 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1953 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1954 StorageBaseImpl_Invalidate( &childstg
->base
);
1957 if (stg
->transactedChild
)
1959 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1961 stg
->transactedChild
= NULL
;
1966 /*********************************************************************
1970 * Delete the contents of a storage entry.
1973 static HRESULT
deleteStorageContents(
1974 StorageBaseImpl
*parentStorage
,
1975 DirRef indexToDelete
,
1976 DirEntry entryDataToDelete
)
1978 IEnumSTATSTG
*elements
= 0;
1979 IStorage
*childStorage
= 0;
1980 STATSTG currentElement
;
1982 HRESULT destroyHr
= S_OK
;
1983 StorageInternalImpl
*stg
, *stg2
;
1985 /* Invalidate any open storage objects. */
1986 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1988 if (stg
->base
.storageDirEntry
== indexToDelete
)
1990 StorageBaseImpl_Invalidate(&stg
->base
);
1995 * Open the storage and enumerate it
1997 hr
= StorageBaseImpl_OpenStorage(
1998 (IStorage
*)parentStorage
,
1999 entryDataToDelete
.name
,
2001 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2012 * Enumerate the elements
2014 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2019 * Obtain the next element
2021 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2024 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2026 CoTaskMemFree(currentElement
.pwcsName
);
2030 * We need to Reset the enumeration every time because we delete elements
2031 * and the enumeration could be invalid
2033 IEnumSTATSTG_Reset(elements
);
2035 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2037 IStorage_Release(childStorage
);
2038 IEnumSTATSTG_Release(elements
);
2043 /*********************************************************************
2047 * Perform the deletion of a stream's data
2050 static HRESULT
deleteStreamContents(
2051 StorageBaseImpl
*parentStorage
,
2052 DirRef indexToDelete
,
2053 DirEntry entryDataToDelete
)
2057 ULARGE_INTEGER size
;
2058 StgStreamImpl
*strm
, *strm2
;
2060 /* Invalidate any open stream objects. */
2061 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2063 if (strm
->dirEntry
== indexToDelete
)
2065 TRACE("Stream deleted %p\n", strm
);
2066 strm
->parentStorage
= NULL
;
2067 list_remove(&strm
->StrmListEntry
);
2071 size
.u
.HighPart
= 0;
2074 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2075 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2085 hr
= IStream_SetSize(pis
, size
);
2093 * Release the stream object.
2095 IStream_Release(pis
);
2100 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2104 case DIRENTRY_RELATION_PREVIOUS
:
2105 entry
->leftChild
= new_target
;
2107 case DIRENTRY_RELATION_NEXT
:
2108 entry
->rightChild
= new_target
;
2110 case DIRENTRY_RELATION_DIR
:
2111 entry
->dirRootEntry
= new_target
;
2118 /*************************************************************************
2122 * This method removes a directory entry from its parent storage tree without
2123 * freeing any resources attached to it.
2125 static HRESULT
removeFromTree(
2126 StorageBaseImpl
*This
,
2127 DirRef parentStorageIndex
,
2128 DirRef deletedIndex
)
2131 DirEntry entryToDelete
;
2132 DirEntry parentEntry
;
2133 DirRef parentEntryRef
;
2134 ULONG typeOfRelation
;
2136 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2142 * Find the element that links to the one we want to delete.
2144 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2145 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2150 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2153 * Replace the deleted entry with its left child
2155 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2157 hr
= StorageBaseImpl_WriteDirEntry(
2166 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2169 * We need to reinsert the right child somewhere. We already know it and
2170 * its children are greater than everything in the left tree, so we
2171 * insert it at the rightmost point in the left tree.
2173 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2174 DirEntry newRightChildParentEntry
;
2178 hr
= StorageBaseImpl_ReadDirEntry(
2180 newRightChildParent
,
2181 &newRightChildParentEntry
);
2187 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2188 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2189 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2191 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2193 hr
= StorageBaseImpl_WriteDirEntry(
2195 newRightChildParent
,
2196 &newRightChildParentEntry
);
2206 * Replace the deleted entry with its right child
2208 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2210 hr
= StorageBaseImpl_WriteDirEntry(
2224 /******************************************************************************
2225 * SetElementTimes (IStorage)
2227 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2229 const OLECHAR
*pwcsName
,/* [string][in] */
2230 const FILETIME
*pctime
, /* [in] */
2231 const FILETIME
*patime
, /* [in] */
2232 const FILETIME
*pmtime
) /* [in] */
2234 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2238 /******************************************************************************
2239 * SetStateBits (IStorage)
2241 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2243 DWORD grfStateBits
,/* [in] */
2244 DWORD grfMask
) /* [in] */
2246 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2249 return STG_E_REVERTED
;
2251 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2255 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2256 DirRef index
, const DirEntry
*data
)
2258 StorageImpl
*This
= (StorageImpl
*)base
;
2259 return StorageImpl_WriteDirEntry(This
, index
, data
);
2262 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2263 DirRef index
, DirEntry
*data
)
2265 StorageImpl
*This
= (StorageImpl
*)base
;
2266 return StorageImpl_ReadDirEntry(This
, index
, data
);
2269 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2273 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2275 if (!This
->blockChainCache
[i
])
2277 return &This
->blockChainCache
[i
];
2281 i
= This
->blockChainToEvict
;
2283 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2284 This
->blockChainCache
[i
] = NULL
;
2286 This
->blockChainToEvict
++;
2287 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2288 This
->blockChainToEvict
= 0;
2290 return &This
->blockChainCache
[i
];
2293 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2296 int i
, free_index
=-1;
2298 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2300 if (!This
->blockChainCache
[i
])
2302 if (free_index
== -1) free_index
= i
;
2304 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2306 return &This
->blockChainCache
[i
];
2310 if (free_index
== -1)
2312 free_index
= This
->blockChainToEvict
;
2314 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2315 This
->blockChainCache
[free_index
] = NULL
;
2317 This
->blockChainToEvict
++;
2318 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2319 This
->blockChainToEvict
= 0;
2322 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2323 return &This
->blockChainCache
[free_index
];
2326 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2327 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2329 StorageImpl
*This
= (StorageImpl
*)base
;
2334 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2335 if (FAILED(hr
)) return hr
;
2337 if (data
.size
.QuadPart
== 0)
2343 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2345 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2352 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2354 SmallBlockChainStream
*stream
;
2356 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2357 if (!stream
) return E_OUTOFMEMORY
;
2359 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2361 SmallBlockChainStream_Destroy(stream
);
2367 BlockChainStream
*stream
= NULL
;
2369 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2370 if (!stream
) return E_OUTOFMEMORY
;
2372 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2378 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2379 ULARGE_INTEGER newsize
)
2381 StorageImpl
*This
= (StorageImpl
*)base
;
2384 SmallBlockChainStream
*smallblock
=NULL
;
2385 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2387 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2388 if (FAILED(hr
)) return hr
;
2390 /* In simple mode keep the stream size above the small block limit */
2391 if (This
->base
.openFlags
& STGM_SIMPLE
)
2392 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2394 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2397 /* Create a block chain object of the appropriate type */
2398 if (data
.size
.QuadPart
== 0)
2400 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2402 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2403 if (!smallblock
) return E_OUTOFMEMORY
;
2407 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2408 bigblock
= *pbigblock
;
2409 if (!bigblock
) return E_OUTOFMEMORY
;
2412 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2414 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2415 if (!smallblock
) return E_OUTOFMEMORY
;
2419 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2420 bigblock
= *pbigblock
;
2421 if (!bigblock
) return E_OUTOFMEMORY
;
2424 /* Change the block chain type if necessary. */
2425 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2427 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2430 SmallBlockChainStream_Destroy(smallblock
);
2434 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2435 *pbigblock
= bigblock
;
2437 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2439 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2444 /* Set the size of the block chain. */
2447 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2448 SmallBlockChainStream_Destroy(smallblock
);
2452 BlockChainStream_SetSize(bigblock
, newsize
);
2455 /* Set the size in the directory entry. */
2456 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2459 data
.size
= newsize
;
2461 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2466 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2467 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2469 StorageImpl
*This
= (StorageImpl
*)base
;
2472 ULARGE_INTEGER newSize
;
2474 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2475 if (FAILED(hr
)) return hr
;
2477 /* Grow the stream if necessary */
2478 newSize
.QuadPart
= 0;
2479 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2481 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2483 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2487 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2488 if (FAILED(hr
)) return hr
;
2491 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2493 SmallBlockChainStream
*stream
;
2495 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2496 if (!stream
) return E_OUTOFMEMORY
;
2498 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2500 SmallBlockChainStream_Destroy(stream
);
2506 BlockChainStream
*stream
;
2508 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2509 if (!stream
) return E_OUTOFMEMORY
;
2511 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2518 * Virtual function table for the IStorage32Impl class.
2520 static const IStorageVtbl Storage32Impl_Vtbl
=
2522 StorageBaseImpl_QueryInterface
,
2523 StorageBaseImpl_AddRef
,
2524 StorageBaseImpl_Release
,
2525 StorageBaseImpl_CreateStream
,
2526 StorageBaseImpl_OpenStream
,
2527 StorageBaseImpl_CreateStorage
,
2528 StorageBaseImpl_OpenStorage
,
2529 StorageBaseImpl_CopyTo
,
2530 StorageBaseImpl_MoveElementTo
,
2533 StorageBaseImpl_EnumElements
,
2534 StorageBaseImpl_DestroyElement
,
2535 StorageBaseImpl_RenameElement
,
2536 StorageBaseImpl_SetElementTimes
,
2537 StorageBaseImpl_SetClass
,
2538 StorageBaseImpl_SetStateBits
,
2539 StorageBaseImpl_Stat
2542 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2544 StorageImpl_Destroy
,
2545 StorageImpl_Invalidate
,
2546 StorageImpl_CreateDirEntry
,
2547 StorageImpl_BaseWriteDirEntry
,
2548 StorageImpl_BaseReadDirEntry
,
2549 StorageImpl_DestroyDirEntry
,
2550 StorageImpl_StreamReadAt
,
2551 StorageImpl_StreamWriteAt
,
2552 StorageImpl_StreamSetSize
2555 static HRESULT
StorageImpl_Construct(
2562 StorageImpl
** result
)
2566 DirEntry currentEntry
;
2567 DirRef currentEntryRef
;
2568 WCHAR fullpath
[MAX_PATH
];
2570 if ( FAILED( validateSTGM(openFlags
) ))
2571 return STG_E_INVALIDFLAG
;
2573 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2575 return E_OUTOFMEMORY
;
2577 memset(This
, 0, sizeof(StorageImpl
));
2579 list_init(&This
->base
.strmHead
);
2581 list_init(&This
->base
.storageHead
);
2583 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2584 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2585 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2586 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2588 This
->base
.create
= create
;
2590 This
->base
.reverted
= 0;
2592 This
->hFile
= hFile
;
2595 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2597 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2599 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2600 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2601 if (!This
->pwcsName
)
2603 hr
= STG_E_INSUFFICIENTMEMORY
;
2606 strcpyW(This
->pwcsName
, fullpath
);
2607 This
->base
.filename
= This
->pwcsName
;
2611 * Initialize the big block cache.
2613 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2614 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2615 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2621 if (This
->bigBlockFile
== 0)
2629 ULARGE_INTEGER size
;
2630 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2633 * Initialize all header variables:
2634 * - The big block depot consists of one block and it is at block 0
2635 * - The directory table starts at block 1
2636 * - There is no small block depot
2638 memset( This
->bigBlockDepotStart
,
2640 sizeof(This
->bigBlockDepotStart
));
2642 This
->bigBlockDepotCount
= 1;
2643 This
->bigBlockDepotStart
[0] = 0;
2644 This
->rootStartBlock
= 1;
2645 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2646 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2647 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2648 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2649 This
->extBigBlockDepotCount
= 0;
2651 StorageImpl_SaveFileHeader(This
);
2654 * Add one block for the big block depot and one block for the directory table
2656 size
.u
.HighPart
= 0;
2657 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2658 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2661 * Initialize the big block depot
2663 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2664 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2665 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2666 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2671 * Load the header for the file.
2673 hr
= StorageImpl_LoadFileHeader(This
);
2682 * There is no block depot cached yet.
2684 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2687 * Start searching for free blocks with block 0.
2689 This
->prevFreeBlock
= 0;
2692 * Create the block chain abstractions.
2694 if(!(This
->rootBlockChain
=
2695 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2697 hr
= STG_E_READFAULT
;
2701 if(!(This
->smallBlockDepotChain
=
2702 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2705 hr
= STG_E_READFAULT
;
2710 * Write the root storage entry (memory only)
2716 * Initialize the directory table
2718 memset(&rootEntry
, 0, sizeof(rootEntry
));
2719 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2720 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2721 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2722 rootEntry
.stgType
= STGTY_ROOT
;
2723 rootEntry
.leftChild
= DIRENTRY_NULL
;
2724 rootEntry
.rightChild
= DIRENTRY_NULL
;
2725 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2726 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2727 rootEntry
.size
.u
.HighPart
= 0;
2728 rootEntry
.size
.u
.LowPart
= 0;
2730 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2734 * Find the ID of the root storage.
2736 currentEntryRef
= 0;
2740 hr
= StorageImpl_ReadDirEntry(
2747 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2748 (currentEntry
.stgType
== STGTY_ROOT
) )
2750 This
->base
.storageDirEntry
= currentEntryRef
;
2756 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2760 hr
= STG_E_READFAULT
;
2765 * Create the block chain abstraction for the small block root chain.
2767 if(!(This
->smallBlockRootChain
=
2768 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2770 hr
= STG_E_READFAULT
;
2776 IStorage_Release((IStorage
*)This
);
2785 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2787 StorageImpl
*This
= (StorageImpl
*) iface
;
2789 StorageBaseImpl_DeleteAll(&This
->base
);
2791 This
->base
.reverted
= 1;
2794 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2796 StorageImpl
*This
= (StorageImpl
*) iface
;
2798 TRACE("(%p)\n", This
);
2800 StorageImpl_Invalidate(iface
);
2802 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2804 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2805 BlockChainStream_Destroy(This
->rootBlockChain
);
2806 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2808 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2809 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2811 if (This
->bigBlockFile
)
2812 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2813 HeapFree(GetProcessHeap(), 0, This
);
2816 /******************************************************************************
2817 * Storage32Impl_GetNextFreeBigBlock
2819 * Returns the index of the next free big block.
2820 * If the big block depot is filled, this method will enlarge it.
2823 static ULONG
StorageImpl_GetNextFreeBigBlock(
2826 ULONG depotBlockIndexPos
;
2827 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2829 ULONG depotBlockOffset
;
2830 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2831 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2833 ULONG freeBlock
= BLOCK_UNUSED
;
2835 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2836 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2839 * Scan the entire big block depot until we find a block marked free
2841 while (nextBlockIndex
!= BLOCK_UNUSED
)
2843 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2845 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2848 * Grow the primary depot.
2850 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2852 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2855 * Add a block depot.
2857 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2858 This
->bigBlockDepotCount
++;
2859 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2862 * Flag it as a block depot.
2864 StorageImpl_SetNextBlockInChain(This
,
2868 /* Save new header information.
2870 StorageImpl_SaveFileHeader(This
);
2875 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2877 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2880 * Grow the extended depot.
2882 ULONG extIndex
= BLOCK_UNUSED
;
2883 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2884 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2886 if (extBlockOffset
== 0)
2888 /* We need an extended block.
2890 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2891 This
->extBigBlockDepotCount
++;
2892 depotBlockIndexPos
= extIndex
+ 1;
2895 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2898 * Add a block depot and mark it in the extended block.
2900 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2901 This
->bigBlockDepotCount
++;
2902 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2904 /* Flag the block depot.
2906 StorageImpl_SetNextBlockInChain(This
,
2910 /* If necessary, flag the extended depot block.
2912 if (extIndex
!= BLOCK_UNUSED
)
2913 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2915 /* Save header information.
2917 StorageImpl_SaveFileHeader(This
);
2921 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2925 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2926 ( nextBlockIndex
!= BLOCK_UNUSED
))
2928 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2930 if (nextBlockIndex
== BLOCK_UNUSED
)
2932 freeBlock
= (depotIndex
* blocksPerDepot
) +
2933 (depotBlockOffset
/sizeof(ULONG
));
2936 depotBlockOffset
+= sizeof(ULONG
);
2941 depotBlockOffset
= 0;
2945 * make sure that the block physically exists before using it
2947 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2949 This
->prevFreeBlock
= freeBlock
;
2954 /******************************************************************************
2955 * Storage32Impl_AddBlockDepot
2957 * This will create a depot block, essentially it is a block initialized
2960 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2962 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2965 * Initialize blocks as free
2967 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2968 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2971 /******************************************************************************
2972 * Storage32Impl_GetExtDepotBlock
2974 * Returns the index of the block that corresponds to the specified depot
2975 * index. This method is only for depot indexes equal or greater than
2976 * COUNT_BBDEPOTINHEADER.
2978 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2980 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2981 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2982 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2983 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2984 ULONG blockIndex
= BLOCK_UNUSED
;
2985 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2987 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2989 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2990 return BLOCK_UNUSED
;
2992 while (extBlockCount
> 0)
2994 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2998 if (extBlockIndex
!= BLOCK_UNUSED
)
2999 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3000 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3005 /******************************************************************************
3006 * Storage32Impl_SetExtDepotBlock
3008 * Associates the specified block index to the specified depot index.
3009 * This method is only for depot indexes equal or greater than
3010 * COUNT_BBDEPOTINHEADER.
3012 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3014 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3015 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3016 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3017 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3018 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3020 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3022 while (extBlockCount
> 0)
3024 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3028 if (extBlockIndex
!= BLOCK_UNUSED
)
3030 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3031 extBlockOffset
* sizeof(ULONG
),
3036 /******************************************************************************
3037 * Storage32Impl_AddExtBlockDepot
3039 * Creates an extended depot block.
3041 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3043 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3044 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3045 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3046 ULONG index
= BLOCK_UNUSED
;
3047 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3048 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3049 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3051 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3052 blocksPerDepotBlock
;
3054 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3057 * The first extended block.
3059 This
->extBigBlockDepotStart
= index
;
3065 * Follow the chain to the last one.
3067 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3069 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3073 * Add the new extended block to the chain.
3075 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3080 * Initialize this block.
3082 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3083 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3088 /******************************************************************************
3089 * Storage32Impl_FreeBigBlock
3091 * This method will flag the specified block as free in the big block depot.
3093 static void StorageImpl_FreeBigBlock(
3097 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3099 if (blockIndex
< This
->prevFreeBlock
)
3100 This
->prevFreeBlock
= blockIndex
;
3103 /************************************************************************
3104 * Storage32Impl_GetNextBlockInChain
3106 * This method will retrieve the block index of the next big block in
3109 * Params: This - Pointer to the Storage object.
3110 * blockIndex - Index of the block to retrieve the chain
3112 * nextBlockIndex - receives the return value.
3114 * Returns: This method returns the index of the next block in the chain.
3115 * It will return the constants:
3116 * BLOCK_SPECIAL - If the block given was not part of a
3118 * BLOCK_END_OF_CHAIN - If the block given was the last in
3120 * BLOCK_UNUSED - If the block given was not past of a chain
3122 * BLOCK_EXTBBDEPOT - This block is part of the extended
3125 * See Windows documentation for more details on IStorage methods.
3127 static HRESULT
StorageImpl_GetNextBlockInChain(
3130 ULONG
* nextBlockIndex
)
3132 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3133 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3134 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3135 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3137 ULONG depotBlockIndexPos
;
3140 *nextBlockIndex
= BLOCK_SPECIAL
;
3142 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3144 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3145 This
->bigBlockDepotCount
);
3146 return STG_E_READFAULT
;
3150 * Cache the currently accessed depot block.
3152 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3154 This
->indexBlockDepotCached
= depotBlockCount
;
3156 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3158 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3163 * We have to look in the extended depot.
3165 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3168 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3171 return STG_E_READFAULT
;
3173 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3175 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3176 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3180 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3185 /******************************************************************************
3186 * Storage32Impl_GetNextExtendedBlock
3188 * Given an extended block this method will return the next extended block.
3191 * The last ULONG of an extended block is the block index of the next
3192 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3196 * - The index of the next extended block
3197 * - BLOCK_UNUSED: there is no next extended block.
3198 * - Any other return values denotes failure.
3200 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3202 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3203 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3205 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3208 return nextBlockIndex
;
3211 /******************************************************************************
3212 * Storage32Impl_SetNextBlockInChain
3214 * This method will write the index of the specified block's next block
3215 * in the big block depot.
3217 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3220 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3221 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3222 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3225 static void StorageImpl_SetNextBlockInChain(
3230 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3231 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3232 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3233 ULONG depotBlockIndexPos
;
3235 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3236 assert(blockIndex
!= nextBlock
);
3238 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3240 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3245 * We have to look in the extended depot.
3247 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3250 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3253 * Update the cached block depot, if necessary.
3255 if (depotBlockCount
== This
->indexBlockDepotCached
)
3257 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3261 /******************************************************************************
3262 * Storage32Impl_LoadFileHeader
3264 * This method will read in the file header, i.e. big block index -1.
3266 static HRESULT
StorageImpl_LoadFileHeader(
3269 HRESULT hr
= STG_E_FILENOTFOUND
;
3270 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3276 * Get a pointer to the big block of data containing the header.
3278 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3281 * Extract the information from the header.
3286 * Check for the "magic number" signature and return an error if it is not
3289 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3291 return STG_E_OLDFORMAT
;
3294 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3296 return STG_E_INVALIDHEADER
;
3299 StorageUtl_ReadWord(
3301 OFFSET_BIGBLOCKSIZEBITS
,
3302 &This
->bigBlockSizeBits
);
3304 StorageUtl_ReadWord(
3306 OFFSET_SMALLBLOCKSIZEBITS
,
3307 &This
->smallBlockSizeBits
);
3309 StorageUtl_ReadDWord(
3311 OFFSET_BBDEPOTCOUNT
,
3312 &This
->bigBlockDepotCount
);
3314 StorageUtl_ReadDWord(
3316 OFFSET_ROOTSTARTBLOCK
,
3317 &This
->rootStartBlock
);
3319 StorageUtl_ReadDWord(
3321 OFFSET_SBDEPOTSTART
,
3322 &This
->smallBlockDepotStart
);
3324 StorageUtl_ReadDWord(
3326 OFFSET_EXTBBDEPOTSTART
,
3327 &This
->extBigBlockDepotStart
);
3329 StorageUtl_ReadDWord(
3331 OFFSET_EXTBBDEPOTCOUNT
,
3332 &This
->extBigBlockDepotCount
);
3334 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3336 StorageUtl_ReadDWord(
3338 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3339 &(This
->bigBlockDepotStart
[index
]));
3343 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3345 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3346 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3349 * Right now, the code is making some assumptions about the size of the
3350 * blocks, just make sure they are what we're expecting.
3352 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3353 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3355 WARN("Broken OLE storage file\n");
3356 hr
= STG_E_INVALIDHEADER
;
3365 /******************************************************************************
3366 * Storage32Impl_SaveFileHeader
3368 * This method will save to the file the header, i.e. big block -1.
3370 static void StorageImpl_SaveFileHeader(
3373 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3378 * Get a pointer to the big block of data containing the header.
3380 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3383 * If the block read failed, the file is probably new.
3388 * Initialize for all unknown fields.
3390 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3393 * Initialize the magic number.
3395 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3398 * And a bunch of things we don't know what they mean
3400 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3401 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3402 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3403 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3407 * Write the information to the header.
3409 StorageUtl_WriteWord(
3411 OFFSET_BIGBLOCKSIZEBITS
,
3412 This
->bigBlockSizeBits
);
3414 StorageUtl_WriteWord(
3416 OFFSET_SMALLBLOCKSIZEBITS
,
3417 This
->smallBlockSizeBits
);
3419 StorageUtl_WriteDWord(
3421 OFFSET_BBDEPOTCOUNT
,
3422 This
->bigBlockDepotCount
);
3424 StorageUtl_WriteDWord(
3426 OFFSET_ROOTSTARTBLOCK
,
3427 This
->rootStartBlock
);
3429 StorageUtl_WriteDWord(
3431 OFFSET_SBDEPOTSTART
,
3432 This
->smallBlockDepotStart
);
3434 StorageUtl_WriteDWord(
3436 OFFSET_SBDEPOTCOUNT
,
3437 This
->smallBlockDepotChain
?
3438 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3440 StorageUtl_WriteDWord(
3442 OFFSET_EXTBBDEPOTSTART
,
3443 This
->extBigBlockDepotStart
);
3445 StorageUtl_WriteDWord(
3447 OFFSET_EXTBBDEPOTCOUNT
,
3448 This
->extBigBlockDepotCount
);
3450 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3452 StorageUtl_WriteDWord(
3454 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3455 (This
->bigBlockDepotStart
[index
]));
3459 * Write the big block back to the file.
3461 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3464 /******************************************************************************
3465 * StorageImpl_ReadRawDirEntry
3467 * This method will read the raw data from a directory entry in the file.
3469 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3471 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3473 ULARGE_INTEGER offset
;
3477 offset
.u
.HighPart
= 0;
3478 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3480 hr
= BlockChainStream_ReadAt(
3481 This
->rootBlockChain
,
3490 /******************************************************************************
3491 * StorageImpl_WriteRawDirEntry
3493 * This method will write the raw data from a directory entry in the file.
3495 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3497 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3499 ULARGE_INTEGER offset
;
3503 offset
.u
.HighPart
= 0;
3504 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3506 hr
= BlockChainStream_WriteAt(
3507 This
->rootBlockChain
,
3516 /******************************************************************************
3519 * Update raw directory entry data from the fields in newData.
3521 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3523 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3525 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3528 buffer
+ OFFSET_PS_NAME
,
3530 DIRENTRY_NAME_BUFFER_LEN
);
3532 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3534 StorageUtl_WriteWord(
3536 OFFSET_PS_NAMELENGTH
,
3537 newData
->sizeOfNameString
);
3539 StorageUtl_WriteDWord(
3541 OFFSET_PS_LEFTCHILD
,
3542 newData
->leftChild
);
3544 StorageUtl_WriteDWord(
3546 OFFSET_PS_RIGHTCHILD
,
3547 newData
->rightChild
);
3549 StorageUtl_WriteDWord(
3552 newData
->dirRootEntry
);
3554 StorageUtl_WriteGUID(
3559 StorageUtl_WriteDWord(
3562 newData
->ctime
.dwLowDateTime
);
3564 StorageUtl_WriteDWord(
3566 OFFSET_PS_CTIMEHIGH
,
3567 newData
->ctime
.dwHighDateTime
);
3569 StorageUtl_WriteDWord(
3572 newData
->mtime
.dwLowDateTime
);
3574 StorageUtl_WriteDWord(
3576 OFFSET_PS_MTIMEHIGH
,
3577 newData
->ctime
.dwHighDateTime
);
3579 StorageUtl_WriteDWord(
3581 OFFSET_PS_STARTBLOCK
,
3582 newData
->startingBlock
);
3584 StorageUtl_WriteDWord(
3587 newData
->size
.u
.LowPart
);
3590 /******************************************************************************
3591 * Storage32Impl_ReadDirEntry
3593 * This method will read the specified directory entry.
3595 HRESULT
StorageImpl_ReadDirEntry(
3600 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3603 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3605 if (SUCCEEDED(readRes
))
3607 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3610 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3611 DIRENTRY_NAME_BUFFER_LEN
);
3612 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3614 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3616 StorageUtl_ReadWord(
3618 OFFSET_PS_NAMELENGTH
,
3619 &buffer
->sizeOfNameString
);
3621 StorageUtl_ReadDWord(
3623 OFFSET_PS_LEFTCHILD
,
3624 &buffer
->leftChild
);
3626 StorageUtl_ReadDWord(
3628 OFFSET_PS_RIGHTCHILD
,
3629 &buffer
->rightChild
);
3631 StorageUtl_ReadDWord(
3634 &buffer
->dirRootEntry
);
3636 StorageUtl_ReadGUID(
3641 StorageUtl_ReadDWord(
3644 &buffer
->ctime
.dwLowDateTime
);
3646 StorageUtl_ReadDWord(
3648 OFFSET_PS_CTIMEHIGH
,
3649 &buffer
->ctime
.dwHighDateTime
);
3651 StorageUtl_ReadDWord(
3654 &buffer
->mtime
.dwLowDateTime
);
3656 StorageUtl_ReadDWord(
3658 OFFSET_PS_MTIMEHIGH
,
3659 &buffer
->mtime
.dwHighDateTime
);
3661 StorageUtl_ReadDWord(
3663 OFFSET_PS_STARTBLOCK
,
3664 &buffer
->startingBlock
);
3666 StorageUtl_ReadDWord(
3669 &buffer
->size
.u
.LowPart
);
3671 buffer
->size
.u
.HighPart
= 0;
3677 /*********************************************************************
3678 * Write the specified directory entry to the file
3680 HRESULT
StorageImpl_WriteDirEntry(
3683 const DirEntry
* buffer
)
3685 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3688 UpdateRawDirEntry(currentEntry
, buffer
);
3690 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3694 static BOOL
StorageImpl_ReadBigBlock(
3699 ULARGE_INTEGER ulOffset
;
3702 ulOffset
.u
.HighPart
= 0;
3703 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3705 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3706 return (read
== This
->bigBlockSize
);
3709 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3715 ULARGE_INTEGER ulOffset
;
3719 ulOffset
.u
.HighPart
= 0;
3720 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3721 ulOffset
.u
.LowPart
+= offset
;
3723 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3724 *value
= lendian32toh(tmp
);
3725 return (read
== sizeof(DWORD
));
3728 static BOOL
StorageImpl_WriteBigBlock(
3733 ULARGE_INTEGER ulOffset
;
3736 ulOffset
.u
.HighPart
= 0;
3737 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3739 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3740 return (wrote
== This
->bigBlockSize
);
3743 static BOOL
StorageImpl_WriteDWordToBigBlock(
3749 ULARGE_INTEGER ulOffset
;
3752 ulOffset
.u
.HighPart
= 0;
3753 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3754 ulOffset
.u
.LowPart
+= offset
;
3756 value
= htole32(value
);
3757 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3758 return (wrote
== sizeof(DWORD
));
3761 /******************************************************************************
3762 * Storage32Impl_SmallBlocksToBigBlocks
3764 * This method will convert a small block chain to a big block chain.
3765 * The small block chain will be destroyed.
3767 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3769 SmallBlockChainStream
** ppsbChain
)
3771 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3772 ULARGE_INTEGER size
, offset
;
3773 ULONG cbRead
, cbWritten
;
3774 ULARGE_INTEGER cbTotalRead
;
3775 DirRef streamEntryRef
;
3776 HRESULT resWrite
= S_OK
;
3778 DirEntry streamEntry
;
3780 BlockChainStream
*bbTempChain
= NULL
;
3781 BlockChainStream
*bigBlockChain
= NULL
;
3784 * Create a temporary big block chain that doesn't have
3785 * an associated directory entry. This temporary chain will be
3786 * used to copy data from small blocks to big blocks.
3788 bbTempChain
= BlockChainStream_Construct(This
,
3791 if(!bbTempChain
) return NULL
;
3793 * Grow the big block chain.
3795 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3796 BlockChainStream_SetSize(bbTempChain
, size
);
3799 * Copy the contents of the small block chain to the big block chain
3800 * by small block size increments.
3802 offset
.u
.LowPart
= 0;
3803 offset
.u
.HighPart
= 0;
3804 cbTotalRead
.QuadPart
= 0;
3806 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3809 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3811 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3814 if (FAILED(resRead
))
3819 cbTotalRead
.QuadPart
+= cbRead
;
3821 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3827 if (FAILED(resWrite
))
3830 offset
.u
.LowPart
+= cbRead
;
3832 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3833 HeapFree(GetProcessHeap(),0,buffer
);
3835 size
.u
.HighPart
= 0;
3838 if (FAILED(resRead
) || FAILED(resWrite
))
3840 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3841 BlockChainStream_SetSize(bbTempChain
, size
);
3842 BlockChainStream_Destroy(bbTempChain
);
3847 * Destroy the small block chain.
3849 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3850 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3851 SmallBlockChainStream_Destroy(*ppsbChain
);
3855 * Change the directory entry. This chain is now a big block chain
3856 * and it doesn't reside in the small blocks chain anymore.
3858 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3860 streamEntry
.startingBlock
= bbHeadOfChain
;
3862 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3865 * Destroy the temporary entryless big block chain.
3866 * Create a new big block chain associated with this entry.
3868 BlockChainStream_Destroy(bbTempChain
);
3869 bigBlockChain
= BlockChainStream_Construct(This
,
3873 return bigBlockChain
;
3876 /******************************************************************************
3877 * Storage32Impl_BigBlocksToSmallBlocks
3879 * This method will convert a big block chain to a small block chain.
3880 * The big block chain will be destroyed on success.
3882 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3884 BlockChainStream
** ppbbChain
)
3886 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3887 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3888 DirRef streamEntryRef
;
3889 HRESULT resWrite
= S_OK
, resRead
;
3890 DirEntry streamEntry
;
3892 SmallBlockChainStream
* sbTempChain
;
3894 TRACE("%p %p\n", This
, ppbbChain
);
3896 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3902 size
= BlockChainStream_GetSize(*ppbbChain
);
3903 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3905 offset
.u
.HighPart
= 0;
3906 offset
.u
.LowPart
= 0;
3907 cbTotalRead
.QuadPart
= 0;
3908 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3911 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3912 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3920 cbTotalRead
.QuadPart
+= cbRead
;
3922 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3923 cbRead
, buffer
, &cbWritten
);
3925 if(FAILED(resWrite
))
3928 offset
.u
.LowPart
+= cbRead
;
3930 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3931 HeapFree(GetProcessHeap(), 0, buffer
);
3933 size
.u
.HighPart
= 0;
3936 if(FAILED(resRead
) || FAILED(resWrite
))
3938 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3939 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3940 SmallBlockChainStream_Destroy(sbTempChain
);
3944 /* destroy the original big block chain */
3945 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3946 BlockChainStream_SetSize(*ppbbChain
, size
);
3947 BlockChainStream_Destroy(*ppbbChain
);
3950 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3951 streamEntry
.startingBlock
= sbHeadOfChain
;
3952 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3954 SmallBlockChainStream_Destroy(sbTempChain
);
3955 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3958 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3961 DirEntry parentData
, snapshotData
;
3963 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3964 0, (IStorage
**)snapshot
);
3968 hr
= StorageBaseImpl_ReadDirEntry(original
,
3969 original
->storageDirEntry
, &parentData
);
3972 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3973 (*snapshot
)->storageDirEntry
, &snapshotData
);
3977 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3978 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3979 snapshotData
.stgType
= parentData
.stgType
;
3980 snapshotData
.clsid
= parentData
.clsid
;
3981 snapshotData
.ctime
= parentData
.ctime
;
3982 snapshotData
.mtime
= parentData
.mtime
;
3983 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3984 (*snapshot
)->storageDirEntry
, &snapshotData
);
3988 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
3989 (IStorage
*)(*snapshot
));
3991 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
3997 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
3999 DWORD grfCommitFlags
) /* [in] */
4001 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4003 DirEntry data
, tempStorageData
, snapshotRootData
;
4004 DirRef tempStorageEntry
, oldDirRoot
;
4005 StorageInternalImpl
*tempStorage
;
4007 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4009 /* Cannot commit a read-only transacted storage */
4010 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4011 return STG_E_ACCESSDENIED
;
4013 /* To prevent data loss, we create the new structure in the file before we
4014 * delete the old one, so that in case of errors the old data is intact. We
4015 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4016 * needed in the rare situation where we have just enough free disk space to
4017 * overwrite the existing data. */
4019 /* Create an orphaned storage in the parent for the new directory structure. */
4020 memset(&data
, 0, sizeof(data
));
4022 data
.sizeOfNameString
= 1;
4023 data
.stgType
= STGTY_STORAGE
;
4024 data
.leftChild
= DIRENTRY_NULL
;
4025 data
.rightChild
= DIRENTRY_NULL
;
4026 data
.dirRootEntry
= DIRENTRY_NULL
;
4027 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4029 if (FAILED(hr
)) return hr
;
4031 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4032 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4035 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4036 (IStorage
*)tempStorage
);
4038 list_init(&tempStorage
->ParentListEntry
);
4040 IStorage_Release((IStorage
*) tempStorage
);
4047 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4051 /* Update the storage to use the new data in one step. */
4052 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4053 This
->transactedParent
->storageDirEntry
, &data
);
4057 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4058 tempStorageEntry
, &tempStorageData
);
4063 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4064 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4069 oldDirRoot
= data
.dirRootEntry
;
4070 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4071 data
.clsid
= snapshotRootData
.clsid
;
4072 data
.ctime
= snapshotRootData
.ctime
;
4073 data
.mtime
= snapshotRootData
.mtime
;
4075 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4076 This
->transactedParent
->storageDirEntry
, &data
);
4081 /* Destroy the old now-orphaned data. */
4082 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4083 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4087 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4093 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4096 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4097 StorageBaseImpl
*newSnapshot
;
4100 TRACE("(%p)\n", iface
);
4102 /* Create a new copy of the parent data. */
4103 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4104 if (FAILED(hr
)) return hr
;
4106 /* Destroy the open objects. */
4107 StorageBaseImpl_DeleteAll(&This
->base
);
4109 /* Replace our current snapshot. */
4110 IStorage_Release((IStorage
*)This
->snapshot
);
4111 This
->snapshot
= newSnapshot
;
4116 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4118 if (!This
->reverted
)
4120 TRACE("Storage invalidated (stg=%p)\n", This
);
4124 StorageBaseImpl_DeleteAll(This
);
4128 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4130 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4132 TransactedSnapshotImpl_Invalidate(iface
);
4134 IStorage_Release((IStorage
*)This
->transactedParent
);
4136 IStorage_Release((IStorage
*)This
->snapshot
);
4138 HeapFree(GetProcessHeap(), 0, This
);
4141 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4142 const DirEntry
*newData
, DirRef
*index
)
4144 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4146 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4150 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4151 DirRef index
, const DirEntry
*data
)
4153 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4155 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4159 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4160 DirRef index
, DirEntry
*data
)
4162 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4164 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4168 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4171 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4173 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4177 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4178 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4180 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4182 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4183 index
, offset
, size
, buffer
, bytesRead
);
4186 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4187 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4189 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4191 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4192 index
, offset
, size
, buffer
, bytesWritten
);
4195 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4196 DirRef index
, ULARGE_INTEGER newsize
)
4198 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4200 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4204 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4206 StorageBaseImpl_QueryInterface
,
4207 StorageBaseImpl_AddRef
,
4208 StorageBaseImpl_Release
,
4209 StorageBaseImpl_CreateStream
,
4210 StorageBaseImpl_OpenStream
,
4211 StorageBaseImpl_CreateStorage
,
4212 StorageBaseImpl_OpenStorage
,
4213 StorageBaseImpl_CopyTo
,
4214 StorageBaseImpl_MoveElementTo
,
4215 TransactedSnapshotImpl_Commit
,
4216 TransactedSnapshotImpl_Revert
,
4217 StorageBaseImpl_EnumElements
,
4218 StorageBaseImpl_DestroyElement
,
4219 StorageBaseImpl_RenameElement
,
4220 StorageBaseImpl_SetElementTimes
,
4221 StorageBaseImpl_SetClass
,
4222 StorageBaseImpl_SetStateBits
,
4223 StorageBaseImpl_Stat
4226 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4228 TransactedSnapshotImpl_Destroy
,
4229 TransactedSnapshotImpl_Invalidate
,
4230 TransactedSnapshotImpl_CreateDirEntry
,
4231 TransactedSnapshotImpl_WriteDirEntry
,
4232 TransactedSnapshotImpl_ReadDirEntry
,
4233 TransactedSnapshotImpl_DestroyDirEntry
,
4234 TransactedSnapshotImpl_StreamReadAt
,
4235 TransactedSnapshotImpl_StreamWriteAt
,
4236 TransactedSnapshotImpl_StreamSetSize
4239 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4240 TransactedSnapshotImpl
** result
)
4244 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4247 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4249 /* This is OK because the property set storage functions use the IStorage functions. */
4250 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4252 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4254 list_init(&(*result
)->base
.strmHead
);
4256 list_init(&(*result
)->base
.storageHead
);
4258 (*result
)->base
.ref
= 1;
4260 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4262 (*result
)->base
.filename
= parentStorage
->filename
;
4264 /* Create a new temporary storage to act as the snapshot */
4265 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4269 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4271 /* parentStorage already has 1 reference, which we take over here. */
4272 (*result
)->transactedParent
= parentStorage
;
4274 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4277 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4282 return E_OUTOFMEMORY
;
4285 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4286 StorageBaseImpl
** result
)
4290 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4292 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4295 return TransactedSnapshotImpl_Construct(parentStorage
,
4296 (TransactedSnapshotImpl
**)result
);
4299 static HRESULT
Storage_Construct(
4306 StorageBaseImpl
** result
)
4308 StorageImpl
*newStorage
;
4309 StorageBaseImpl
*newTransactedStorage
;
4312 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4313 if (FAILED(hr
)) goto end
;
4315 if (openFlags
& STGM_TRANSACTED
)
4317 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4319 IStorage_Release((IStorage
*)newStorage
);
4321 *result
= newTransactedStorage
;
4324 *result
= &newStorage
->base
;
4330 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4332 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4334 if (!This
->base
.reverted
)
4336 TRACE("Storage invalidated (stg=%p)\n", This
);
4338 This
->base
.reverted
= 1;
4340 This
->parentStorage
= NULL
;
4342 StorageBaseImpl_DeleteAll(&This
->base
);
4344 list_remove(&This
->ParentListEntry
);
4348 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4350 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4352 StorageInternalImpl_Invalidate(&This
->base
);
4354 HeapFree(GetProcessHeap(), 0, This
);
4357 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4358 const DirEntry
*newData
, DirRef
*index
)
4360 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4362 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4366 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4367 DirRef index
, const DirEntry
*data
)
4369 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4371 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4375 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4376 DirRef index
, DirEntry
*data
)
4378 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4380 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4384 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4387 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4389 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4393 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4394 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4396 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4398 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4399 index
, offset
, size
, buffer
, bytesRead
);
4402 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4403 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4405 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4407 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4408 index
, offset
, size
, buffer
, bytesWritten
);
4411 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4412 DirRef index
, ULARGE_INTEGER newsize
)
4414 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4416 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4420 /******************************************************************************
4422 ** Storage32InternalImpl_Commit
4425 static HRESULT WINAPI
StorageInternalImpl_Commit(
4427 DWORD grfCommitFlags
) /* [in] */
4429 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4433 /******************************************************************************
4435 ** Storage32InternalImpl_Revert
4438 static HRESULT WINAPI
StorageInternalImpl_Revert(
4441 FIXME("(%p): stub\n", iface
);
4445 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4447 IStorage_Release((IStorage
*)This
->parentStorage
);
4448 HeapFree(GetProcessHeap(), 0, This
);
4451 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4452 IEnumSTATSTG
* iface
,
4456 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4459 return E_INVALIDARG
;
4463 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4464 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4467 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4471 return E_NOINTERFACE
;
4474 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4475 IEnumSTATSTG
* iface
)
4477 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4478 return InterlockedIncrement(&This
->ref
);
4481 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4482 IEnumSTATSTG
* iface
)
4484 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4488 newRef
= InterlockedDecrement(&This
->ref
);
4492 IEnumSTATSTGImpl_Destroy(This
);
4498 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4499 IEnumSTATSTGImpl
* This
,
4502 DirRef result
= DIRENTRY_NULL
;
4506 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4508 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4509 This
->parentStorage
->storageDirEntry
, &entry
);
4510 searchNode
= entry
.dirRootEntry
;
4512 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4514 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4518 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4522 searchNode
= entry
.rightChild
;
4526 result
= searchNode
;
4527 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4528 searchNode
= entry
.leftChild
;
4536 if (result
!= DIRENTRY_NULL
)
4537 memcpy(This
->name
, result_name
, sizeof(result_name
));
4543 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4544 IEnumSTATSTG
* iface
,
4547 ULONG
* pceltFetched
)
4549 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4551 DirEntry currentEntry
;
4552 STATSTG
* currentReturnStruct
= rgelt
;
4553 ULONG objectFetched
= 0;
4554 DirRef currentSearchNode
;
4557 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4558 return E_INVALIDARG
;
4560 if (This
->parentStorage
->reverted
)
4561 return STG_E_REVERTED
;
4564 * To avoid the special case, get another pointer to a ULONG value if
4565 * the caller didn't supply one.
4567 if (pceltFetched
==0)
4568 pceltFetched
= &objectFetched
;
4571 * Start the iteration, we will iterate until we hit the end of the
4572 * linked list or until we hit the number of items to iterate through
4576 while ( *pceltFetched
< celt
)
4578 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4580 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4584 * Read the entry from the storage.
4586 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4591 * Copy the information to the return buffer.
4593 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4594 currentReturnStruct
,
4599 * Step to the next item in the iteration
4602 currentReturnStruct
++;
4605 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4612 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4613 IEnumSTATSTG
* iface
,
4616 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4618 ULONG objectFetched
= 0;
4619 DirRef currentSearchNode
;
4622 if (This
->parentStorage
->reverted
)
4623 return STG_E_REVERTED
;
4625 while ( (objectFetched
< celt
) )
4627 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4629 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4635 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4641 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4642 IEnumSTATSTG
* iface
)
4644 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4646 if (This
->parentStorage
->reverted
)
4647 return STG_E_REVERTED
;
4654 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4655 IEnumSTATSTG
* iface
,
4656 IEnumSTATSTG
** ppenum
)
4658 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4660 IEnumSTATSTGImpl
* newClone
;
4662 if (This
->parentStorage
->reverted
)
4663 return STG_E_REVERTED
;
4666 * Perform a sanity check on the parameters.
4669 return E_INVALIDARG
;
4671 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4672 This
->storageDirEntry
);
4676 * The new clone enumeration must point to the same current node as
4679 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4681 *ppenum
= (IEnumSTATSTG
*)newClone
;
4684 * Don't forget to nail down a reference to the clone before
4687 IEnumSTATSTGImpl_AddRef(*ppenum
);
4693 * Virtual function table for the IEnumSTATSTGImpl class.
4695 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4697 IEnumSTATSTGImpl_QueryInterface
,
4698 IEnumSTATSTGImpl_AddRef
,
4699 IEnumSTATSTGImpl_Release
,
4700 IEnumSTATSTGImpl_Next
,
4701 IEnumSTATSTGImpl_Skip
,
4702 IEnumSTATSTGImpl_Reset
,
4703 IEnumSTATSTGImpl_Clone
4706 /******************************************************************************
4707 ** IEnumSTATSTGImpl implementation
4710 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4711 StorageBaseImpl
* parentStorage
,
4712 DirRef storageDirEntry
)
4714 IEnumSTATSTGImpl
* newEnumeration
;
4716 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4718 if (newEnumeration
!=0)
4721 * Set-up the virtual function table and reference count.
4723 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4724 newEnumeration
->ref
= 0;
4727 * We want to nail-down the reference to the storage in case the
4728 * enumeration out-lives the storage in the client application.
4730 newEnumeration
->parentStorage
= parentStorage
;
4731 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4733 newEnumeration
->storageDirEntry
= storageDirEntry
;
4736 * Make sure the current node of the iterator is the first one.
4738 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4741 return newEnumeration
;
4745 * Virtual function table for the Storage32InternalImpl class.
4747 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4749 StorageBaseImpl_QueryInterface
,
4750 StorageBaseImpl_AddRef
,
4751 StorageBaseImpl_Release
,
4752 StorageBaseImpl_CreateStream
,
4753 StorageBaseImpl_OpenStream
,
4754 StorageBaseImpl_CreateStorage
,
4755 StorageBaseImpl_OpenStorage
,
4756 StorageBaseImpl_CopyTo
,
4757 StorageBaseImpl_MoveElementTo
,
4758 StorageInternalImpl_Commit
,
4759 StorageInternalImpl_Revert
,
4760 StorageBaseImpl_EnumElements
,
4761 StorageBaseImpl_DestroyElement
,
4762 StorageBaseImpl_RenameElement
,
4763 StorageBaseImpl_SetElementTimes
,
4764 StorageBaseImpl_SetClass
,
4765 StorageBaseImpl_SetStateBits
,
4766 StorageBaseImpl_Stat
4769 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4771 StorageInternalImpl_Destroy
,
4772 StorageInternalImpl_Invalidate
,
4773 StorageInternalImpl_CreateDirEntry
,
4774 StorageInternalImpl_WriteDirEntry
,
4775 StorageInternalImpl_ReadDirEntry
,
4776 StorageInternalImpl_DestroyDirEntry
,
4777 StorageInternalImpl_StreamReadAt
,
4778 StorageInternalImpl_StreamWriteAt
,
4779 StorageInternalImpl_StreamSetSize
4782 /******************************************************************************
4783 ** Storage32InternalImpl implementation
4786 static StorageInternalImpl
* StorageInternalImpl_Construct(
4787 StorageBaseImpl
* parentStorage
,
4789 DirRef storageDirEntry
)
4791 StorageInternalImpl
* newStorage
;
4793 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4797 list_init(&newStorage
->base
.strmHead
);
4799 list_init(&newStorage
->base
.storageHead
);
4802 * Initialize the virtual function table.
4804 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4805 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4806 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4808 newStorage
->base
.reverted
= 0;
4810 newStorage
->base
.ref
= 1;
4812 newStorage
->parentStorage
= parentStorage
;
4815 * Keep a reference to the directory entry of this storage
4817 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4819 newStorage
->base
.create
= 0;
4827 /******************************************************************************
4828 ** StorageUtl implementation
4831 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4835 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4836 *value
= lendian16toh(tmp
);
4839 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4841 value
= htole16(value
);
4842 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4845 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4849 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4850 *value
= lendian32toh(tmp
);
4853 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4855 value
= htole32(value
);
4856 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4859 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4860 ULARGE_INTEGER
* value
)
4862 #ifdef WORDS_BIGENDIAN
4865 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4866 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4867 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4869 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4873 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4874 const ULARGE_INTEGER
*value
)
4876 #ifdef WORDS_BIGENDIAN
4879 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4880 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4881 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4883 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4887 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4889 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4890 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4891 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4893 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4896 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4898 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4899 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4900 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4902 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4905 void StorageUtl_CopyDirEntryToSTATSTG(
4906 StorageBaseImpl
* storage
,
4907 STATSTG
* destination
,
4908 const DirEntry
* source
,
4913 if (source
->stgType
== STGTY_ROOT
)
4915 /* replace the name of root entry (often "Root Entry") by the file name */
4916 entryName
= storage
->filename
;
4920 entryName
= source
->name
;
4924 * The copy of the string occurs only when the flag is not set
4926 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4927 (entryName
== NULL
) ||
4928 (entryName
[0] == 0) )
4930 destination
->pwcsName
= 0;
4934 destination
->pwcsName
=
4935 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4937 strcpyW(destination
->pwcsName
, entryName
);
4940 switch (source
->stgType
)
4944 destination
->type
= STGTY_STORAGE
;
4947 destination
->type
= STGTY_STREAM
;
4950 destination
->type
= STGTY_STREAM
;
4954 destination
->cbSize
= source
->size
;
4956 currentReturnStruct->mtime = {0}; TODO
4957 currentReturnStruct->ctime = {0};
4958 currentReturnStruct->atime = {0};
4960 destination
->grfMode
= 0;
4961 destination
->grfLocksSupported
= 0;
4962 destination
->clsid
= source
->clsid
;
4963 destination
->grfStateBits
= 0;
4964 destination
->reserved
= 0;
4967 /******************************************************************************
4968 ** BlockChainStream implementation
4971 BlockChainStream
* BlockChainStream_Construct(
4972 StorageImpl
* parentStorage
,
4973 ULONG
* headOfStreamPlaceHolder
,
4976 BlockChainStream
* newStream
;
4979 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4981 newStream
->parentStorage
= parentStorage
;
4982 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4983 newStream
->ownerDirEntry
= dirEntry
;
4984 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4985 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4986 newStream
->numBlocks
= 0;
4988 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4990 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4992 newStream
->numBlocks
++;
4993 newStream
->tailIndex
= blockIndex
;
4995 if(FAILED(StorageImpl_GetNextBlockInChain(
5000 HeapFree(GetProcessHeap(), 0, newStream
);
5008 void BlockChainStream_Destroy(BlockChainStream
* This
)
5010 HeapFree(GetProcessHeap(), 0, This
);
5013 /******************************************************************************
5014 * BlockChainStream_GetHeadOfChain
5016 * Returns the head of this stream chain.
5017 * Some special chains don't have directory entries, their heads are kept in
5018 * This->headOfStreamPlaceHolder.
5021 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5023 DirEntry chainEntry
;
5026 if (This
->headOfStreamPlaceHolder
!= 0)
5027 return *(This
->headOfStreamPlaceHolder
);
5029 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5031 hr
= StorageImpl_ReadDirEntry(
5032 This
->parentStorage
,
5033 This
->ownerDirEntry
,
5038 return chainEntry
.startingBlock
;
5042 return BLOCK_END_OF_CHAIN
;
5045 /******************************************************************************
5046 * BlockChainStream_GetCount
5048 * Returns the number of blocks that comprises this chain.
5049 * This is not the size of the stream as the last block may not be full!
5052 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5057 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5059 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5063 if(FAILED(StorageImpl_GetNextBlockInChain(
5064 This
->parentStorage
,
5073 /******************************************************************************
5074 * BlockChainStream_ReadAt
5076 * Reads a specified number of bytes from this chain at the specified offset.
5077 * bytesRead may be NULL.
5078 * Failure will be returned if the specified number of bytes has not been read.
5080 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5081 ULARGE_INTEGER offset
,
5086 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5087 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5088 ULONG bytesToReadInBuffer
;
5092 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5095 * Find the first block in the stream that contains part of the buffer.
5097 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5098 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5099 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5101 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5102 This
->lastBlockNoInSequence
= blockNoInSequence
;
5106 ULONG temp
= blockNoInSequence
;
5108 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5109 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5110 This
->lastBlockNoInSequence
= temp
;
5113 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5115 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5116 return STG_E_DOCFILECORRUPT
;
5117 blockNoInSequence
--;
5120 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5121 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5123 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5126 * Start reading the buffer.
5129 bufferWalker
= buffer
;
5131 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5133 ULARGE_INTEGER ulOffset
;
5136 * Calculate how many bytes we can copy from this big block.
5138 bytesToReadInBuffer
=
5139 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5141 TRACE("block %i\n",blockIndex
);
5142 ulOffset
.u
.HighPart
= 0;
5143 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5146 StorageImpl_ReadAt(This
->parentStorage
,
5149 bytesToReadInBuffer
,
5152 * Step to the next big block.
5154 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5155 return STG_E_DOCFILECORRUPT
;
5157 bufferWalker
+= bytesReadAt
;
5158 size
-= bytesReadAt
;
5159 *bytesRead
+= bytesReadAt
;
5160 offsetInBlock
= 0; /* There is no offset on the next block */
5162 if (bytesToReadInBuffer
!= bytesReadAt
)
5166 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5169 /******************************************************************************
5170 * BlockChainStream_WriteAt
5172 * Writes the specified number of bytes to this chain at the specified offset.
5173 * Will fail if not all specified number of bytes have been written.
5175 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5176 ULARGE_INTEGER offset
,
5179 ULONG
* bytesWritten
)
5181 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5182 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5185 const BYTE
* bufferWalker
;
5188 * Find the first block in the stream that contains part of the buffer.
5190 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5191 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5192 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5194 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5195 This
->lastBlockNoInSequence
= blockNoInSequence
;
5199 ULONG temp
= blockNoInSequence
;
5201 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5202 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5203 This
->lastBlockNoInSequence
= temp
;
5206 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5208 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5210 return STG_E_DOCFILECORRUPT
;
5211 blockNoInSequence
--;
5214 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5216 /* BlockChainStream_SetSize should have already been called to ensure we have
5217 * enough blocks in the chain to write into */
5218 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5220 ERR("not enough blocks in chain to write data\n");
5221 return STG_E_DOCFILECORRUPT
;
5225 bufferWalker
= buffer
;
5227 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5229 ULARGE_INTEGER ulOffset
;
5230 DWORD bytesWrittenAt
;
5232 * Calculate how many bytes we can copy from this big block.
5235 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5237 TRACE("block %i\n",blockIndex
);
5238 ulOffset
.u
.HighPart
= 0;
5239 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5242 StorageImpl_WriteAt(This
->parentStorage
,
5249 * Step to the next big block.
5251 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5253 return STG_E_DOCFILECORRUPT
;
5255 bufferWalker
+= bytesWrittenAt
;
5256 size
-= bytesWrittenAt
;
5257 *bytesWritten
+= bytesWrittenAt
;
5258 offsetInBlock
= 0; /* There is no offset on the next block */
5260 if (bytesWrittenAt
!= bytesToWrite
)
5264 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5267 /******************************************************************************
5268 * BlockChainStream_Shrink
5270 * Shrinks this chain in the big block depot.
5272 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5273 ULARGE_INTEGER newSize
)
5275 ULONG blockIndex
, extraBlock
;
5280 * Reset the last accessed block cache.
5282 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5283 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5286 * Figure out how many blocks are needed to contain the new size
5288 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5290 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5293 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5296 * Go to the new end of chain
5298 while (count
< numBlocks
)
5300 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5306 /* Get the next block before marking the new end */
5307 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5311 /* Mark the new end of chain */
5312 StorageImpl_SetNextBlockInChain(
5313 This
->parentStorage
,
5315 BLOCK_END_OF_CHAIN
);
5317 This
->tailIndex
= blockIndex
;
5318 This
->numBlocks
= numBlocks
;
5321 * Mark the extra blocks as free
5323 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5325 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5328 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5329 extraBlock
= blockIndex
;
5335 /******************************************************************************
5336 * BlockChainStream_Enlarge
5338 * Grows this chain in the big block depot.
5340 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5341 ULARGE_INTEGER newSize
)
5343 ULONG blockIndex
, currentBlock
;
5345 ULONG oldNumBlocks
= 0;
5347 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5350 * Empty chain. Create the head.
5352 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5354 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5355 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5357 BLOCK_END_OF_CHAIN
);
5359 if (This
->headOfStreamPlaceHolder
!= 0)
5361 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5365 DirEntry chainEntry
;
5366 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5368 StorageImpl_ReadDirEntry(
5369 This
->parentStorage
,
5370 This
->ownerDirEntry
,
5373 chainEntry
.startingBlock
= blockIndex
;
5375 StorageImpl_WriteDirEntry(
5376 This
->parentStorage
,
5377 This
->ownerDirEntry
,
5381 This
->tailIndex
= blockIndex
;
5382 This
->numBlocks
= 1;
5386 * Figure out how many blocks are needed to contain this stream
5388 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5390 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5394 * Go to the current end of chain
5396 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5398 currentBlock
= blockIndex
;
5400 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5403 currentBlock
= blockIndex
;
5405 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5410 This
->tailIndex
= currentBlock
;
5413 currentBlock
= This
->tailIndex
;
5414 oldNumBlocks
= This
->numBlocks
;
5417 * Add new blocks to the chain
5419 if (oldNumBlocks
< newNumBlocks
)
5421 while (oldNumBlocks
< newNumBlocks
)
5423 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5425 StorageImpl_SetNextBlockInChain(
5426 This
->parentStorage
,
5430 StorageImpl_SetNextBlockInChain(
5431 This
->parentStorage
,
5433 BLOCK_END_OF_CHAIN
);
5435 currentBlock
= blockIndex
;
5439 This
->tailIndex
= blockIndex
;
5440 This
->numBlocks
= newNumBlocks
;
5446 /******************************************************************************
5447 * BlockChainStream_SetSize
5449 * Sets the size of this stream. The big block depot will be updated.
5450 * The file will grow if we grow the chain.
5452 * TODO: Free the actual blocks in the file when we shrink the chain.
5453 * Currently, the blocks are still in the file. So the file size
5454 * doesn't shrink even if we shrink streams.
5456 BOOL
BlockChainStream_SetSize(
5457 BlockChainStream
* This
,
5458 ULARGE_INTEGER newSize
)
5460 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5462 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5465 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5467 BlockChainStream_Shrink(This
, newSize
);
5471 BlockChainStream_Enlarge(This
, newSize
);
5477 /******************************************************************************
5478 * BlockChainStream_GetSize
5480 * Returns the size of this chain.
5481 * Will return the block count if this chain doesn't have a directory entry.
5483 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5485 DirEntry chainEntry
;
5487 if(This
->headOfStreamPlaceHolder
== NULL
)
5490 * This chain has a directory entry so use the size value from there.
5492 StorageImpl_ReadDirEntry(
5493 This
->parentStorage
,
5494 This
->ownerDirEntry
,
5497 return chainEntry
.size
;
5502 * this chain is a chain that does not have a directory entry, figure out the
5503 * size by making the product number of used blocks times the
5506 ULARGE_INTEGER result
;
5507 result
.u
.HighPart
= 0;
5510 BlockChainStream_GetCount(This
) *
5511 This
->parentStorage
->bigBlockSize
;
5517 /******************************************************************************
5518 ** SmallBlockChainStream implementation
5521 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5522 StorageImpl
* parentStorage
,
5523 ULONG
* headOfStreamPlaceHolder
,
5526 SmallBlockChainStream
* newStream
;
5528 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5530 newStream
->parentStorage
= parentStorage
;
5531 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5532 newStream
->ownerDirEntry
= dirEntry
;
5537 void SmallBlockChainStream_Destroy(
5538 SmallBlockChainStream
* This
)
5540 HeapFree(GetProcessHeap(), 0, This
);
5543 /******************************************************************************
5544 * SmallBlockChainStream_GetHeadOfChain
5546 * Returns the head of this chain of small blocks.
5548 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5549 SmallBlockChainStream
* This
)
5551 DirEntry chainEntry
;
5554 if (This
->headOfStreamPlaceHolder
!= NULL
)
5555 return *(This
->headOfStreamPlaceHolder
);
5557 if (This
->ownerDirEntry
)
5559 hr
= StorageImpl_ReadDirEntry(
5560 This
->parentStorage
,
5561 This
->ownerDirEntry
,
5566 return chainEntry
.startingBlock
;
5571 return BLOCK_END_OF_CHAIN
;
5574 /******************************************************************************
5575 * SmallBlockChainStream_GetNextBlockInChain
5577 * Returns the index of the next small block in this chain.
5580 * - BLOCK_END_OF_CHAIN: end of this chain
5581 * - BLOCK_UNUSED: small block 'blockIndex' is free
5583 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5584 SmallBlockChainStream
* This
,
5586 ULONG
* nextBlockInChain
)
5588 ULARGE_INTEGER offsetOfBlockInDepot
;
5593 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5595 offsetOfBlockInDepot
.u
.HighPart
= 0;
5596 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5599 * Read those bytes in the buffer from the small block file.
5601 res
= BlockChainStream_ReadAt(
5602 This
->parentStorage
->smallBlockDepotChain
,
5603 offsetOfBlockInDepot
,
5610 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5617 /******************************************************************************
5618 * SmallBlockChainStream_SetNextBlockInChain
5620 * Writes the index of the next block of the specified block in the small
5622 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5623 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5625 static void SmallBlockChainStream_SetNextBlockInChain(
5626 SmallBlockChainStream
* This
,
5630 ULARGE_INTEGER offsetOfBlockInDepot
;
5634 offsetOfBlockInDepot
.u
.HighPart
= 0;
5635 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5637 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5640 * Read those bytes in the buffer from the small block file.
5642 BlockChainStream_WriteAt(
5643 This
->parentStorage
->smallBlockDepotChain
,
5644 offsetOfBlockInDepot
,
5650 /******************************************************************************
5651 * SmallBlockChainStream_FreeBlock
5653 * Flag small block 'blockIndex' as free in the small block depot.
5655 static void SmallBlockChainStream_FreeBlock(
5656 SmallBlockChainStream
* This
,
5659 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5662 /******************************************************************************
5663 * SmallBlockChainStream_GetNextFreeBlock
5665 * Returns the index of a free small block. The small block depot will be
5666 * enlarged if necessary. The small block chain will also be enlarged if
5669 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5670 SmallBlockChainStream
* This
)
5672 ULARGE_INTEGER offsetOfBlockInDepot
;
5675 ULONG blockIndex
= 0;
5676 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5678 ULONG smallBlocksPerBigBlock
;
5680 offsetOfBlockInDepot
.u
.HighPart
= 0;
5683 * Scan the small block depot for a free block
5685 while (nextBlockIndex
!= BLOCK_UNUSED
)
5687 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5689 res
= BlockChainStream_ReadAt(
5690 This
->parentStorage
->smallBlockDepotChain
,
5691 offsetOfBlockInDepot
,
5697 * If we run out of space for the small block depot, enlarge it
5701 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5703 if (nextBlockIndex
!= BLOCK_UNUSED
)
5709 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5711 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5712 ULONG nextBlock
, newsbdIndex
;
5713 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5715 nextBlock
= sbdIndex
;
5716 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5718 sbdIndex
= nextBlock
;
5719 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5722 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5723 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5724 StorageImpl_SetNextBlockInChain(
5725 This
->parentStorage
,
5729 StorageImpl_SetNextBlockInChain(
5730 This
->parentStorage
,
5732 BLOCK_END_OF_CHAIN
);
5735 * Initialize all the small blocks to free
5737 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5738 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5743 * We have just created the small block depot.
5749 * Save it in the header
5751 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5752 StorageImpl_SaveFileHeader(This
->parentStorage
);
5755 * And allocate the first big block that will contain small blocks
5758 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5760 StorageImpl_SetNextBlockInChain(
5761 This
->parentStorage
,
5763 BLOCK_END_OF_CHAIN
);
5765 StorageImpl_ReadDirEntry(
5766 This
->parentStorage
,
5767 This
->parentStorage
->base
.storageDirEntry
,
5770 rootEntry
.startingBlock
= sbStartIndex
;
5771 rootEntry
.size
.u
.HighPart
= 0;
5772 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5774 StorageImpl_WriteDirEntry(
5775 This
->parentStorage
,
5776 This
->parentStorage
->base
.storageDirEntry
,
5780 StorageImpl_SaveFileHeader(This
->parentStorage
);
5784 smallBlocksPerBigBlock
=
5785 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5788 * Verify if we have to allocate big blocks to contain small blocks
5790 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5793 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5795 StorageImpl_ReadDirEntry(
5796 This
->parentStorage
,
5797 This
->parentStorage
->base
.storageDirEntry
,
5800 if (rootEntry
.size
.u
.LowPart
<
5801 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5803 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5805 BlockChainStream_SetSize(
5806 This
->parentStorage
->smallBlockRootChain
,
5809 StorageImpl_WriteDirEntry(
5810 This
->parentStorage
,
5811 This
->parentStorage
->base
.storageDirEntry
,
5819 /******************************************************************************
5820 * SmallBlockChainStream_ReadAt
5822 * Reads a specified number of bytes from this chain at the specified offset.
5823 * bytesRead may be NULL.
5824 * Failure will be returned if the specified number of bytes has not been read.
5826 HRESULT
SmallBlockChainStream_ReadAt(
5827 SmallBlockChainStream
* This
,
5828 ULARGE_INTEGER offset
,
5834 ULARGE_INTEGER offsetInBigBlockFile
;
5835 ULONG blockNoInSequence
=
5836 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5838 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5839 ULONG bytesToReadInBuffer
;
5841 ULONG bytesReadFromBigBlockFile
;
5845 * This should never happen on a small block file.
5847 assert(offset
.u
.HighPart
==0);
5850 * Find the first block in the stream that contains part of the buffer.
5852 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5854 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5856 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5859 blockNoInSequence
--;
5863 * Start reading the buffer.
5866 bufferWalker
= buffer
;
5868 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5871 * Calculate how many bytes we can copy from this small block.
5873 bytesToReadInBuffer
=
5874 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5877 * Calculate the offset of the small block in the small block file.
5879 offsetInBigBlockFile
.u
.HighPart
= 0;
5880 offsetInBigBlockFile
.u
.LowPart
=
5881 blockIndex
* This
->parentStorage
->smallBlockSize
;
5883 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5886 * Read those bytes in the buffer from the small block file.
5887 * The small block has already been identified so it shouldn't fail
5888 * unless the file is corrupt.
5890 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5891 offsetInBigBlockFile
,
5892 bytesToReadInBuffer
,
5894 &bytesReadFromBigBlockFile
);
5900 * Step to the next big block.
5902 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5904 return STG_E_DOCFILECORRUPT
;
5906 bufferWalker
+= bytesReadFromBigBlockFile
;
5907 size
-= bytesReadFromBigBlockFile
;
5908 *bytesRead
+= bytesReadFromBigBlockFile
;
5909 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5912 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5915 /******************************************************************************
5916 * SmallBlockChainStream_WriteAt
5918 * Writes the specified number of bytes to this chain at the specified offset.
5919 * Will fail if not all specified number of bytes have been written.
5921 HRESULT
SmallBlockChainStream_WriteAt(
5922 SmallBlockChainStream
* This
,
5923 ULARGE_INTEGER offset
,
5926 ULONG
* bytesWritten
)
5928 ULARGE_INTEGER offsetInBigBlockFile
;
5929 ULONG blockNoInSequence
=
5930 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5932 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5933 ULONG bytesToWriteInBuffer
;
5935 ULONG bytesWrittenToBigBlockFile
;
5936 const BYTE
* bufferWalker
;
5940 * This should never happen on a small block file.
5942 assert(offset
.u
.HighPart
==0);
5945 * Find the first block in the stream that contains part of the buffer.
5947 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5949 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5951 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5952 return STG_E_DOCFILECORRUPT
;
5953 blockNoInSequence
--;
5957 * Start writing the buffer.
5960 bufferWalker
= buffer
;
5961 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5964 * Calculate how many bytes we can copy to this small block.
5966 bytesToWriteInBuffer
=
5967 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5970 * Calculate the offset of the small block in the small block file.
5972 offsetInBigBlockFile
.u
.HighPart
= 0;
5973 offsetInBigBlockFile
.u
.LowPart
=
5974 blockIndex
* This
->parentStorage
->smallBlockSize
;
5976 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5979 * Write those bytes in the buffer to the small block file.
5981 res
= BlockChainStream_WriteAt(
5982 This
->parentStorage
->smallBlockRootChain
,
5983 offsetInBigBlockFile
,
5984 bytesToWriteInBuffer
,
5986 &bytesWrittenToBigBlockFile
);
5991 * Step to the next big block.
5993 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5996 bufferWalker
+= bytesWrittenToBigBlockFile
;
5997 size
-= bytesWrittenToBigBlockFile
;
5998 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5999 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6002 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6005 /******************************************************************************
6006 * SmallBlockChainStream_Shrink
6008 * Shrinks this chain in the small block depot.
6010 static BOOL
SmallBlockChainStream_Shrink(
6011 SmallBlockChainStream
* This
,
6012 ULARGE_INTEGER newSize
)
6014 ULONG blockIndex
, extraBlock
;
6018 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6020 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6023 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6026 * Go to the new end of chain
6028 while (count
< numBlocks
)
6030 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6037 * If the count is 0, we have a special case, the head of the chain was
6042 DirEntry chainEntry
;
6044 StorageImpl_ReadDirEntry(This
->parentStorage
,
6045 This
->ownerDirEntry
,
6048 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6050 StorageImpl_WriteDirEntry(This
->parentStorage
,
6051 This
->ownerDirEntry
,
6055 * We start freeing the chain at the head block.
6057 extraBlock
= blockIndex
;
6061 /* Get the next block before marking the new end */
6062 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6066 /* Mark the new end of chain */
6067 SmallBlockChainStream_SetNextBlockInChain(
6070 BLOCK_END_OF_CHAIN
);
6074 * Mark the extra blocks as free
6076 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6081 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6082 extraBlock
= blockIndex
;
6088 /******************************************************************************
6089 * SmallBlockChainStream_Enlarge
6091 * Grows this chain in the small block depot.
6093 static BOOL
SmallBlockChainStream_Enlarge(
6094 SmallBlockChainStream
* This
,
6095 ULARGE_INTEGER newSize
)
6097 ULONG blockIndex
, currentBlock
;
6099 ULONG oldNumBlocks
= 0;
6101 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6104 * Empty chain. Create the head.
6106 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6108 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6109 SmallBlockChainStream_SetNextBlockInChain(
6112 BLOCK_END_OF_CHAIN
);
6114 if (This
->headOfStreamPlaceHolder
!= NULL
)
6116 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6120 DirEntry chainEntry
;
6122 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6125 chainEntry
.startingBlock
= blockIndex
;
6127 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6132 currentBlock
= blockIndex
;
6135 * Figure out how many blocks are needed to contain this stream
6137 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6139 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6143 * Go to the current end of chain
6145 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6148 currentBlock
= blockIndex
;
6149 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6154 * Add new blocks to the chain
6156 while (oldNumBlocks
< newNumBlocks
)
6158 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6159 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6161 SmallBlockChainStream_SetNextBlockInChain(
6164 BLOCK_END_OF_CHAIN
);
6166 currentBlock
= blockIndex
;
6173 /******************************************************************************
6174 * SmallBlockChainStream_SetSize
6176 * Sets the size of this stream.
6177 * The file will grow if we grow the chain.
6179 * TODO: Free the actual blocks in the file when we shrink the chain.
6180 * Currently, the blocks are still in the file. So the file size
6181 * doesn't shrink even if we shrink streams.
6183 BOOL
SmallBlockChainStream_SetSize(
6184 SmallBlockChainStream
* This
,
6185 ULARGE_INTEGER newSize
)
6187 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6189 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6192 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6194 SmallBlockChainStream_Shrink(This
, newSize
);
6198 SmallBlockChainStream_Enlarge(This
, newSize
);
6204 /******************************************************************************
6205 * SmallBlockChainStream_GetCount
6207 * Returns the number of small blocks that comprises this chain.
6208 * This is not the size of the stream as the last block may not be full!
6211 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6216 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6218 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6222 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6223 blockIndex
, &blockIndex
)))
6230 /******************************************************************************
6231 * SmallBlockChainStream_GetSize
6233 * Returns the size of this chain.
6235 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6237 DirEntry chainEntry
;
6239 if(This
->headOfStreamPlaceHolder
!= NULL
)
6241 ULARGE_INTEGER result
;
6242 result
.u
.HighPart
= 0;
6244 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6245 This
->parentStorage
->smallBlockSize
;
6250 StorageImpl_ReadDirEntry(
6251 This
->parentStorage
,
6252 This
->ownerDirEntry
,
6255 return chainEntry
.size
;
6258 /******************************************************************************
6259 * StgCreateDocfile [OLE32.@]
6260 * Creates a new compound file storage object
6263 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6264 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6265 * reserved [ ?] unused?, usually 0
6266 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6269 * S_OK if the file was successfully created
6270 * some STG_E_ value if error
6272 * if pwcsName is NULL, create file with new unique name
6273 * the function can returns
6274 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6277 HRESULT WINAPI
StgCreateDocfile(
6281 IStorage
**ppstgOpen
)
6283 StorageBaseImpl
* newStorage
= 0;
6284 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6285 HRESULT hr
= STG_E_INVALIDFLAG
;
6289 DWORD fileAttributes
;
6290 WCHAR tempFileName
[MAX_PATH
];
6292 TRACE("(%s, %x, %d, %p)\n",
6293 debugstr_w(pwcsName
), grfMode
,
6294 reserved
, ppstgOpen
);
6297 return STG_E_INVALIDPOINTER
;
6299 return STG_E_INVALIDPARAMETER
;
6301 /* if no share mode given then DENY_NONE is the default */
6302 if (STGM_SHARE_MODE(grfMode
) == 0)
6303 grfMode
|= STGM_SHARE_DENY_NONE
;
6305 if ( FAILED( validateSTGM(grfMode
) ))
6308 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6309 switch(STGM_ACCESS_MODE(grfMode
))
6312 case STGM_READWRITE
:
6318 /* in direct mode, can only use SHARE_EXCLUSIVE */
6319 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6322 /* but in transacted mode, any share mode is valid */
6325 * Generate a unique name.
6329 WCHAR tempPath
[MAX_PATH
];
6330 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6332 memset(tempPath
, 0, sizeof(tempPath
));
6333 memset(tempFileName
, 0, sizeof(tempFileName
));
6335 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6338 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6339 pwcsName
= tempFileName
;
6342 hr
= STG_E_INSUFFICIENTMEMORY
;
6346 creationMode
= TRUNCATE_EXISTING
;
6350 creationMode
= GetCreationModeFromSTGM(grfMode
);
6354 * Interpret the STGM value grfMode
6356 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6357 accessMode
= GetAccessModeFromSTGM(grfMode
);
6359 if (grfMode
& STGM_DELETEONRELEASE
)
6360 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6362 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6364 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6368 FIXME("Storage share mode not implemented.\n");
6373 hFile
= CreateFileW(pwcsName
,
6381 if (hFile
== INVALID_HANDLE_VALUE
)
6383 if(GetLastError() == ERROR_FILE_EXISTS
)
6384 hr
= STG_E_FILEALREADYEXISTS
;
6391 * Allocate and initialize the new IStorage32object.
6393 hr
= Storage_Construct(
6408 * Get an "out" pointer for the caller.
6410 *ppstgOpen
= (IStorage
*)newStorage
;
6413 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6418 /******************************************************************************
6419 * StgCreateStorageEx [OLE32.@]
6421 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6423 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6424 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6426 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6428 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6429 return STG_E_INVALIDPARAMETER
;
6432 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6434 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6435 return STG_E_INVALIDPARAMETER
;
6438 if (stgfmt
== STGFMT_FILE
)
6440 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6441 return STG_E_INVALIDPARAMETER
;
6444 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6446 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6447 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6450 ERR("Invalid stgfmt argument\n");
6451 return STG_E_INVALIDPARAMETER
;
6454 /******************************************************************************
6455 * StgCreatePropSetStg [OLE32.@]
6457 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6458 IPropertySetStorage
**ppPropSetStg
)
6462 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6464 hr
= STG_E_INVALIDPARAMETER
;
6466 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6467 (void**)ppPropSetStg
);
6471 /******************************************************************************
6472 * StgOpenStorageEx [OLE32.@]
6474 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6476 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6477 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6479 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6481 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6482 return STG_E_INVALIDPARAMETER
;
6488 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6489 return STG_E_INVALIDPARAMETER
;
6491 case STGFMT_STORAGE
:
6494 case STGFMT_DOCFILE
:
6495 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6497 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6498 return STG_E_INVALIDPARAMETER
;
6500 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6504 WARN("STGFMT_ANY assuming storage\n");
6508 return STG_E_INVALIDPARAMETER
;
6511 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6515 /******************************************************************************
6516 * StgOpenStorage [OLE32.@]
6518 HRESULT WINAPI
StgOpenStorage(
6519 const OLECHAR
*pwcsName
,
6520 IStorage
*pstgPriority
,
6524 IStorage
**ppstgOpen
)
6526 StorageBaseImpl
* newStorage
= 0;
6532 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6533 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6534 snbExclude
, reserved
, ppstgOpen
);
6538 hr
= STG_E_INVALIDNAME
;
6544 hr
= STG_E_INVALIDPOINTER
;
6550 hr
= STG_E_INVALIDPARAMETER
;
6554 if (grfMode
& STGM_PRIORITY
)
6556 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6557 return STG_E_INVALIDFLAG
;
6558 if (grfMode
& STGM_DELETEONRELEASE
)
6559 return STG_E_INVALIDFUNCTION
;
6560 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6561 return STG_E_INVALIDFLAG
;
6562 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6563 grfMode
|= STGM_SHARE_DENY_NONE
;
6565 /* STGM_PRIORITY stops other IStorage objects on the same file from
6566 * committing until the STGM_PRIORITY IStorage is closed. it also
6567 * stops non-transacted mode StgOpenStorage calls with write access from
6568 * succeeding. obviously, both of these cannot be achieved through just
6569 * file share flags */
6570 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6574 * Validate the sharing mode
6576 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6577 switch(STGM_SHARE_MODE(grfMode
))
6579 case STGM_SHARE_EXCLUSIVE
:
6580 case STGM_SHARE_DENY_WRITE
:
6583 hr
= STG_E_INVALIDFLAG
;
6587 if ( FAILED( validateSTGM(grfMode
) ) ||
6588 (grfMode
&STGM_CREATE
))
6590 hr
= STG_E_INVALIDFLAG
;
6594 /* shared reading requires transacted mode */
6595 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6596 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6597 !(grfMode
&STGM_TRANSACTED
) )
6599 hr
= STG_E_INVALIDFLAG
;
6604 * Interpret the STGM value grfMode
6606 shareMode
= GetShareModeFromSTGM(grfMode
);
6607 accessMode
= GetAccessModeFromSTGM(grfMode
);
6611 hFile
= CreateFileW( pwcsName
,
6616 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6619 if (hFile
==INVALID_HANDLE_VALUE
)
6621 DWORD last_error
= GetLastError();
6627 case ERROR_FILE_NOT_FOUND
:
6628 hr
= STG_E_FILENOTFOUND
;
6631 case ERROR_PATH_NOT_FOUND
:
6632 hr
= STG_E_PATHNOTFOUND
;
6635 case ERROR_ACCESS_DENIED
:
6636 case ERROR_WRITE_PROTECT
:
6637 hr
= STG_E_ACCESSDENIED
;
6640 case ERROR_SHARING_VIOLATION
:
6641 hr
= STG_E_SHAREVIOLATION
;
6652 * Refuse to open the file if it's too small to be a structured storage file
6653 * FIXME: verify the file when reading instead of here
6655 if (GetFileSize(hFile
, NULL
) < 0x100)
6658 hr
= STG_E_FILEALREADYEXISTS
;
6663 * Allocate and initialize the new IStorage32object.
6665 hr
= Storage_Construct(
6677 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6679 if(hr
== STG_E_INVALIDHEADER
)
6680 hr
= STG_E_FILEALREADYEXISTS
;
6685 * Get an "out" pointer for the caller.
6687 *ppstgOpen
= (IStorage
*)newStorage
;
6690 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6694 /******************************************************************************
6695 * StgCreateDocfileOnILockBytes [OLE32.@]
6697 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6701 IStorage
** ppstgOpen
)
6703 StorageBaseImpl
* newStorage
= 0;
6706 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6707 return STG_E_INVALIDPOINTER
;
6710 * Allocate and initialize the new IStorage object.
6712 hr
= Storage_Construct(
6727 * Get an "out" pointer for the caller.
6729 *ppstgOpen
= (IStorage
*)newStorage
;
6734 /******************************************************************************
6735 * StgOpenStorageOnILockBytes [OLE32.@]
6737 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6739 IStorage
*pstgPriority
,
6743 IStorage
**ppstgOpen
)
6745 StorageBaseImpl
* newStorage
= 0;
6748 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6749 return STG_E_INVALIDPOINTER
;
6751 if ( FAILED( validateSTGM(grfMode
) ))
6752 return STG_E_INVALIDFLAG
;
6757 * Allocate and initialize the new IStorage object.
6759 hr
= Storage_Construct(
6774 * Get an "out" pointer for the caller.
6776 *ppstgOpen
= (IStorage
*)newStorage
;
6781 /******************************************************************************
6782 * StgSetTimes [ole32.@]
6783 * StgSetTimes [OLE32.@]
6787 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6788 FILETIME
const *patime
, FILETIME
const *pmtime
)
6790 IStorage
*stg
= NULL
;
6793 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6795 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6799 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6800 IStorage_Release(stg
);
6806 /******************************************************************************
6807 * StgIsStorageILockBytes [OLE32.@]
6809 * Determines if the ILockBytes contains a storage object.
6811 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6814 ULARGE_INTEGER offset
;
6816 offset
.u
.HighPart
= 0;
6817 offset
.u
.LowPart
= 0;
6819 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6821 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6827 /******************************************************************************
6828 * WriteClassStg [OLE32.@]
6830 * This method will store the specified CLSID in the specified storage object
6832 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6837 return E_INVALIDARG
;
6840 return STG_E_INVALIDPOINTER
;
6842 hRes
= IStorage_SetClass(pStg
, rclsid
);
6847 /***********************************************************************
6848 * ReadClassStg (OLE32.@)
6850 * This method reads the CLSID previously written to a storage object with
6851 * the WriteClassStg.
6854 * pstg [I] IStorage pointer
6855 * pclsid [O] Pointer to where the CLSID is written
6859 * Failure: HRESULT code.
6861 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6866 TRACE("(%p, %p)\n", pstg
, pclsid
);
6868 if(!pstg
|| !pclsid
)
6869 return E_INVALIDARG
;
6872 * read a STATSTG structure (contains the clsid) from the storage
6874 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6877 *pclsid
=pstatstg
.clsid
;
6882 /***********************************************************************
6883 * OleLoadFromStream (OLE32.@)
6885 * This function loads an object from stream
6887 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6891 LPPERSISTSTREAM xstm
;
6893 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6895 res
=ReadClassStm(pStm
,&clsid
);
6898 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6901 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6903 IUnknown_Release((IUnknown
*)*ppvObj
);
6906 res
=IPersistStream_Load(xstm
,pStm
);
6907 IPersistStream_Release(xstm
);
6908 /* FIXME: all refcounts ok at this point? I think they should be:
6911 * xstm : 0 (released)
6916 /***********************************************************************
6917 * OleSaveToStream (OLE32.@)
6919 * This function saves an object with the IPersistStream interface on it
6920 * to the specified stream.
6922 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6928 TRACE("(%p,%p)\n",pPStm
,pStm
);
6930 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6932 if (SUCCEEDED(res
)){
6934 res
=WriteClassStm(pStm
,&clsid
);
6938 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6941 TRACE("Finished Save\n");
6945 /****************************************************************************
6946 * This method validate a STGM parameter that can contain the values below
6948 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6949 * The stgm values contained in 0xffff0000 are bitmasks.
6951 * STGM_DIRECT 0x00000000
6952 * STGM_TRANSACTED 0x00010000
6953 * STGM_SIMPLE 0x08000000
6955 * STGM_READ 0x00000000
6956 * STGM_WRITE 0x00000001
6957 * STGM_READWRITE 0x00000002
6959 * STGM_SHARE_DENY_NONE 0x00000040
6960 * STGM_SHARE_DENY_READ 0x00000030
6961 * STGM_SHARE_DENY_WRITE 0x00000020
6962 * STGM_SHARE_EXCLUSIVE 0x00000010
6964 * STGM_PRIORITY 0x00040000
6965 * STGM_DELETEONRELEASE 0x04000000
6967 * STGM_CREATE 0x00001000
6968 * STGM_CONVERT 0x00020000
6969 * STGM_FAILIFTHERE 0x00000000
6971 * STGM_NOSCRATCH 0x00100000
6972 * STGM_NOSNAPSHOT 0x00200000
6974 static HRESULT
validateSTGM(DWORD stgm
)
6976 DWORD access
= STGM_ACCESS_MODE(stgm
);
6977 DWORD share
= STGM_SHARE_MODE(stgm
);
6978 DWORD create
= STGM_CREATE_MODE(stgm
);
6980 if (stgm
&~STGM_KNOWN_FLAGS
)
6982 ERR("unknown flags %08x\n", stgm
);
6990 case STGM_READWRITE
:
6998 case STGM_SHARE_DENY_NONE
:
6999 case STGM_SHARE_DENY_READ
:
7000 case STGM_SHARE_DENY_WRITE
:
7001 case STGM_SHARE_EXCLUSIVE
:
7010 case STGM_FAILIFTHERE
:
7017 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7019 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7023 * STGM_CREATE | STGM_CONVERT
7024 * if both are false, STGM_FAILIFTHERE is set to TRUE
7026 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7030 * STGM_NOSCRATCH requires STGM_TRANSACTED
7032 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7036 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7037 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7039 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7040 (!(stgm
& STGM_TRANSACTED
) ||
7041 share
== STGM_SHARE_EXCLUSIVE
||
7042 share
== STGM_SHARE_DENY_WRITE
) )
7048 /****************************************************************************
7049 * GetShareModeFromSTGM
7051 * This method will return a share mode flag from a STGM value.
7052 * The STGM value is assumed valid.
7054 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7056 switch (STGM_SHARE_MODE(stgm
))
7058 case STGM_SHARE_DENY_NONE
:
7059 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7060 case STGM_SHARE_DENY_READ
:
7061 return FILE_SHARE_WRITE
;
7062 case STGM_SHARE_DENY_WRITE
:
7063 return FILE_SHARE_READ
;
7064 case STGM_SHARE_EXCLUSIVE
:
7067 ERR("Invalid share mode!\n");
7072 /****************************************************************************
7073 * GetAccessModeFromSTGM
7075 * This method will return an access mode flag from a STGM value.
7076 * The STGM value is assumed valid.
7078 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7080 switch (STGM_ACCESS_MODE(stgm
))
7083 return GENERIC_READ
;
7085 case STGM_READWRITE
:
7086 return GENERIC_READ
| GENERIC_WRITE
;
7088 ERR("Invalid access mode!\n");
7093 /****************************************************************************
7094 * GetCreationModeFromSTGM
7096 * This method will return a creation mode flag from a STGM value.
7097 * The STGM value is assumed valid.
7099 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7101 switch(STGM_CREATE_MODE(stgm
))
7104 return CREATE_ALWAYS
;
7106 FIXME("STGM_CONVERT not implemented!\n");
7108 case STGM_FAILIFTHERE
:
7111 ERR("Invalid create mode!\n");
7117 /*************************************************************************
7118 * OLECONVERT_LoadOLE10 [Internal]
7120 * Loads the OLE10 STREAM to memory
7123 * pOleStream [I] The OLESTREAM
7124 * pData [I] Data Structure for the OLESTREAM Data
7128 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7129 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7132 * This function is used by OleConvertOLESTREAMToIStorage only.
7134 * Memory allocated for pData must be freed by the caller
7136 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7139 HRESULT hRes
= S_OK
;
7143 pData
->pData
= NULL
;
7144 pData
->pstrOleObjFileName
= NULL
;
7146 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7149 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7150 if(dwSize
!= sizeof(pData
->dwOleID
))
7152 hRes
= CONVERT10_E_OLESTREAM_GET
;
7154 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7156 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7167 /* Get the TypeID... more info needed for this field */
7168 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7169 if(dwSize
!= sizeof(pData
->dwTypeID
))
7171 hRes
= CONVERT10_E_OLESTREAM_GET
;
7176 if(pData
->dwTypeID
!= 0)
7178 /* Get the length of the OleTypeName */
7179 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7180 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7182 hRes
= CONVERT10_E_OLESTREAM_GET
;
7187 if(pData
->dwOleTypeNameLength
> 0)
7189 /* Get the OleTypeName */
7190 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7191 if(dwSize
!= pData
->dwOleTypeNameLength
)
7193 hRes
= CONVERT10_E_OLESTREAM_GET
;
7199 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7200 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7202 hRes
= CONVERT10_E_OLESTREAM_GET
;
7206 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7207 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7208 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7209 if(pData
->pstrOleObjFileName
)
7211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7212 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7214 hRes
= CONVERT10_E_OLESTREAM_GET
;
7218 hRes
= CONVERT10_E_OLESTREAM_GET
;
7223 /* Get the Width of the Metafile */
7224 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7225 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7227 hRes
= CONVERT10_E_OLESTREAM_GET
;
7231 /* Get the Height of the Metafile */
7232 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7233 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7235 hRes
= CONVERT10_E_OLESTREAM_GET
;
7241 /* Get the Length of the Data */
7242 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7243 if(dwSize
!= sizeof(pData
->dwDataLength
))
7245 hRes
= CONVERT10_E_OLESTREAM_GET
;
7249 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7251 if(!bStrem1
) /* if it is a second OLE stream data */
7253 pData
->dwDataLength
-= 8;
7254 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7255 if(dwSize
!= sizeof(pData
->strUnknown
))
7257 hRes
= CONVERT10_E_OLESTREAM_GET
;
7263 if(pData
->dwDataLength
> 0)
7265 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7267 /* Get Data (ex. IStorage, Metafile, or BMP) */
7270 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7271 if(dwSize
!= pData
->dwDataLength
)
7273 hRes
= CONVERT10_E_OLESTREAM_GET
;
7278 hRes
= CONVERT10_E_OLESTREAM_GET
;
7287 /*************************************************************************
7288 * OLECONVERT_SaveOLE10 [Internal]
7290 * Saves the OLE10 STREAM From memory
7293 * pData [I] Data Structure for the OLESTREAM Data
7294 * pOleStream [I] The OLESTREAM to save
7298 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7301 * This function is used by OleConvertIStorageToOLESTREAM only.
7304 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7307 HRESULT hRes
= S_OK
;
7311 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7312 if(dwSize
!= sizeof(pData
->dwOleID
))
7314 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7319 /* Set the TypeID */
7320 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7321 if(dwSize
!= sizeof(pData
->dwTypeID
))
7323 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7327 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7329 /* Set the Length of the OleTypeName */
7330 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7331 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7333 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7338 if(pData
->dwOleTypeNameLength
> 0)
7340 /* Set the OleTypeName */
7341 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7342 if(dwSize
!= pData
->dwOleTypeNameLength
)
7344 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7351 /* Set the width of the Metafile */
7352 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7353 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7355 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7361 /* Set the height of the Metafile */
7362 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7363 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7365 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7371 /* Set the length of the Data */
7372 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7373 if(dwSize
!= sizeof(pData
->dwDataLength
))
7375 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7381 if(pData
->dwDataLength
> 0)
7383 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7384 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7385 if(dwSize
!= pData
->dwDataLength
)
7387 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7395 /*************************************************************************
7396 * OLECONVERT_GetOLE20FromOLE10[Internal]
7398 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7399 * opens it, and copies the content to the dest IStorage for
7400 * OleConvertOLESTREAMToIStorage
7404 * pDestStorage [I] The IStorage to copy the data to
7405 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7406 * nBufferLength [I] The size of the buffer
7415 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7419 IStorage
*pTempStorage
;
7420 DWORD dwNumOfBytesWritten
;
7421 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7422 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7424 /* Create a temp File */
7425 GetTempPathW(MAX_PATH
, wstrTempDir
);
7426 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7427 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7429 if(hFile
!= INVALID_HANDLE_VALUE
)
7431 /* Write IStorage Data to File */
7432 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7435 /* Open and copy temp storage to the Dest Storage */
7436 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7439 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7440 IStorage_Release(pTempStorage
);
7442 DeleteFileW(wstrTempFile
);
7447 /*************************************************************************
7448 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7450 * Saves the OLE10 STREAM From memory
7453 * pStorage [I] The Src IStorage to copy
7454 * pData [I] The Dest Memory to write to.
7457 * The size in bytes allocated for pData
7460 * Memory allocated for pData must be freed by the caller
7462 * Used by OleConvertIStorageToOLESTREAM only.
7465 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7469 DWORD nDataLength
= 0;
7470 IStorage
*pTempStorage
;
7471 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7472 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7476 /* Create temp Storage */
7477 GetTempPathW(MAX_PATH
, wstrTempDir
);
7478 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7479 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7483 /* Copy Src Storage to the Temp Storage */
7484 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7485 IStorage_Release(pTempStorage
);
7487 /* Open Temp Storage as a file and copy to memory */
7488 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7489 if(hFile
!= INVALID_HANDLE_VALUE
)
7491 nDataLength
= GetFileSize(hFile
, NULL
);
7492 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7493 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7496 DeleteFileW(wstrTempFile
);
7501 /*************************************************************************
7502 * OLECONVERT_CreateOleStream [Internal]
7504 * Creates the "\001OLE" stream in the IStorage if necessary.
7507 * pStorage [I] Dest storage to create the stream in
7513 * This function is used by OleConvertOLESTREAMToIStorage only.
7515 * This stream is still unknown, MS Word seems to have extra data
7516 * but since the data is stored in the OLESTREAM there should be
7517 * no need to recreate the stream. If the stream is manually
7518 * deleted it will create it with this default data.
7521 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7525 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7526 BYTE pOleStreamHeader
[] =
7528 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7529 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7530 0x00, 0x00, 0x00, 0x00
7533 /* Create stream if not present */
7534 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7535 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7539 /* Write default Data */
7540 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7541 IStream_Release(pStream
);
7545 /* write a string to a stream, preceded by its length */
7546 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7553 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7554 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7559 str
= CoTaskMemAlloc( len
);
7560 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7561 r
= IStream_Write( stm
, str
, len
, NULL
);
7562 CoTaskMemFree( str
);
7566 /* read a string preceded by its length from a stream */
7567 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7570 DWORD len
, count
= 0;
7574 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7577 if( count
!= sizeof(len
) )
7578 return E_OUTOFMEMORY
;
7580 TRACE("%d bytes\n",len
);
7582 str
= CoTaskMemAlloc( len
);
7584 return E_OUTOFMEMORY
;
7586 r
= IStream_Read( stm
, str
, len
, &count
);
7591 CoTaskMemFree( str
);
7592 return E_OUTOFMEMORY
;
7595 TRACE("Read string %s\n",debugstr_an(str
,len
));
7597 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7598 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7600 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7601 CoTaskMemFree( str
);
7609 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7610 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7614 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7616 static const BYTE unknown1
[12] =
7617 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7618 0xFF, 0xFF, 0xFF, 0xFF};
7619 static const BYTE unknown2
[16] =
7620 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7623 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7624 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7625 debugstr_w(szProgIDName
));
7627 /* Create a CompObj stream */
7628 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7629 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7633 /* Write CompObj Structure to stream */
7634 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7636 if( SUCCEEDED( r
) )
7637 r
= WriteClassStm( pstm
, clsid
);
7639 if( SUCCEEDED( r
) )
7640 r
= STREAM_WriteString( pstm
, lpszUserType
);
7641 if( SUCCEEDED( r
) )
7642 r
= STREAM_WriteString( pstm
, szClipName
);
7643 if( SUCCEEDED( r
) )
7644 r
= STREAM_WriteString( pstm
, szProgIDName
);
7645 if( SUCCEEDED( r
) )
7646 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7648 IStream_Release( pstm
);
7653 /***********************************************************************
7654 * WriteFmtUserTypeStg (OLE32.@)
7656 HRESULT WINAPI
WriteFmtUserTypeStg(
7657 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7660 WCHAR szwClipName
[0x40];
7661 CLSID clsid
= CLSID_NULL
;
7662 LPWSTR wstrProgID
= NULL
;
7665 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7667 /* get the clipboard format name */
7668 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7671 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7673 /* FIXME: There's room to save a CLSID and its ProgID, but
7674 the CLSID is not looked up in the registry and in all the
7675 tests I wrote it was CLSID_NULL. Where does it come from?
7678 /* get the real program ID. This may fail, but that's fine */
7679 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7681 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7683 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7684 lpszUserType
, szwClipName
, wstrProgID
);
7686 CoTaskMemFree(wstrProgID
);
7692 /******************************************************************************
7693 * ReadFmtUserTypeStg [OLE32.@]
7695 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7699 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7700 unsigned char unknown1
[12];
7701 unsigned char unknown2
[16];
7703 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7706 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7708 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7709 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7712 WARN("Failed to open stream r = %08x\n", r
);
7716 /* read the various parts of the structure */
7717 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7718 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7720 r
= ReadClassStm( stm
, &clsid
);
7724 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7728 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7732 r
= STREAM_ReadString( stm
, &szProgIDName
);
7736 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7737 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7740 /* ok, success... now we just need to store what we found */
7742 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7743 CoTaskMemFree( szOleTypeName
);
7745 if( lplpszUserType
)
7746 *lplpszUserType
= szCLSIDName
;
7747 CoTaskMemFree( szProgIDName
);
7750 IStream_Release( stm
);
7756 /*************************************************************************
7757 * OLECONVERT_CreateCompObjStream [Internal]
7759 * Creates a "\001CompObj" is the destination IStorage if necessary.
7762 * pStorage [I] The dest IStorage to create the CompObj Stream
7764 * strOleTypeName [I] The ProgID
7768 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7771 * This function is used by OleConvertOLESTREAMToIStorage only.
7773 * The stream data is stored in the OLESTREAM and there should be
7774 * no need to recreate the stream. If the stream is manually
7775 * deleted it will attempt to create it by querying the registry.
7779 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7782 HRESULT hStorageRes
, hRes
= S_OK
;
7783 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7784 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7785 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7787 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7788 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7790 /* Initialize the CompObj structure */
7791 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7792 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7793 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7796 /* Create a CompObj stream if it doesn't exist */
7797 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7798 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7799 if(hStorageRes
== S_OK
)
7801 /* copy the OleTypeName to the compobj struct */
7802 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7803 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7805 /* copy the OleTypeName to the compobj struct */
7806 /* Note: in the test made, these were Identical */
7807 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7808 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7811 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7812 bufferW
, OLESTREAM_MAX_STR_LEN
);
7813 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7819 /* Get the CLSID Default Name from the Registry */
7820 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7821 if(hErr
== ERROR_SUCCESS
)
7823 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7824 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7825 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7826 if(hErr
== ERROR_SUCCESS
)
7828 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7834 /* Write CompObj Structure to stream */
7835 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7837 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7839 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7840 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7842 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7844 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7845 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7847 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7849 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7850 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7852 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7854 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7855 IStream_Release(pStream
);
7861 /*************************************************************************
7862 * OLECONVERT_CreateOlePresStream[Internal]
7864 * Creates the "\002OlePres000" Stream with the Metafile data
7867 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7868 * dwExtentX [I] Width of the Metafile
7869 * dwExtentY [I] Height of the Metafile
7870 * pData [I] Metafile data
7871 * dwDataLength [I] Size of the Metafile data
7875 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7878 * This function is used by OleConvertOLESTREAMToIStorage only.
7881 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7885 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7886 BYTE pOlePresStreamHeader
[] =
7888 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7889 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7890 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7891 0x00, 0x00, 0x00, 0x00
7894 BYTE pOlePresStreamHeaderEmpty
[] =
7896 0x00, 0x00, 0x00, 0x00,
7897 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7898 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7899 0x00, 0x00, 0x00, 0x00
7902 /* Create the OlePres000 Stream */
7903 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7904 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7909 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7911 memset(&OlePres
, 0, sizeof(OlePres
));
7912 /* Do we have any metafile data to save */
7913 if(dwDataLength
> 0)
7915 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7916 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7920 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7921 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7923 /* Set width and height of the metafile */
7924 OlePres
.dwExtentX
= dwExtentX
;
7925 OlePres
.dwExtentY
= -dwExtentY
;
7927 /* Set Data and Length */
7928 if(dwDataLength
> sizeof(METAFILEPICT16
))
7930 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7931 OlePres
.pData
= &(pData
[8]);
7933 /* Save OlePres000 Data to Stream */
7934 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7935 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7936 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7937 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7938 if(OlePres
.dwSize
> 0)
7940 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7942 IStream_Release(pStream
);
7946 /*************************************************************************
7947 * OLECONVERT_CreateOle10NativeStream [Internal]
7949 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7952 * pStorage [I] Dest storage to create the stream in
7953 * pData [I] Ole10 Native Data (ex. bmp)
7954 * dwDataLength [I] Size of the Ole10 Native Data
7960 * This function is used by OleConvertOLESTREAMToIStorage only.
7962 * Might need to verify the data and return appropriate error message
7965 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7969 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7971 /* Create the Ole10Native Stream */
7972 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7973 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7977 /* Write info to stream */
7978 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7979 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7980 IStream_Release(pStream
);
7985 /*************************************************************************
7986 * OLECONVERT_GetOLE10ProgID [Internal]
7988 * Finds the ProgID (or OleTypeID) from the IStorage
7991 * pStorage [I] The Src IStorage to get the ProgID
7992 * strProgID [I] the ProgID string to get
7993 * dwSize [I] the size of the string
7997 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8000 * This function is used by OleConvertIStorageToOLESTREAM only.
8004 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8008 LARGE_INTEGER iSeekPos
;
8009 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8010 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8012 /* Open the CompObj Stream */
8013 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8014 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8018 /*Get the OleType from the CompObj Stream */
8019 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8020 iSeekPos
.u
.HighPart
= 0;
8022 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8023 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8024 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8025 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8026 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8027 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8028 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8030 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8033 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8035 IStream_Release(pStream
);
8040 LPOLESTR wstrProgID
;
8042 /* Get the OleType from the registry */
8043 REFCLSID clsid
= &(stat
.clsid
);
8044 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8045 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8048 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8055 /*************************************************************************
8056 * OLECONVERT_GetOle10PresData [Internal]
8058 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8061 * pStorage [I] Src IStroage
8062 * pOleStream [I] Dest OleStream Mem Struct
8068 * This function is used by OleConvertIStorageToOLESTREAM only.
8070 * Memory allocated for pData must be freed by the caller
8074 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8079 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8081 /* Initialize Default data for OLESTREAM */
8082 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8083 pOleStreamData
[0].dwTypeID
= 2;
8084 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8085 pOleStreamData
[1].dwTypeID
= 0;
8086 pOleStreamData
[0].dwMetaFileWidth
= 0;
8087 pOleStreamData
[0].dwMetaFileHeight
= 0;
8088 pOleStreamData
[0].pData
= NULL
;
8089 pOleStreamData
[1].pData
= NULL
;
8091 /* Open Ole10Native Stream */
8092 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8093 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8097 /* Read Size and Data */
8098 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8099 if(pOleStreamData
->dwDataLength
> 0)
8101 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8102 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8104 IStream_Release(pStream
);
8110 /*************************************************************************
8111 * OLECONVERT_GetOle20PresData[Internal]
8113 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8116 * pStorage [I] Src IStroage
8117 * pOleStreamData [I] Dest OleStream Mem Struct
8123 * This function is used by OleConvertIStorageToOLESTREAM only.
8125 * Memory allocated for pData must be freed by the caller
8127 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8131 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8132 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8134 /* Initialize Default data for OLESTREAM */
8135 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8136 pOleStreamData
[0].dwTypeID
= 2;
8137 pOleStreamData
[0].dwMetaFileWidth
= 0;
8138 pOleStreamData
[0].dwMetaFileHeight
= 0;
8139 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8140 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8141 pOleStreamData
[1].dwTypeID
= 0;
8142 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8143 pOleStreamData
[1].strOleTypeName
[0] = 0;
8144 pOleStreamData
[1].dwMetaFileWidth
= 0;
8145 pOleStreamData
[1].dwMetaFileHeight
= 0;
8146 pOleStreamData
[1].pData
= NULL
;
8147 pOleStreamData
[1].dwDataLength
= 0;
8150 /* Open OlePress000 stream */
8151 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8152 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8155 LARGE_INTEGER iSeekPos
;
8156 METAFILEPICT16 MetaFilePict
;
8157 static const char strMetafilePictName
[] = "METAFILEPICT";
8159 /* Set the TypeID for a Metafile */
8160 pOleStreamData
[1].dwTypeID
= 5;
8162 /* Set the OleTypeName to Metafile */
8163 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8164 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8166 iSeekPos
.u
.HighPart
= 0;
8167 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8169 /* Get Presentation Data */
8170 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8171 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8172 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8173 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8175 /*Set width and Height */
8176 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8177 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8178 if(olePress
.dwSize
> 0)
8181 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8183 /* Set MetaFilePict struct */
8184 MetaFilePict
.mm
= 8;
8185 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8186 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8187 MetaFilePict
.hMF
= 0;
8189 /* Get Metafile Data */
8190 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8191 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8192 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8194 IStream_Release(pStream
);
8198 /*************************************************************************
8199 * OleConvertOLESTREAMToIStorage [OLE32.@]
8204 * DVTARGETDEVICE parameter is not handled
8205 * Still unsure of some mem fields for OLE 10 Stream
8206 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8207 * and "\001OLE" streams
8210 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8211 LPOLESTREAM pOleStream
,
8213 const DVTARGETDEVICE
* ptd
)
8217 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8219 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8221 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8225 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8228 if(pstg
== NULL
|| pOleStream
== NULL
)
8230 hRes
= E_INVALIDARG
;
8235 /* Load the OLESTREAM to Memory */
8236 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8241 /* Load the OLESTREAM to Memory (part 2)*/
8242 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8248 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8250 /* Do we have the IStorage Data in the OLESTREAM */
8251 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8253 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8254 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8258 /* It must be an original OLE 1.0 source */
8259 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8264 /* It must be an original OLE 1.0 source */
8265 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8268 /* Create CompObj Stream if necessary */
8269 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8272 /*Create the Ole Stream if necessary */
8273 OLECONVERT_CreateOleStream(pstg
);
8278 /* Free allocated memory */
8279 for(i
=0; i
< 2; i
++)
8281 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8282 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8283 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8288 /*************************************************************************
8289 * OleConvertIStorageToOLESTREAM [OLE32.@]
8296 * Still unsure of some mem fields for OLE 10 Stream
8297 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8298 * and "\001OLE" streams.
8301 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8303 LPOLESTREAM pOleStream
)
8306 HRESULT hRes
= S_OK
;
8308 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8309 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8311 TRACE("%p %p\n", pstg
, pOleStream
);
8313 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8315 if(pstg
== NULL
|| pOleStream
== NULL
)
8317 hRes
= E_INVALIDARG
;
8321 /* Get the ProgID */
8322 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8323 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8327 /* Was it originally Ole10 */
8328 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8331 IStream_Release(pStream
);
8332 /* Get Presentation Data for Ole10Native */
8333 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8337 /* Get Presentation Data (OLE20) */
8338 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8341 /* Save OLESTREAM */
8342 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8345 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8350 /* Free allocated memory */
8351 for(i
=0; i
< 2; i
++)
8353 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8359 /***********************************************************************
8360 * GetConvertStg (OLE32.@)
8362 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8363 FIXME("unimplemented stub!\n");
8367 /******************************************************************************
8368 * StgIsStorageFile [OLE32.@]
8369 * Verify if the file contains a storage object
8375 * S_OK if file has magic bytes as a storage object
8376 * S_FALSE if file is not storage
8379 StgIsStorageFile(LPCOLESTR fn
)
8385 TRACE("%s\n", debugstr_w(fn
));
8386 hf
= CreateFileW(fn
, GENERIC_READ
,
8387 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8388 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8390 if (hf
== INVALID_HANDLE_VALUE
)
8391 return STG_E_FILENOTFOUND
;
8393 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8395 WARN(" unable to read file\n");
8402 if (bytes_read
!= 8) {
8403 TRACE(" too short\n");
8407 if (!memcmp(magic
,STORAGE_magic
,8)) {
8412 TRACE(" -> Invalid header.\n");
8416 /***********************************************************************
8417 * WriteClassStm (OLE32.@)
8419 * Writes a CLSID to a stream.
8422 * pStm [I] Stream to write to.
8423 * rclsid [I] CLSID to write.
8427 * Failure: HRESULT code.
8429 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8431 TRACE("(%p,%p)\n",pStm
,rclsid
);
8433 if (!pStm
|| !rclsid
)
8434 return E_INVALIDARG
;
8436 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8439 /***********************************************************************
8440 * ReadClassStm (OLE32.@)
8442 * Reads a CLSID from a stream.
8445 * pStm [I] Stream to read from.
8446 * rclsid [O] CLSID to read.
8450 * Failure: HRESULT code.
8452 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8457 TRACE("(%p,%p)\n",pStm
,pclsid
);
8459 if (!pStm
|| !pclsid
)
8460 return E_INVALIDARG
;
8462 /* clear the output args */
8463 *pclsid
= CLSID_NULL
;
8465 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8470 if (nbByte
!= sizeof(CLSID
))
8471 return STG_E_READFAULT
;