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 /* Delete any sibling links */
801 currentEntry
.leftChild
= DIRENTRY_NULL
;
802 currentEntry
.rightChild
= DIRENTRY_NULL
;
804 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
807 /* Insert the element in a new position in the tree */
808 insertIntoTree(This
, This
->storageDirEntry
,
814 * There is no element with the old name
816 return STG_E_FILENOTFOUND
;
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
825 * This method will create a stream object within this storage
827 * See Windows documentation for more details on IStorage methods.
829 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
831 const OLECHAR
* pwcsName
, /* [string][in] */
832 DWORD grfMode
, /* [in] */
833 DWORD reserved1
, /* [in] */
834 DWORD reserved2
, /* [in] */
835 IStream
** ppstm
) /* [out] */
837 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
838 StgStreamImpl
* newStream
;
839 DirEntry currentEntry
, newStreamEntry
;
840 DirRef currentEntryRef
, newStreamEntryRef
;
843 TRACE("(%p, %s, %x, %d, %d, %p)\n",
844 iface
, debugstr_w(pwcsName
), grfMode
,
845 reserved1
, reserved2
, ppstm
);
848 return STG_E_INVALIDPOINTER
;
851 return STG_E_INVALIDNAME
;
853 if (reserved1
|| reserved2
)
854 return STG_E_INVALIDPARAMETER
;
856 if ( FAILED( validateSTGM(grfMode
) ))
857 return STG_E_INVALIDFLAG
;
859 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
860 return STG_E_INVALIDFLAG
;
863 return STG_E_REVERTED
;
868 if ((grfMode
& STGM_DELETEONRELEASE
) ||
869 (grfMode
& STGM_TRANSACTED
))
870 return STG_E_INVALIDFUNCTION
;
873 * Don't worry about permissions in transacted mode, as we can always write
874 * changes; we just can't always commit them.
876 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
877 /* Can't create a stream on read-only storage */
878 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
879 return STG_E_ACCESSDENIED
;
881 /* Can't create a stream with greater access than the parent. */
882 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
883 return STG_E_ACCESSDENIED
;
886 if(This
->openFlags
& STGM_SIMPLE
)
887 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
891 currentEntryRef
= findElement(This
,
892 This
->storageDirEntry
,
896 if (currentEntryRef
!= DIRENTRY_NULL
)
899 * An element with this name already exists
901 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
903 IStorage_DestroyElement(iface
, pwcsName
);
906 return STG_E_FILEALREADYEXISTS
;
910 * memset the empty entry
912 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
914 newStreamEntry
.sizeOfNameString
=
915 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
917 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
918 return STG_E_INVALIDNAME
;
920 strcpyW(newStreamEntry
.name
, pwcsName
);
922 newStreamEntry
.stgType
= STGTY_STREAM
;
923 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
924 newStreamEntry
.size
.u
.LowPart
= 0;
925 newStreamEntry
.size
.u
.HighPart
= 0;
927 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
928 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
929 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
931 /* call CoFileTime to get the current time
936 /* newStreamEntry.clsid */
939 * Create an entry with the new data
941 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
946 * Insert the new entry in the parent storage's tree.
950 This
->storageDirEntry
,
954 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
959 * Open the stream to return it.
961 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
965 *ppstm
= (IStream
*)newStream
;
967 IStream_AddRef(*ppstm
);
971 return STG_E_INSUFFICIENTMEMORY
;
977 /************************************************************************
978 * Storage32BaseImpl_SetClass (IStorage)
980 * This method will write the specified CLSID in the directory entry of this
983 * See Windows documentation for more details on IStorage methods.
985 static HRESULT WINAPI
StorageBaseImpl_SetClass(
987 REFCLSID clsid
) /* [in] */
989 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
991 DirEntry currentEntry
;
993 TRACE("(%p, %p)\n", iface
, clsid
);
996 return STG_E_REVERTED
;
998 hRes
= StorageBaseImpl_ReadDirEntry(This
,
999 This
->storageDirEntry
,
1001 if (SUCCEEDED(hRes
))
1003 currentEntry
.clsid
= *clsid
;
1005 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1006 This
->storageDirEntry
,
1013 /************************************************************************
1014 ** Storage32Impl implementation
1017 /************************************************************************
1018 * Storage32BaseImpl_CreateStorage (IStorage)
1020 * This method will create the storage object within the provided storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1026 const OLECHAR
*pwcsName
, /* [string][in] */
1027 DWORD grfMode
, /* [in] */
1028 DWORD reserved1
, /* [in] */
1029 DWORD reserved2
, /* [in] */
1030 IStorage
**ppstg
) /* [out] */
1032 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1034 DirEntry currentEntry
;
1036 DirRef currentEntryRef
;
1040 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1041 iface
, debugstr_w(pwcsName
), grfMode
,
1042 reserved1
, reserved2
, ppstg
);
1045 return STG_E_INVALIDPOINTER
;
1047 if (This
->openFlags
& STGM_SIMPLE
)
1049 return STG_E_INVALIDFUNCTION
;
1053 return STG_E_INVALIDNAME
;
1057 if ( FAILED( validateSTGM(grfMode
) ) ||
1058 (grfMode
& STGM_DELETEONRELEASE
) )
1060 WARN("bad grfMode: 0x%x\n", grfMode
);
1061 return STG_E_INVALIDFLAG
;
1065 return STG_E_REVERTED
;
1068 * Check that we're compatible with the parent's storage mode
1070 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1071 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1073 WARN("access denied\n");
1074 return STG_E_ACCESSDENIED
;
1077 currentEntryRef
= findElement(This
,
1078 This
->storageDirEntry
,
1082 if (currentEntryRef
!= DIRENTRY_NULL
)
1085 * An element with this name already exists
1087 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1088 ((This
->openFlags
& STGM_TRANSACTED
) ||
1089 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1091 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS
;
1101 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1102 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1104 WARN("read-only storage\n");
1105 return STG_E_ACCESSDENIED
;
1108 memset(&newEntry
, 0, sizeof(DirEntry
));
1110 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1112 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1114 FIXME("name too long\n");
1115 return STG_E_INVALIDNAME
;
1118 strcpyW(newEntry
.name
, pwcsName
);
1120 newEntry
.stgType
= STGTY_STORAGE
;
1121 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1122 newEntry
.size
.u
.LowPart
= 0;
1123 newEntry
.size
.u
.HighPart
= 0;
1125 newEntry
.leftChild
= DIRENTRY_NULL
;
1126 newEntry
.rightChild
= DIRENTRY_NULL
;
1127 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1129 /* call CoFileTime to get the current time
1134 /* newEntry.clsid */
1137 * Create a new directory entry for the storage
1139 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1144 * Insert the new directory entry into the parent storage's tree
1146 hr
= insertIntoTree(
1148 This
->storageDirEntry
,
1152 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1157 * Open it to get a pointer to return.
1159 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1161 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1171 /***************************************************************************
1175 * Reserve a directory entry in the file and initialize it.
1177 static HRESULT
StorageImpl_CreateDirEntry(
1178 StorageBaseImpl
*base
,
1179 const DirEntry
*newData
,
1182 StorageImpl
*storage
= (StorageImpl
*)base
;
1183 ULONG currentEntryIndex
= 0;
1184 ULONG newEntryIndex
= DIRENTRY_NULL
;
1186 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1187 WORD sizeOfNameString
;
1191 hr
= StorageImpl_ReadRawDirEntry(storage
,
1197 StorageUtl_ReadWord(
1199 OFFSET_PS_NAMELENGTH
,
1202 if (sizeOfNameString
== 0)
1205 * The entry exists and is available, we found it.
1207 newEntryIndex
= currentEntryIndex
;
1213 * We exhausted the directory entries, we will create more space below
1215 newEntryIndex
= currentEntryIndex
;
1217 currentEntryIndex
++;
1219 } while (newEntryIndex
== DIRENTRY_NULL
);
1222 * grow the directory stream
1226 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1227 ULARGE_INTEGER newSize
;
1229 ULONG lastEntry
= 0;
1230 ULONG blockCount
= 0;
1233 * obtain the new count of blocks in the directory stream
1235 blockCount
= BlockChainStream_GetCount(
1236 storage
->rootBlockChain
)+1;
1239 * initialize the size used by the directory stream
1241 newSize
.u
.HighPart
= 0;
1242 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1245 * add a block to the directory stream
1247 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1250 * memset the empty entry in order to initialize the unused newly
1253 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1258 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1261 entryIndex
= newEntryIndex
+ 1;
1262 entryIndex
< lastEntry
;
1265 StorageImpl_WriteRawDirEntry(
1272 UpdateRawDirEntry(currentData
, newData
);
1274 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1277 *index
= newEntryIndex
;
1282 /***************************************************************************
1286 * Mark a directory entry in the file as free.
1288 static HRESULT
StorageImpl_DestroyDirEntry(
1289 StorageBaseImpl
*base
,
1293 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1294 StorageImpl
*storage
= (StorageImpl
*)base
;
1296 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1298 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1304 /***************************************************************************
1308 * Destroy an entry, its attached data, and all entries reachable from it.
1310 static HRESULT
DestroyReachableEntries(
1311 StorageBaseImpl
*base
,
1316 ULARGE_INTEGER zero
;
1320 if (index
!= DIRENTRY_NULL
)
1322 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1325 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1328 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1331 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1334 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1337 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1344 /****************************************************************************
1348 * Case insensitive comparison of DirEntry.name by first considering
1351 * Returns <0 when name1 < name2
1352 * >0 when name1 > name2
1353 * 0 when name1 == name2
1355 static LONG
entryNameCmp(
1356 const OLECHAR
*name1
,
1357 const OLECHAR
*name2
)
1359 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1361 while (diff
== 0 && *name1
!= 0)
1364 * We compare the string themselves only when they are of the same length
1366 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1372 /****************************************************************************
1376 * Add a directory entry to a storage
1378 static HRESULT
insertIntoTree(
1379 StorageBaseImpl
*This
,
1380 DirRef parentStorageIndex
,
1381 DirRef newEntryIndex
)
1383 DirEntry currentEntry
;
1387 * Read the inserted entry
1389 StorageBaseImpl_ReadDirEntry(This
,
1394 * Read the storage entry
1396 StorageBaseImpl_ReadDirEntry(This
,
1400 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1403 * The root storage contains some element, therefore, start the research
1404 * for the appropriate location.
1407 DirRef current
, next
, previous
, currentEntryId
;
1410 * Keep a reference to the root of the storage's element tree
1412 currentEntryId
= currentEntry
.dirRootEntry
;
1417 StorageBaseImpl_ReadDirEntry(This
,
1418 currentEntry
.dirRootEntry
,
1421 previous
= currentEntry
.leftChild
;
1422 next
= currentEntry
.rightChild
;
1423 current
= currentEntryId
;
1427 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1431 if (previous
!= DIRENTRY_NULL
)
1433 StorageBaseImpl_ReadDirEntry(This
,
1440 currentEntry
.leftChild
= newEntryIndex
;
1441 StorageBaseImpl_WriteDirEntry(This
,
1449 if (next
!= DIRENTRY_NULL
)
1451 StorageBaseImpl_ReadDirEntry(This
,
1458 currentEntry
.rightChild
= newEntryIndex
;
1459 StorageBaseImpl_WriteDirEntry(This
,
1468 * Trying to insert an item with the same name in the
1469 * subtree structure.
1471 return STG_E_FILEALREADYEXISTS
;
1474 previous
= currentEntry
.leftChild
;
1475 next
= currentEntry
.rightChild
;
1481 * The storage is empty, make the new entry the root of its element tree
1483 currentEntry
.dirRootEntry
= newEntryIndex
;
1484 StorageBaseImpl_WriteDirEntry(This
,
1492 /****************************************************************************
1496 * Find and read the element of a storage with the given name.
1498 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1499 const OLECHAR
*name
, DirEntry
*data
)
1501 DirRef currentEntry
;
1503 /* Read the storage entry to find the root of the tree. */
1504 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1506 currentEntry
= data
->dirRootEntry
;
1508 while (currentEntry
!= DIRENTRY_NULL
)
1512 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1514 cmp
= entryNameCmp(name
, data
->name
);
1521 currentEntry
= data
->leftChild
;
1524 currentEntry
= data
->rightChild
;
1527 return currentEntry
;
1530 /****************************************************************************
1534 * Find and read the binary tree parent of the element with the given name.
1536 * If there is no such element, find a place where it could be inserted and
1537 * return STG_E_FILENOTFOUND.
1539 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1540 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1549 *parentEntry
= storageEntry
;
1550 *relation
= DIRENTRY_RELATION_DIR
;
1552 childEntry
= parentData
->dirRootEntry
;
1554 while (childEntry
!= DIRENTRY_NULL
)
1558 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1560 cmp
= entryNameCmp(childName
, childData
.name
);
1568 *parentData
= childData
;
1569 *parentEntry
= childEntry
;
1570 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1572 childEntry
= parentData
->leftChild
;
1577 *parentData
= childData
;
1578 *parentEntry
= childEntry
;
1579 *relation
= DIRENTRY_RELATION_NEXT
;
1581 childEntry
= parentData
->rightChild
;
1585 if (childEntry
== DIRENTRY_NULL
)
1586 return STG_E_FILENOTFOUND
;
1592 /*************************************************************************
1595 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1597 DWORD ciidExclude
, /* [in] */
1598 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1599 SNB snbExclude
, /* [unique][in] */
1600 IStorage
* pstgDest
) /* [unique][in] */
1602 IEnumSTATSTG
*elements
= 0;
1603 STATSTG curElement
, strStat
;
1605 IStorage
*pstgTmp
, *pstgChild
;
1606 IStream
*pstrTmp
, *pstrChild
;
1607 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1610 TRACE("(%p, %d, %p, %p, %p)\n",
1611 iface
, ciidExclude
, rgiidExclude
,
1612 snbExclude
, pstgDest
);
1614 if ( pstgDest
== 0 )
1615 return STG_E_INVALIDPOINTER
;
1618 * Enumerate the elements
1620 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1628 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1629 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1631 for(i
= 0; i
< ciidExclude
; ++i
)
1633 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1634 skip_storage
= TRUE
;
1635 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1638 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1644 * Obtain the next element
1646 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1648 if ( hr
== S_FALSE
)
1650 hr
= S_OK
; /* done, every element has been copied */
1656 WCHAR
**snb
= snbExclude
;
1658 while ( *snb
!= NULL
&& !skip
)
1660 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1669 if (curElement
.type
== STGTY_STORAGE
)
1675 * open child source storage
1677 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1678 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1679 NULL
, 0, &pstgChild
);
1685 * create a new storage in destination storage
1687 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1688 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1692 * if it already exist, don't create a new one use this one
1694 if (hr
== STG_E_FILEALREADYEXISTS
)
1696 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1697 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1698 NULL
, 0, &pstgTmp
);
1704 * do the copy recursively
1706 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1709 IStorage_Release( pstgTmp
);
1712 IStorage_Release( pstgChild
);
1714 else if (curElement
.type
== STGTY_STREAM
)
1720 * create a new stream in destination storage. If the stream already
1721 * exist, it will be deleted and a new one will be created.
1723 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1724 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1731 * open child stream storage
1733 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1734 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1740 * Get the size of the source stream
1742 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1745 * Set the size of the destination stream.
1747 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1752 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1755 IStream_Release( pstrChild
);
1758 IStream_Release( pstrTmp
);
1762 WARN("unknown element type: %d\n", curElement
.type
);
1766 CoTaskMemFree(curElement
.pwcsName
);
1767 } while (hr
== S_OK
);
1772 IEnumSTATSTG_Release(elements
);
1777 /*************************************************************************
1778 * MoveElementTo (IStorage)
1780 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1782 const OLECHAR
*pwcsName
, /* [string][in] */
1783 IStorage
*pstgDest
, /* [unique][in] */
1784 const OLECHAR
*pwcsNewName
,/* [string][in] */
1785 DWORD grfFlags
) /* [in] */
1787 FIXME("(%p %s %p %s %u): stub\n", iface
,
1788 debugstr_w(pwcsName
), pstgDest
,
1789 debugstr_w(pwcsNewName
), grfFlags
);
1793 /*************************************************************************
1796 * Ensures that any changes made to a storage object open in transacted mode
1797 * are reflected in the parent storage
1800 * Wine doesn't implement transacted mode, which seems to be a basic
1801 * optimization, so we can ignore this stub for now.
1803 static HRESULT WINAPI
StorageImpl_Commit(
1805 DWORD grfCommitFlags
)/* [in] */
1807 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1811 /*************************************************************************
1814 * Discard all changes that have been made since the last commit operation
1816 static HRESULT WINAPI
StorageImpl_Revert(
1819 TRACE("(%p)\n", iface
);
1823 /*************************************************************************
1824 * DestroyElement (IStorage)
1826 * Strategy: This implementation is built this way for simplicity not for speed.
1827 * I always delete the topmost element of the enumeration and adjust
1828 * the deleted element pointer all the time. This takes longer to
1829 * do but allow to reinvoke DestroyElement whenever we encounter a
1830 * storage object. The optimisation resides in the usage of another
1831 * enumeration strategy that would give all the leaves of a storage
1832 * first. (postfix order)
1834 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1836 const OLECHAR
*pwcsName
)/* [string][in] */
1838 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1841 DirEntry entryToDelete
;
1842 DirRef entryToDeleteRef
;
1845 iface
, debugstr_w(pwcsName
));
1848 return STG_E_INVALIDPOINTER
;
1851 return STG_E_REVERTED
;
1853 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1854 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1855 return STG_E_ACCESSDENIED
;
1857 entryToDeleteRef
= findElement(
1859 This
->storageDirEntry
,
1863 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1865 return STG_E_FILENOTFOUND
;
1868 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1870 hr
= deleteStorageContents(
1875 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1877 hr
= deleteStreamContents(
1887 * Remove the entry from its parent storage
1889 hr
= removeFromTree(
1891 This
->storageDirEntry
,
1895 * Invalidate the entry
1898 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1904 /******************************************************************************
1905 * Internal stream list handlers
1908 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1910 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1911 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1914 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1916 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1917 list_remove(&(strm
->StrmListEntry
));
1920 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1922 StgStreamImpl
*strm
;
1924 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1926 if (strm
->dirEntry
== streamEntry
)
1935 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1937 StorageInternalImpl
*childstg
;
1939 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1941 if (childstg
->base
.storageDirEntry
== storageEntry
)
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1952 struct list
*cur
, *cur2
;
1953 StgStreamImpl
*strm
=NULL
;
1954 StorageInternalImpl
*childstg
=NULL
;
1956 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1957 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1958 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1959 strm
->parentStorage
= NULL
;
1963 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1964 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1965 StorageBaseImpl_Invalidate( &childstg
->base
);
1968 if (stg
->transactedChild
)
1970 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1972 stg
->transactedChild
= NULL
;
1977 /*********************************************************************
1981 * Delete the contents of a storage entry.
1984 static HRESULT
deleteStorageContents(
1985 StorageBaseImpl
*parentStorage
,
1986 DirRef indexToDelete
,
1987 DirEntry entryDataToDelete
)
1989 IEnumSTATSTG
*elements
= 0;
1990 IStorage
*childStorage
= 0;
1991 STATSTG currentElement
;
1993 HRESULT destroyHr
= S_OK
;
1994 StorageInternalImpl
*stg
, *stg2
;
1996 /* Invalidate any open storage objects. */
1997 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1999 if (stg
->base
.storageDirEntry
== indexToDelete
)
2001 StorageBaseImpl_Invalidate(&stg
->base
);
2006 * Open the storage and enumerate it
2008 hr
= StorageBaseImpl_OpenStorage(
2009 (IStorage
*)parentStorage
,
2010 entryDataToDelete
.name
,
2012 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2023 * Enumerate the elements
2025 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2030 * Obtain the next element
2032 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2035 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2037 CoTaskMemFree(currentElement
.pwcsName
);
2041 * We need to Reset the enumeration every time because we delete elements
2042 * and the enumeration could be invalid
2044 IEnumSTATSTG_Reset(elements
);
2046 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2048 IStorage_Release(childStorage
);
2049 IEnumSTATSTG_Release(elements
);
2054 /*********************************************************************
2058 * Perform the deletion of a stream's data
2061 static HRESULT
deleteStreamContents(
2062 StorageBaseImpl
*parentStorage
,
2063 DirRef indexToDelete
,
2064 DirEntry entryDataToDelete
)
2068 ULARGE_INTEGER size
;
2069 StgStreamImpl
*strm
, *strm2
;
2071 /* Invalidate any open stream objects. */
2072 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2074 if (strm
->dirEntry
== indexToDelete
)
2076 TRACE("Stream deleted %p\n", strm
);
2077 strm
->parentStorage
= NULL
;
2078 list_remove(&strm
->StrmListEntry
);
2082 size
.u
.HighPart
= 0;
2085 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2086 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2096 hr
= IStream_SetSize(pis
, size
);
2104 * Release the stream object.
2106 IStream_Release(pis
);
2111 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2115 case DIRENTRY_RELATION_PREVIOUS
:
2116 entry
->leftChild
= new_target
;
2118 case DIRENTRY_RELATION_NEXT
:
2119 entry
->rightChild
= new_target
;
2121 case DIRENTRY_RELATION_DIR
:
2122 entry
->dirRootEntry
= new_target
;
2129 /*************************************************************************
2133 * This method removes a directory entry from its parent storage tree without
2134 * freeing any resources attached to it.
2136 static HRESULT
removeFromTree(
2137 StorageBaseImpl
*This
,
2138 DirRef parentStorageIndex
,
2139 DirRef deletedIndex
)
2142 DirEntry entryToDelete
;
2143 DirEntry parentEntry
;
2144 DirRef parentEntryRef
;
2145 ULONG typeOfRelation
;
2147 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2153 * Find the element that links to the one we want to delete.
2155 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2156 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2161 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2164 * Replace the deleted entry with its left child
2166 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2168 hr
= StorageBaseImpl_WriteDirEntry(
2177 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2180 * We need to reinsert the right child somewhere. We already know it and
2181 * its children are greater than everything in the left tree, so we
2182 * insert it at the rightmost point in the left tree.
2184 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2185 DirEntry newRightChildParentEntry
;
2189 hr
= StorageBaseImpl_ReadDirEntry(
2191 newRightChildParent
,
2192 &newRightChildParentEntry
);
2198 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2199 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2200 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2202 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2204 hr
= StorageBaseImpl_WriteDirEntry(
2206 newRightChildParent
,
2207 &newRightChildParentEntry
);
2217 * Replace the deleted entry with its right child
2219 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2221 hr
= StorageBaseImpl_WriteDirEntry(
2235 /******************************************************************************
2236 * SetElementTimes (IStorage)
2238 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2240 const OLECHAR
*pwcsName
,/* [string][in] */
2241 const FILETIME
*pctime
, /* [in] */
2242 const FILETIME
*patime
, /* [in] */
2243 const FILETIME
*pmtime
) /* [in] */
2245 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2249 /******************************************************************************
2250 * SetStateBits (IStorage)
2252 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2254 DWORD grfStateBits
,/* [in] */
2255 DWORD grfMask
) /* [in] */
2257 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2260 return STG_E_REVERTED
;
2262 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2266 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2267 DirRef index
, const DirEntry
*data
)
2269 StorageImpl
*This
= (StorageImpl
*)base
;
2270 return StorageImpl_WriteDirEntry(This
, index
, data
);
2273 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2274 DirRef index
, DirEntry
*data
)
2276 StorageImpl
*This
= (StorageImpl
*)base
;
2277 return StorageImpl_ReadDirEntry(This
, index
, data
);
2280 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2284 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2286 if (!This
->blockChainCache
[i
])
2288 return &This
->blockChainCache
[i
];
2292 i
= This
->blockChainToEvict
;
2294 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2295 This
->blockChainCache
[i
] = NULL
;
2297 This
->blockChainToEvict
++;
2298 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2299 This
->blockChainToEvict
= 0;
2301 return &This
->blockChainCache
[i
];
2304 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2307 int i
, free_index
=-1;
2309 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2311 if (!This
->blockChainCache
[i
])
2313 if (free_index
== -1) free_index
= i
;
2315 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2317 return &This
->blockChainCache
[i
];
2321 if (free_index
== -1)
2323 free_index
= This
->blockChainToEvict
;
2325 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2326 This
->blockChainCache
[free_index
] = NULL
;
2328 This
->blockChainToEvict
++;
2329 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2330 This
->blockChainToEvict
= 0;
2333 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2334 return &This
->blockChainCache
[free_index
];
2337 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2338 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2340 StorageImpl
*This
= (StorageImpl
*)base
;
2345 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2346 if (FAILED(hr
)) return hr
;
2348 if (data
.size
.QuadPart
== 0)
2354 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2356 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2363 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2365 SmallBlockChainStream
*stream
;
2367 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2368 if (!stream
) return E_OUTOFMEMORY
;
2370 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2372 SmallBlockChainStream_Destroy(stream
);
2378 BlockChainStream
*stream
= NULL
;
2380 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2381 if (!stream
) return E_OUTOFMEMORY
;
2383 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2389 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2390 ULARGE_INTEGER newsize
)
2392 StorageImpl
*This
= (StorageImpl
*)base
;
2395 SmallBlockChainStream
*smallblock
=NULL
;
2396 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2398 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2399 if (FAILED(hr
)) return hr
;
2401 /* In simple mode keep the stream size above the small block limit */
2402 if (This
->base
.openFlags
& STGM_SIMPLE
)
2403 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2405 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2408 /* Create a block chain object of the appropriate type */
2409 if (data
.size
.QuadPart
== 0)
2411 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2413 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2414 if (!smallblock
) return E_OUTOFMEMORY
;
2418 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2419 bigblock
= *pbigblock
;
2420 if (!bigblock
) return E_OUTOFMEMORY
;
2423 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2425 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2426 if (!smallblock
) return E_OUTOFMEMORY
;
2430 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2431 bigblock
= *pbigblock
;
2432 if (!bigblock
) return E_OUTOFMEMORY
;
2435 /* Change the block chain type if necessary. */
2436 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2438 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2441 SmallBlockChainStream_Destroy(smallblock
);
2445 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2446 *pbigblock
= bigblock
;
2448 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2450 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2455 /* Set the size of the block chain. */
2458 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2459 SmallBlockChainStream_Destroy(smallblock
);
2463 BlockChainStream_SetSize(bigblock
, newsize
);
2466 /* Set the size in the directory entry. */
2467 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2470 data
.size
= newsize
;
2472 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2477 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2478 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2480 StorageImpl
*This
= (StorageImpl
*)base
;
2483 ULARGE_INTEGER newSize
;
2485 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2486 if (FAILED(hr
)) return hr
;
2488 /* Grow the stream if necessary */
2489 newSize
.QuadPart
= 0;
2490 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2492 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2494 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2498 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2499 if (FAILED(hr
)) return hr
;
2502 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2504 SmallBlockChainStream
*stream
;
2506 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2507 if (!stream
) return E_OUTOFMEMORY
;
2509 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2511 SmallBlockChainStream_Destroy(stream
);
2517 BlockChainStream
*stream
;
2519 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2520 if (!stream
) return E_OUTOFMEMORY
;
2522 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2529 * Virtual function table for the IStorage32Impl class.
2531 static const IStorageVtbl Storage32Impl_Vtbl
=
2533 StorageBaseImpl_QueryInterface
,
2534 StorageBaseImpl_AddRef
,
2535 StorageBaseImpl_Release
,
2536 StorageBaseImpl_CreateStream
,
2537 StorageBaseImpl_OpenStream
,
2538 StorageBaseImpl_CreateStorage
,
2539 StorageBaseImpl_OpenStorage
,
2540 StorageBaseImpl_CopyTo
,
2541 StorageBaseImpl_MoveElementTo
,
2544 StorageBaseImpl_EnumElements
,
2545 StorageBaseImpl_DestroyElement
,
2546 StorageBaseImpl_RenameElement
,
2547 StorageBaseImpl_SetElementTimes
,
2548 StorageBaseImpl_SetClass
,
2549 StorageBaseImpl_SetStateBits
,
2550 StorageBaseImpl_Stat
2553 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2555 StorageImpl_Destroy
,
2556 StorageImpl_Invalidate
,
2557 StorageImpl_CreateDirEntry
,
2558 StorageImpl_BaseWriteDirEntry
,
2559 StorageImpl_BaseReadDirEntry
,
2560 StorageImpl_DestroyDirEntry
,
2561 StorageImpl_StreamReadAt
,
2562 StorageImpl_StreamWriteAt
,
2563 StorageImpl_StreamSetSize
2566 static HRESULT
StorageImpl_Construct(
2573 StorageImpl
** result
)
2577 DirEntry currentEntry
;
2578 DirRef currentEntryRef
;
2579 WCHAR fullpath
[MAX_PATH
];
2581 if ( FAILED( validateSTGM(openFlags
) ))
2582 return STG_E_INVALIDFLAG
;
2584 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2586 return E_OUTOFMEMORY
;
2588 memset(This
, 0, sizeof(StorageImpl
));
2590 list_init(&This
->base
.strmHead
);
2592 list_init(&This
->base
.storageHead
);
2594 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2595 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2596 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2597 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2599 This
->base
.create
= create
;
2601 This
->base
.reverted
= 0;
2603 This
->hFile
= hFile
;
2606 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2608 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2610 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2611 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2612 if (!This
->pwcsName
)
2614 hr
= STG_E_INSUFFICIENTMEMORY
;
2617 strcpyW(This
->pwcsName
, fullpath
);
2618 This
->base
.filename
= This
->pwcsName
;
2622 * Initialize the big block cache.
2624 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2625 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2626 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2632 if (This
->bigBlockFile
== 0)
2640 ULARGE_INTEGER size
;
2641 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2644 * Initialize all header variables:
2645 * - The big block depot consists of one block and it is at block 0
2646 * - The directory table starts at block 1
2647 * - There is no small block depot
2649 memset( This
->bigBlockDepotStart
,
2651 sizeof(This
->bigBlockDepotStart
));
2653 This
->bigBlockDepotCount
= 1;
2654 This
->bigBlockDepotStart
[0] = 0;
2655 This
->rootStartBlock
= 1;
2656 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2657 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2658 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2659 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2660 This
->extBigBlockDepotCount
= 0;
2662 StorageImpl_SaveFileHeader(This
);
2665 * Add one block for the big block depot and one block for the directory table
2667 size
.u
.HighPart
= 0;
2668 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2669 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2672 * Initialize the big block depot
2674 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2675 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2676 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2677 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2682 * Load the header for the file.
2684 hr
= StorageImpl_LoadFileHeader(This
);
2693 * There is no block depot cached yet.
2695 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2698 * Start searching for free blocks with block 0.
2700 This
->prevFreeBlock
= 0;
2703 * Create the block chain abstractions.
2705 if(!(This
->rootBlockChain
=
2706 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2708 hr
= STG_E_READFAULT
;
2712 if(!(This
->smallBlockDepotChain
=
2713 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2716 hr
= STG_E_READFAULT
;
2721 * Write the root storage entry (memory only)
2727 * Initialize the directory table
2729 memset(&rootEntry
, 0, sizeof(rootEntry
));
2730 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2731 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2732 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2733 rootEntry
.stgType
= STGTY_ROOT
;
2734 rootEntry
.leftChild
= DIRENTRY_NULL
;
2735 rootEntry
.rightChild
= DIRENTRY_NULL
;
2736 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2737 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2738 rootEntry
.size
.u
.HighPart
= 0;
2739 rootEntry
.size
.u
.LowPart
= 0;
2741 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2745 * Find the ID of the root storage.
2747 currentEntryRef
= 0;
2751 hr
= StorageImpl_ReadDirEntry(
2758 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2759 (currentEntry
.stgType
== STGTY_ROOT
) )
2761 This
->base
.storageDirEntry
= currentEntryRef
;
2767 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2771 hr
= STG_E_READFAULT
;
2776 * Create the block chain abstraction for the small block root chain.
2778 if(!(This
->smallBlockRootChain
=
2779 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2781 hr
= STG_E_READFAULT
;
2787 IStorage_Release((IStorage
*)This
);
2796 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2798 StorageImpl
*This
= (StorageImpl
*) iface
;
2800 StorageBaseImpl_DeleteAll(&This
->base
);
2802 This
->base
.reverted
= 1;
2805 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2807 StorageImpl
*This
= (StorageImpl
*) iface
;
2809 TRACE("(%p)\n", This
);
2811 StorageImpl_Invalidate(iface
);
2813 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2815 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2816 BlockChainStream_Destroy(This
->rootBlockChain
);
2817 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2819 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2820 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2822 if (This
->bigBlockFile
)
2823 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2824 HeapFree(GetProcessHeap(), 0, This
);
2827 /******************************************************************************
2828 * Storage32Impl_GetNextFreeBigBlock
2830 * Returns the index of the next free big block.
2831 * If the big block depot is filled, this method will enlarge it.
2834 static ULONG
StorageImpl_GetNextFreeBigBlock(
2837 ULONG depotBlockIndexPos
;
2838 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2840 ULONG depotBlockOffset
;
2841 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2842 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2844 ULONG freeBlock
= BLOCK_UNUSED
;
2846 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2847 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2850 * Scan the entire big block depot until we find a block marked free
2852 while (nextBlockIndex
!= BLOCK_UNUSED
)
2854 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2856 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2859 * Grow the primary depot.
2861 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2863 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2866 * Add a block depot.
2868 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2869 This
->bigBlockDepotCount
++;
2870 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2873 * Flag it as a block depot.
2875 StorageImpl_SetNextBlockInChain(This
,
2879 /* Save new header information.
2881 StorageImpl_SaveFileHeader(This
);
2886 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2888 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2891 * Grow the extended depot.
2893 ULONG extIndex
= BLOCK_UNUSED
;
2894 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2895 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2897 if (extBlockOffset
== 0)
2899 /* We need an extended block.
2901 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2902 This
->extBigBlockDepotCount
++;
2903 depotBlockIndexPos
= extIndex
+ 1;
2906 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2909 * Add a block depot and mark it in the extended block.
2911 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2912 This
->bigBlockDepotCount
++;
2913 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2915 /* Flag the block depot.
2917 StorageImpl_SetNextBlockInChain(This
,
2921 /* If necessary, flag the extended depot block.
2923 if (extIndex
!= BLOCK_UNUSED
)
2924 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2926 /* Save header information.
2928 StorageImpl_SaveFileHeader(This
);
2932 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2936 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2937 ( nextBlockIndex
!= BLOCK_UNUSED
))
2939 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2941 if (nextBlockIndex
== BLOCK_UNUSED
)
2943 freeBlock
= (depotIndex
* blocksPerDepot
) +
2944 (depotBlockOffset
/sizeof(ULONG
));
2947 depotBlockOffset
+= sizeof(ULONG
);
2952 depotBlockOffset
= 0;
2956 * make sure that the block physically exists before using it
2958 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2960 This
->prevFreeBlock
= freeBlock
;
2965 /******************************************************************************
2966 * Storage32Impl_AddBlockDepot
2968 * This will create a depot block, essentially it is a block initialized
2971 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2973 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2976 * Initialize blocks as free
2978 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2979 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2982 /******************************************************************************
2983 * Storage32Impl_GetExtDepotBlock
2985 * Returns the index of the block that corresponds to the specified depot
2986 * index. This method is only for depot indexes equal or greater than
2987 * COUNT_BBDEPOTINHEADER.
2989 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2991 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2992 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2993 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2994 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2995 ULONG blockIndex
= BLOCK_UNUSED
;
2996 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2998 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3000 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3001 return BLOCK_UNUSED
;
3003 while (extBlockCount
> 0)
3005 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3009 if (extBlockIndex
!= BLOCK_UNUSED
)
3010 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3011 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3016 /******************************************************************************
3017 * Storage32Impl_SetExtDepotBlock
3019 * Associates the specified block index to the specified depot index.
3020 * This method is only for depot indexes equal or greater than
3021 * COUNT_BBDEPOTINHEADER.
3023 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3025 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3026 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3027 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3028 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3029 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3031 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3033 while (extBlockCount
> 0)
3035 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3039 if (extBlockIndex
!= BLOCK_UNUSED
)
3041 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3042 extBlockOffset
* sizeof(ULONG
),
3047 /******************************************************************************
3048 * Storage32Impl_AddExtBlockDepot
3050 * Creates an extended depot block.
3052 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3054 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3055 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3056 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3057 ULONG index
= BLOCK_UNUSED
;
3058 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3059 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3060 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3062 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3063 blocksPerDepotBlock
;
3065 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3068 * The first extended block.
3070 This
->extBigBlockDepotStart
= index
;
3076 * Follow the chain to the last one.
3078 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3080 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3084 * Add the new extended block to the chain.
3086 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3091 * Initialize this block.
3093 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3094 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3099 /******************************************************************************
3100 * Storage32Impl_FreeBigBlock
3102 * This method will flag the specified block as free in the big block depot.
3104 static void StorageImpl_FreeBigBlock(
3108 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3110 if (blockIndex
< This
->prevFreeBlock
)
3111 This
->prevFreeBlock
= blockIndex
;
3114 /************************************************************************
3115 * Storage32Impl_GetNextBlockInChain
3117 * This method will retrieve the block index of the next big block in
3120 * Params: This - Pointer to the Storage object.
3121 * blockIndex - Index of the block to retrieve the chain
3123 * nextBlockIndex - receives the return value.
3125 * Returns: This method returns the index of the next block in the chain.
3126 * It will return the constants:
3127 * BLOCK_SPECIAL - If the block given was not part of a
3129 * BLOCK_END_OF_CHAIN - If the block given was the last in
3131 * BLOCK_UNUSED - If the block given was not past of a chain
3133 * BLOCK_EXTBBDEPOT - This block is part of the extended
3136 * See Windows documentation for more details on IStorage methods.
3138 static HRESULT
StorageImpl_GetNextBlockInChain(
3141 ULONG
* nextBlockIndex
)
3143 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3144 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3145 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3146 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3148 ULONG depotBlockIndexPos
;
3151 *nextBlockIndex
= BLOCK_SPECIAL
;
3153 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3155 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3156 This
->bigBlockDepotCount
);
3157 return STG_E_READFAULT
;
3161 * Cache the currently accessed depot block.
3163 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3165 This
->indexBlockDepotCached
= depotBlockCount
;
3167 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3169 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3174 * We have to look in the extended depot.
3176 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3179 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3182 return STG_E_READFAULT
;
3184 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3186 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3187 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3191 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3196 /******************************************************************************
3197 * Storage32Impl_GetNextExtendedBlock
3199 * Given an extended block this method will return the next extended block.
3202 * The last ULONG of an extended block is the block index of the next
3203 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3207 * - The index of the next extended block
3208 * - BLOCK_UNUSED: there is no next extended block.
3209 * - Any other return values denotes failure.
3211 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3213 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3214 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3216 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3219 return nextBlockIndex
;
3222 /******************************************************************************
3223 * Storage32Impl_SetNextBlockInChain
3225 * This method will write the index of the specified block's next block
3226 * in the big block depot.
3228 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3231 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3232 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3233 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3236 static void StorageImpl_SetNextBlockInChain(
3241 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3242 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3243 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3244 ULONG depotBlockIndexPos
;
3246 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3247 assert(blockIndex
!= nextBlock
);
3249 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3251 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3256 * We have to look in the extended depot.
3258 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3261 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3264 * Update the cached block depot, if necessary.
3266 if (depotBlockCount
== This
->indexBlockDepotCached
)
3268 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3272 /******************************************************************************
3273 * Storage32Impl_LoadFileHeader
3275 * This method will read in the file header, i.e. big block index -1.
3277 static HRESULT
StorageImpl_LoadFileHeader(
3280 HRESULT hr
= STG_E_FILENOTFOUND
;
3281 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3287 * Get a pointer to the big block of data containing the header.
3289 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3292 * Extract the information from the header.
3297 * Check for the "magic number" signature and return an error if it is not
3300 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3302 return STG_E_OLDFORMAT
;
3305 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3307 return STG_E_INVALIDHEADER
;
3310 StorageUtl_ReadWord(
3312 OFFSET_BIGBLOCKSIZEBITS
,
3313 &This
->bigBlockSizeBits
);
3315 StorageUtl_ReadWord(
3317 OFFSET_SMALLBLOCKSIZEBITS
,
3318 &This
->smallBlockSizeBits
);
3320 StorageUtl_ReadDWord(
3322 OFFSET_BBDEPOTCOUNT
,
3323 &This
->bigBlockDepotCount
);
3325 StorageUtl_ReadDWord(
3327 OFFSET_ROOTSTARTBLOCK
,
3328 &This
->rootStartBlock
);
3330 StorageUtl_ReadDWord(
3332 OFFSET_SBDEPOTSTART
,
3333 &This
->smallBlockDepotStart
);
3335 StorageUtl_ReadDWord(
3337 OFFSET_EXTBBDEPOTSTART
,
3338 &This
->extBigBlockDepotStart
);
3340 StorageUtl_ReadDWord(
3342 OFFSET_EXTBBDEPOTCOUNT
,
3343 &This
->extBigBlockDepotCount
);
3345 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3347 StorageUtl_ReadDWord(
3349 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3350 &(This
->bigBlockDepotStart
[index
]));
3354 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3356 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3357 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3360 * Right now, the code is making some assumptions about the size of the
3361 * blocks, just make sure they are what we're expecting.
3363 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3364 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3366 WARN("Broken OLE storage file\n");
3367 hr
= STG_E_INVALIDHEADER
;
3376 /******************************************************************************
3377 * Storage32Impl_SaveFileHeader
3379 * This method will save to the file the header, i.e. big block -1.
3381 static void StorageImpl_SaveFileHeader(
3384 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3389 * Get a pointer to the big block of data containing the header.
3391 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3394 * If the block read failed, the file is probably new.
3399 * Initialize for all unknown fields.
3401 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3404 * Initialize the magic number.
3406 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3409 * And a bunch of things we don't know what they mean
3411 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3412 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3413 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3414 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3418 * Write the information to the header.
3420 StorageUtl_WriteWord(
3422 OFFSET_BIGBLOCKSIZEBITS
,
3423 This
->bigBlockSizeBits
);
3425 StorageUtl_WriteWord(
3427 OFFSET_SMALLBLOCKSIZEBITS
,
3428 This
->smallBlockSizeBits
);
3430 StorageUtl_WriteDWord(
3432 OFFSET_BBDEPOTCOUNT
,
3433 This
->bigBlockDepotCount
);
3435 StorageUtl_WriteDWord(
3437 OFFSET_ROOTSTARTBLOCK
,
3438 This
->rootStartBlock
);
3440 StorageUtl_WriteDWord(
3442 OFFSET_SBDEPOTSTART
,
3443 This
->smallBlockDepotStart
);
3445 StorageUtl_WriteDWord(
3447 OFFSET_SBDEPOTCOUNT
,
3448 This
->smallBlockDepotChain
?
3449 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3451 StorageUtl_WriteDWord(
3453 OFFSET_EXTBBDEPOTSTART
,
3454 This
->extBigBlockDepotStart
);
3456 StorageUtl_WriteDWord(
3458 OFFSET_EXTBBDEPOTCOUNT
,
3459 This
->extBigBlockDepotCount
);
3461 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3463 StorageUtl_WriteDWord(
3465 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3466 (This
->bigBlockDepotStart
[index
]));
3470 * Write the big block back to the file.
3472 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3475 /******************************************************************************
3476 * StorageImpl_ReadRawDirEntry
3478 * This method will read the raw data from a directory entry in the file.
3480 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3482 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3484 ULARGE_INTEGER offset
;
3488 offset
.u
.HighPart
= 0;
3489 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3491 hr
= BlockChainStream_ReadAt(
3492 This
->rootBlockChain
,
3501 /******************************************************************************
3502 * StorageImpl_WriteRawDirEntry
3504 * This method will write the raw data from a directory entry in the file.
3506 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3508 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3510 ULARGE_INTEGER offset
;
3514 offset
.u
.HighPart
= 0;
3515 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3517 hr
= BlockChainStream_WriteAt(
3518 This
->rootBlockChain
,
3527 /******************************************************************************
3530 * Update raw directory entry data from the fields in newData.
3532 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3534 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3536 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3539 buffer
+ OFFSET_PS_NAME
,
3541 DIRENTRY_NAME_BUFFER_LEN
);
3543 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3545 StorageUtl_WriteWord(
3547 OFFSET_PS_NAMELENGTH
,
3548 newData
->sizeOfNameString
);
3550 StorageUtl_WriteDWord(
3552 OFFSET_PS_LEFTCHILD
,
3553 newData
->leftChild
);
3555 StorageUtl_WriteDWord(
3557 OFFSET_PS_RIGHTCHILD
,
3558 newData
->rightChild
);
3560 StorageUtl_WriteDWord(
3563 newData
->dirRootEntry
);
3565 StorageUtl_WriteGUID(
3570 StorageUtl_WriteDWord(
3573 newData
->ctime
.dwLowDateTime
);
3575 StorageUtl_WriteDWord(
3577 OFFSET_PS_CTIMEHIGH
,
3578 newData
->ctime
.dwHighDateTime
);
3580 StorageUtl_WriteDWord(
3583 newData
->mtime
.dwLowDateTime
);
3585 StorageUtl_WriteDWord(
3587 OFFSET_PS_MTIMEHIGH
,
3588 newData
->ctime
.dwHighDateTime
);
3590 StorageUtl_WriteDWord(
3592 OFFSET_PS_STARTBLOCK
,
3593 newData
->startingBlock
);
3595 StorageUtl_WriteDWord(
3598 newData
->size
.u
.LowPart
);
3601 /******************************************************************************
3602 * Storage32Impl_ReadDirEntry
3604 * This method will read the specified directory entry.
3606 HRESULT
StorageImpl_ReadDirEntry(
3611 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3614 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3616 if (SUCCEEDED(readRes
))
3618 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3621 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3622 DIRENTRY_NAME_BUFFER_LEN
);
3623 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3625 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3627 StorageUtl_ReadWord(
3629 OFFSET_PS_NAMELENGTH
,
3630 &buffer
->sizeOfNameString
);
3632 StorageUtl_ReadDWord(
3634 OFFSET_PS_LEFTCHILD
,
3635 &buffer
->leftChild
);
3637 StorageUtl_ReadDWord(
3639 OFFSET_PS_RIGHTCHILD
,
3640 &buffer
->rightChild
);
3642 StorageUtl_ReadDWord(
3645 &buffer
->dirRootEntry
);
3647 StorageUtl_ReadGUID(
3652 StorageUtl_ReadDWord(
3655 &buffer
->ctime
.dwLowDateTime
);
3657 StorageUtl_ReadDWord(
3659 OFFSET_PS_CTIMEHIGH
,
3660 &buffer
->ctime
.dwHighDateTime
);
3662 StorageUtl_ReadDWord(
3665 &buffer
->mtime
.dwLowDateTime
);
3667 StorageUtl_ReadDWord(
3669 OFFSET_PS_MTIMEHIGH
,
3670 &buffer
->mtime
.dwHighDateTime
);
3672 StorageUtl_ReadDWord(
3674 OFFSET_PS_STARTBLOCK
,
3675 &buffer
->startingBlock
);
3677 StorageUtl_ReadDWord(
3680 &buffer
->size
.u
.LowPart
);
3682 buffer
->size
.u
.HighPart
= 0;
3688 /*********************************************************************
3689 * Write the specified directory entry to the file
3691 HRESULT
StorageImpl_WriteDirEntry(
3694 const DirEntry
* buffer
)
3696 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3699 UpdateRawDirEntry(currentEntry
, buffer
);
3701 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3705 static BOOL
StorageImpl_ReadBigBlock(
3710 ULARGE_INTEGER ulOffset
;
3713 ulOffset
.u
.HighPart
= 0;
3714 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3716 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3717 return (read
== This
->bigBlockSize
);
3720 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3726 ULARGE_INTEGER ulOffset
;
3730 ulOffset
.u
.HighPart
= 0;
3731 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3732 ulOffset
.u
.LowPart
+= offset
;
3734 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3735 *value
= lendian32toh(tmp
);
3736 return (read
== sizeof(DWORD
));
3739 static BOOL
StorageImpl_WriteBigBlock(
3744 ULARGE_INTEGER ulOffset
;
3747 ulOffset
.u
.HighPart
= 0;
3748 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3750 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3751 return (wrote
== This
->bigBlockSize
);
3754 static BOOL
StorageImpl_WriteDWordToBigBlock(
3760 ULARGE_INTEGER ulOffset
;
3763 ulOffset
.u
.HighPart
= 0;
3764 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3765 ulOffset
.u
.LowPart
+= offset
;
3767 value
= htole32(value
);
3768 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3769 return (wrote
== sizeof(DWORD
));
3772 /******************************************************************************
3773 * Storage32Impl_SmallBlocksToBigBlocks
3775 * This method will convert a small block chain to a big block chain.
3776 * The small block chain will be destroyed.
3778 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3780 SmallBlockChainStream
** ppsbChain
)
3782 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3783 ULARGE_INTEGER size
, offset
;
3784 ULONG cbRead
, cbWritten
;
3785 ULARGE_INTEGER cbTotalRead
;
3786 DirRef streamEntryRef
;
3787 HRESULT resWrite
= S_OK
;
3789 DirEntry streamEntry
;
3791 BlockChainStream
*bbTempChain
= NULL
;
3792 BlockChainStream
*bigBlockChain
= NULL
;
3795 * Create a temporary big block chain that doesn't have
3796 * an associated directory entry. This temporary chain will be
3797 * used to copy data from small blocks to big blocks.
3799 bbTempChain
= BlockChainStream_Construct(This
,
3802 if(!bbTempChain
) return NULL
;
3804 * Grow the big block chain.
3806 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3807 BlockChainStream_SetSize(bbTempChain
, size
);
3810 * Copy the contents of the small block chain to the big block chain
3811 * by small block size increments.
3813 offset
.u
.LowPart
= 0;
3814 offset
.u
.HighPart
= 0;
3815 cbTotalRead
.QuadPart
= 0;
3817 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3820 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3822 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3825 if (FAILED(resRead
))
3830 cbTotalRead
.QuadPart
+= cbRead
;
3832 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3838 if (FAILED(resWrite
))
3841 offset
.u
.LowPart
+= cbRead
;
3843 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3844 HeapFree(GetProcessHeap(),0,buffer
);
3846 size
.u
.HighPart
= 0;
3849 if (FAILED(resRead
) || FAILED(resWrite
))
3851 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3852 BlockChainStream_SetSize(bbTempChain
, size
);
3853 BlockChainStream_Destroy(bbTempChain
);
3858 * Destroy the small block chain.
3860 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3861 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3862 SmallBlockChainStream_Destroy(*ppsbChain
);
3866 * Change the directory entry. This chain is now a big block chain
3867 * and it doesn't reside in the small blocks chain anymore.
3869 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3871 streamEntry
.startingBlock
= bbHeadOfChain
;
3873 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3876 * Destroy the temporary entryless big block chain.
3877 * Create a new big block chain associated with this entry.
3879 BlockChainStream_Destroy(bbTempChain
);
3880 bigBlockChain
= BlockChainStream_Construct(This
,
3884 return bigBlockChain
;
3887 /******************************************************************************
3888 * Storage32Impl_BigBlocksToSmallBlocks
3890 * This method will convert a big block chain to a small block chain.
3891 * The big block chain will be destroyed on success.
3893 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3895 BlockChainStream
** ppbbChain
)
3897 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3898 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3899 DirRef streamEntryRef
;
3900 HRESULT resWrite
= S_OK
, resRead
;
3901 DirEntry streamEntry
;
3903 SmallBlockChainStream
* sbTempChain
;
3905 TRACE("%p %p\n", This
, ppbbChain
);
3907 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3913 size
= BlockChainStream_GetSize(*ppbbChain
);
3914 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3916 offset
.u
.HighPart
= 0;
3917 offset
.u
.LowPart
= 0;
3918 cbTotalRead
.QuadPart
= 0;
3919 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3922 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3923 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3931 cbTotalRead
.QuadPart
+= cbRead
;
3933 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3934 cbRead
, buffer
, &cbWritten
);
3936 if(FAILED(resWrite
))
3939 offset
.u
.LowPart
+= cbRead
;
3941 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3942 HeapFree(GetProcessHeap(), 0, buffer
);
3944 size
.u
.HighPart
= 0;
3947 if(FAILED(resRead
) || FAILED(resWrite
))
3949 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3950 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3951 SmallBlockChainStream_Destroy(sbTempChain
);
3955 /* destroy the original big block chain */
3956 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3957 BlockChainStream_SetSize(*ppbbChain
, size
);
3958 BlockChainStream_Destroy(*ppbbChain
);
3961 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3962 streamEntry
.startingBlock
= sbHeadOfChain
;
3963 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3965 SmallBlockChainStream_Destroy(sbTempChain
);
3966 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3969 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3972 DirEntry parentData
, snapshotData
;
3974 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3975 0, (IStorage
**)snapshot
);
3979 hr
= StorageBaseImpl_ReadDirEntry(original
,
3980 original
->storageDirEntry
, &parentData
);
3983 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3984 (*snapshot
)->storageDirEntry
, &snapshotData
);
3988 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3989 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3990 snapshotData
.stgType
= parentData
.stgType
;
3991 snapshotData
.clsid
= parentData
.clsid
;
3992 snapshotData
.ctime
= parentData
.ctime
;
3993 snapshotData
.mtime
= parentData
.mtime
;
3994 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3995 (*snapshot
)->storageDirEntry
, &snapshotData
);
3999 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
4000 (IStorage
*)(*snapshot
));
4002 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
4008 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4010 DWORD grfCommitFlags
) /* [in] */
4012 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4014 DirEntry data
, tempStorageData
, snapshotRootData
;
4015 DirRef tempStorageEntry
, oldDirRoot
;
4016 StorageInternalImpl
*tempStorage
;
4018 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4020 /* Cannot commit a read-only transacted storage */
4021 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4022 return STG_E_ACCESSDENIED
;
4024 /* To prevent data loss, we create the new structure in the file before we
4025 * delete the old one, so that in case of errors the old data is intact. We
4026 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4027 * needed in the rare situation where we have just enough free disk space to
4028 * overwrite the existing data. */
4030 /* Create an orphaned storage in the parent for the new directory structure. */
4031 memset(&data
, 0, sizeof(data
));
4033 data
.sizeOfNameString
= 1;
4034 data
.stgType
= STGTY_STORAGE
;
4035 data
.leftChild
= DIRENTRY_NULL
;
4036 data
.rightChild
= DIRENTRY_NULL
;
4037 data
.dirRootEntry
= DIRENTRY_NULL
;
4038 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4040 if (FAILED(hr
)) return hr
;
4042 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4043 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4046 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4047 (IStorage
*)tempStorage
);
4049 list_init(&tempStorage
->ParentListEntry
);
4051 IStorage_Release((IStorage
*) tempStorage
);
4058 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4062 /* Update the storage to use the new data in one step. */
4063 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4064 This
->transactedParent
->storageDirEntry
, &data
);
4068 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4069 tempStorageEntry
, &tempStorageData
);
4074 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4075 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4080 oldDirRoot
= data
.dirRootEntry
;
4081 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4082 data
.clsid
= snapshotRootData
.clsid
;
4083 data
.ctime
= snapshotRootData
.ctime
;
4084 data
.mtime
= snapshotRootData
.mtime
;
4086 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4087 This
->transactedParent
->storageDirEntry
, &data
);
4092 /* Destroy the old now-orphaned data. */
4093 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4094 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4098 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4104 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4107 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4108 StorageBaseImpl
*newSnapshot
;
4111 TRACE("(%p)\n", iface
);
4113 /* Create a new copy of the parent data. */
4114 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4115 if (FAILED(hr
)) return hr
;
4117 /* Destroy the open objects. */
4118 StorageBaseImpl_DeleteAll(&This
->base
);
4120 /* Replace our current snapshot. */
4121 IStorage_Release((IStorage
*)This
->snapshot
);
4122 This
->snapshot
= newSnapshot
;
4127 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4129 if (!This
->reverted
)
4131 TRACE("Storage invalidated (stg=%p)\n", This
);
4135 StorageBaseImpl_DeleteAll(This
);
4139 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4141 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4143 TransactedSnapshotImpl_Invalidate(iface
);
4145 IStorage_Release((IStorage
*)This
->transactedParent
);
4147 IStorage_Release((IStorage
*)This
->snapshot
);
4149 HeapFree(GetProcessHeap(), 0, This
);
4152 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4153 const DirEntry
*newData
, DirRef
*index
)
4155 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4157 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4161 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4162 DirRef index
, const DirEntry
*data
)
4164 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4166 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4170 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4171 DirRef index
, DirEntry
*data
)
4173 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4175 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4179 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4182 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4184 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4188 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4189 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4191 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4193 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4194 index
, offset
, size
, buffer
, bytesRead
);
4197 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4198 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4200 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4202 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4203 index
, offset
, size
, buffer
, bytesWritten
);
4206 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4207 DirRef index
, ULARGE_INTEGER newsize
)
4209 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4211 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4215 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4217 StorageBaseImpl_QueryInterface
,
4218 StorageBaseImpl_AddRef
,
4219 StorageBaseImpl_Release
,
4220 StorageBaseImpl_CreateStream
,
4221 StorageBaseImpl_OpenStream
,
4222 StorageBaseImpl_CreateStorage
,
4223 StorageBaseImpl_OpenStorage
,
4224 StorageBaseImpl_CopyTo
,
4225 StorageBaseImpl_MoveElementTo
,
4226 TransactedSnapshotImpl_Commit
,
4227 TransactedSnapshotImpl_Revert
,
4228 StorageBaseImpl_EnumElements
,
4229 StorageBaseImpl_DestroyElement
,
4230 StorageBaseImpl_RenameElement
,
4231 StorageBaseImpl_SetElementTimes
,
4232 StorageBaseImpl_SetClass
,
4233 StorageBaseImpl_SetStateBits
,
4234 StorageBaseImpl_Stat
4237 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4239 TransactedSnapshotImpl_Destroy
,
4240 TransactedSnapshotImpl_Invalidate
,
4241 TransactedSnapshotImpl_CreateDirEntry
,
4242 TransactedSnapshotImpl_WriteDirEntry
,
4243 TransactedSnapshotImpl_ReadDirEntry
,
4244 TransactedSnapshotImpl_DestroyDirEntry
,
4245 TransactedSnapshotImpl_StreamReadAt
,
4246 TransactedSnapshotImpl_StreamWriteAt
,
4247 TransactedSnapshotImpl_StreamSetSize
4250 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4251 TransactedSnapshotImpl
** result
)
4255 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4258 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4260 /* This is OK because the property set storage functions use the IStorage functions. */
4261 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4263 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4265 list_init(&(*result
)->base
.strmHead
);
4267 list_init(&(*result
)->base
.storageHead
);
4269 (*result
)->base
.ref
= 1;
4271 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4273 (*result
)->base
.filename
= parentStorage
->filename
;
4275 /* Create a new temporary storage to act as the snapshot */
4276 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4280 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4282 /* parentStorage already has 1 reference, which we take over here. */
4283 (*result
)->transactedParent
= parentStorage
;
4285 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4288 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4293 return E_OUTOFMEMORY
;
4296 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4297 StorageBaseImpl
** result
)
4301 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4303 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4306 return TransactedSnapshotImpl_Construct(parentStorage
,
4307 (TransactedSnapshotImpl
**)result
);
4310 static HRESULT
Storage_Construct(
4317 StorageBaseImpl
** result
)
4319 StorageImpl
*newStorage
;
4320 StorageBaseImpl
*newTransactedStorage
;
4323 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4324 if (FAILED(hr
)) goto end
;
4326 if (openFlags
& STGM_TRANSACTED
)
4328 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4330 IStorage_Release((IStorage
*)newStorage
);
4332 *result
= newTransactedStorage
;
4335 *result
= &newStorage
->base
;
4341 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4343 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4345 if (!This
->base
.reverted
)
4347 TRACE("Storage invalidated (stg=%p)\n", This
);
4349 This
->base
.reverted
= 1;
4351 This
->parentStorage
= NULL
;
4353 StorageBaseImpl_DeleteAll(&This
->base
);
4355 list_remove(&This
->ParentListEntry
);
4359 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4361 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4363 StorageInternalImpl_Invalidate(&This
->base
);
4365 HeapFree(GetProcessHeap(), 0, This
);
4368 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4369 const DirEntry
*newData
, DirRef
*index
)
4371 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4373 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4377 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4378 DirRef index
, const DirEntry
*data
)
4380 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4382 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4386 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4387 DirRef index
, DirEntry
*data
)
4389 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4391 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4395 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4398 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4400 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4404 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4405 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4407 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4409 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4410 index
, offset
, size
, buffer
, bytesRead
);
4413 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4414 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4416 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4418 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4419 index
, offset
, size
, buffer
, bytesWritten
);
4422 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4423 DirRef index
, ULARGE_INTEGER newsize
)
4425 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4427 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4431 /******************************************************************************
4433 ** Storage32InternalImpl_Commit
4436 static HRESULT WINAPI
StorageInternalImpl_Commit(
4438 DWORD grfCommitFlags
) /* [in] */
4440 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4444 /******************************************************************************
4446 ** Storage32InternalImpl_Revert
4449 static HRESULT WINAPI
StorageInternalImpl_Revert(
4452 FIXME("(%p): stub\n", iface
);
4456 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4458 IStorage_Release((IStorage
*)This
->parentStorage
);
4459 HeapFree(GetProcessHeap(), 0, This
);
4462 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4463 IEnumSTATSTG
* iface
,
4467 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4470 return E_INVALIDARG
;
4474 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4475 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4478 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4482 return E_NOINTERFACE
;
4485 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4486 IEnumSTATSTG
* iface
)
4488 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4489 return InterlockedIncrement(&This
->ref
);
4492 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4493 IEnumSTATSTG
* iface
)
4495 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4499 newRef
= InterlockedDecrement(&This
->ref
);
4503 IEnumSTATSTGImpl_Destroy(This
);
4509 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4510 IEnumSTATSTGImpl
* This
,
4513 DirRef result
= DIRENTRY_NULL
;
4517 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4519 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4520 This
->parentStorage
->storageDirEntry
, &entry
);
4521 searchNode
= entry
.dirRootEntry
;
4523 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4525 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4529 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4533 searchNode
= entry
.rightChild
;
4537 result
= searchNode
;
4538 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4539 searchNode
= entry
.leftChild
;
4547 if (result
!= DIRENTRY_NULL
)
4548 memcpy(This
->name
, result_name
, sizeof(result_name
));
4554 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4555 IEnumSTATSTG
* iface
,
4558 ULONG
* pceltFetched
)
4560 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4562 DirEntry currentEntry
;
4563 STATSTG
* currentReturnStruct
= rgelt
;
4564 ULONG objectFetched
= 0;
4565 DirRef currentSearchNode
;
4568 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4569 return E_INVALIDARG
;
4571 if (This
->parentStorage
->reverted
)
4572 return STG_E_REVERTED
;
4575 * To avoid the special case, get another pointer to a ULONG value if
4576 * the caller didn't supply one.
4578 if (pceltFetched
==0)
4579 pceltFetched
= &objectFetched
;
4582 * Start the iteration, we will iterate until we hit the end of the
4583 * linked list or until we hit the number of items to iterate through
4587 while ( *pceltFetched
< celt
)
4589 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4591 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4595 * Read the entry from the storage.
4597 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4602 * Copy the information to the return buffer.
4604 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4605 currentReturnStruct
,
4610 * Step to the next item in the iteration
4613 currentReturnStruct
++;
4616 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4623 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4624 IEnumSTATSTG
* iface
,
4627 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4629 ULONG objectFetched
= 0;
4630 DirRef currentSearchNode
;
4633 if (This
->parentStorage
->reverted
)
4634 return STG_E_REVERTED
;
4636 while ( (objectFetched
< celt
) )
4638 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4640 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4646 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4652 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4653 IEnumSTATSTG
* iface
)
4655 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4657 if (This
->parentStorage
->reverted
)
4658 return STG_E_REVERTED
;
4665 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4666 IEnumSTATSTG
* iface
,
4667 IEnumSTATSTG
** ppenum
)
4669 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4671 IEnumSTATSTGImpl
* newClone
;
4673 if (This
->parentStorage
->reverted
)
4674 return STG_E_REVERTED
;
4677 * Perform a sanity check on the parameters.
4680 return E_INVALIDARG
;
4682 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4683 This
->storageDirEntry
);
4687 * The new clone enumeration must point to the same current node as
4690 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4692 *ppenum
= (IEnumSTATSTG
*)newClone
;
4695 * Don't forget to nail down a reference to the clone before
4698 IEnumSTATSTGImpl_AddRef(*ppenum
);
4704 * Virtual function table for the IEnumSTATSTGImpl class.
4706 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4708 IEnumSTATSTGImpl_QueryInterface
,
4709 IEnumSTATSTGImpl_AddRef
,
4710 IEnumSTATSTGImpl_Release
,
4711 IEnumSTATSTGImpl_Next
,
4712 IEnumSTATSTGImpl_Skip
,
4713 IEnumSTATSTGImpl_Reset
,
4714 IEnumSTATSTGImpl_Clone
4717 /******************************************************************************
4718 ** IEnumSTATSTGImpl implementation
4721 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4722 StorageBaseImpl
* parentStorage
,
4723 DirRef storageDirEntry
)
4725 IEnumSTATSTGImpl
* newEnumeration
;
4727 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4729 if (newEnumeration
!=0)
4732 * Set-up the virtual function table and reference count.
4734 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4735 newEnumeration
->ref
= 0;
4738 * We want to nail-down the reference to the storage in case the
4739 * enumeration out-lives the storage in the client application.
4741 newEnumeration
->parentStorage
= parentStorage
;
4742 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4744 newEnumeration
->storageDirEntry
= storageDirEntry
;
4747 * Make sure the current node of the iterator is the first one.
4749 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4752 return newEnumeration
;
4756 * Virtual function table for the Storage32InternalImpl class.
4758 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4760 StorageBaseImpl_QueryInterface
,
4761 StorageBaseImpl_AddRef
,
4762 StorageBaseImpl_Release
,
4763 StorageBaseImpl_CreateStream
,
4764 StorageBaseImpl_OpenStream
,
4765 StorageBaseImpl_CreateStorage
,
4766 StorageBaseImpl_OpenStorage
,
4767 StorageBaseImpl_CopyTo
,
4768 StorageBaseImpl_MoveElementTo
,
4769 StorageInternalImpl_Commit
,
4770 StorageInternalImpl_Revert
,
4771 StorageBaseImpl_EnumElements
,
4772 StorageBaseImpl_DestroyElement
,
4773 StorageBaseImpl_RenameElement
,
4774 StorageBaseImpl_SetElementTimes
,
4775 StorageBaseImpl_SetClass
,
4776 StorageBaseImpl_SetStateBits
,
4777 StorageBaseImpl_Stat
4780 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4782 StorageInternalImpl_Destroy
,
4783 StorageInternalImpl_Invalidate
,
4784 StorageInternalImpl_CreateDirEntry
,
4785 StorageInternalImpl_WriteDirEntry
,
4786 StorageInternalImpl_ReadDirEntry
,
4787 StorageInternalImpl_DestroyDirEntry
,
4788 StorageInternalImpl_StreamReadAt
,
4789 StorageInternalImpl_StreamWriteAt
,
4790 StorageInternalImpl_StreamSetSize
4793 /******************************************************************************
4794 ** Storage32InternalImpl implementation
4797 static StorageInternalImpl
* StorageInternalImpl_Construct(
4798 StorageBaseImpl
* parentStorage
,
4800 DirRef storageDirEntry
)
4802 StorageInternalImpl
* newStorage
;
4804 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4808 list_init(&newStorage
->base
.strmHead
);
4810 list_init(&newStorage
->base
.storageHead
);
4813 * Initialize the virtual function table.
4815 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4816 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4817 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4819 newStorage
->base
.reverted
= 0;
4821 newStorage
->base
.ref
= 1;
4823 newStorage
->parentStorage
= parentStorage
;
4826 * Keep a reference to the directory entry of this storage
4828 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4830 newStorage
->base
.create
= 0;
4838 /******************************************************************************
4839 ** StorageUtl implementation
4842 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4846 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4847 *value
= lendian16toh(tmp
);
4850 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4852 value
= htole16(value
);
4853 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4856 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4860 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4861 *value
= lendian32toh(tmp
);
4864 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4866 value
= htole32(value
);
4867 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4870 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4871 ULARGE_INTEGER
* value
)
4873 #ifdef WORDS_BIGENDIAN
4876 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4877 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4878 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4880 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4884 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4885 const ULARGE_INTEGER
*value
)
4887 #ifdef WORDS_BIGENDIAN
4890 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4891 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4892 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4894 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4898 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4900 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4901 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4902 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4904 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4907 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4909 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4910 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4911 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4913 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4916 void StorageUtl_CopyDirEntryToSTATSTG(
4917 StorageBaseImpl
* storage
,
4918 STATSTG
* destination
,
4919 const DirEntry
* source
,
4924 if (source
->stgType
== STGTY_ROOT
)
4926 /* replace the name of root entry (often "Root Entry") by the file name */
4927 entryName
= storage
->filename
;
4931 entryName
= source
->name
;
4935 * The copy of the string occurs only when the flag is not set
4937 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4938 (entryName
== NULL
) ||
4939 (entryName
[0] == 0) )
4941 destination
->pwcsName
= 0;
4945 destination
->pwcsName
=
4946 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4948 strcpyW(destination
->pwcsName
, entryName
);
4951 switch (source
->stgType
)
4955 destination
->type
= STGTY_STORAGE
;
4958 destination
->type
= STGTY_STREAM
;
4961 destination
->type
= STGTY_STREAM
;
4965 destination
->cbSize
= source
->size
;
4967 currentReturnStruct->mtime = {0}; TODO
4968 currentReturnStruct->ctime = {0};
4969 currentReturnStruct->atime = {0};
4971 destination
->grfMode
= 0;
4972 destination
->grfLocksSupported
= 0;
4973 destination
->clsid
= source
->clsid
;
4974 destination
->grfStateBits
= 0;
4975 destination
->reserved
= 0;
4978 /******************************************************************************
4979 ** BlockChainStream implementation
4982 BlockChainStream
* BlockChainStream_Construct(
4983 StorageImpl
* parentStorage
,
4984 ULONG
* headOfStreamPlaceHolder
,
4987 BlockChainStream
* newStream
;
4990 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4992 newStream
->parentStorage
= parentStorage
;
4993 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4994 newStream
->ownerDirEntry
= dirEntry
;
4995 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4996 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4997 newStream
->numBlocks
= 0;
4999 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
5001 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5003 newStream
->numBlocks
++;
5004 newStream
->tailIndex
= blockIndex
;
5006 if(FAILED(StorageImpl_GetNextBlockInChain(
5011 HeapFree(GetProcessHeap(), 0, newStream
);
5019 void BlockChainStream_Destroy(BlockChainStream
* This
)
5021 HeapFree(GetProcessHeap(), 0, This
);
5024 /******************************************************************************
5025 * BlockChainStream_GetHeadOfChain
5027 * Returns the head of this stream chain.
5028 * Some special chains don't have directory entries, their heads are kept in
5029 * This->headOfStreamPlaceHolder.
5032 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5034 DirEntry chainEntry
;
5037 if (This
->headOfStreamPlaceHolder
!= 0)
5038 return *(This
->headOfStreamPlaceHolder
);
5040 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5042 hr
= StorageImpl_ReadDirEntry(
5043 This
->parentStorage
,
5044 This
->ownerDirEntry
,
5049 return chainEntry
.startingBlock
;
5053 return BLOCK_END_OF_CHAIN
;
5056 /******************************************************************************
5057 * BlockChainStream_GetCount
5059 * Returns the number of blocks that comprises this chain.
5060 * This is not the size of the stream as the last block may not be full!
5063 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5068 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5070 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5074 if(FAILED(StorageImpl_GetNextBlockInChain(
5075 This
->parentStorage
,
5084 /******************************************************************************
5085 * BlockChainStream_ReadAt
5087 * Reads a specified number of bytes from this chain at the specified offset.
5088 * bytesRead may be NULL.
5089 * Failure will be returned if the specified number of bytes has not been read.
5091 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5092 ULARGE_INTEGER offset
,
5097 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5098 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5099 ULONG bytesToReadInBuffer
;
5103 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5106 * Find the first block in the stream that contains part of the buffer.
5108 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5109 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5110 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5112 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5113 This
->lastBlockNoInSequence
= blockNoInSequence
;
5117 ULONG temp
= blockNoInSequence
;
5119 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5120 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5121 This
->lastBlockNoInSequence
= temp
;
5124 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5126 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5127 return STG_E_DOCFILECORRUPT
;
5128 blockNoInSequence
--;
5131 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5132 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5134 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5137 * Start reading the buffer.
5140 bufferWalker
= buffer
;
5142 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5144 ULARGE_INTEGER ulOffset
;
5147 * Calculate how many bytes we can copy from this big block.
5149 bytesToReadInBuffer
=
5150 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5152 TRACE("block %i\n",blockIndex
);
5153 ulOffset
.u
.HighPart
= 0;
5154 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5157 StorageImpl_ReadAt(This
->parentStorage
,
5160 bytesToReadInBuffer
,
5163 * Step to the next big block.
5165 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5166 return STG_E_DOCFILECORRUPT
;
5168 bufferWalker
+= bytesReadAt
;
5169 size
-= bytesReadAt
;
5170 *bytesRead
+= bytesReadAt
;
5171 offsetInBlock
= 0; /* There is no offset on the next block */
5173 if (bytesToReadInBuffer
!= bytesReadAt
)
5177 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5180 /******************************************************************************
5181 * BlockChainStream_WriteAt
5183 * Writes the specified number of bytes to this chain at the specified offset.
5184 * Will fail if not all specified number of bytes have been written.
5186 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5187 ULARGE_INTEGER offset
,
5190 ULONG
* bytesWritten
)
5192 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5193 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5196 const BYTE
* bufferWalker
;
5199 * Find the first block in the stream that contains part of the buffer.
5201 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5202 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5203 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5205 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5206 This
->lastBlockNoInSequence
= blockNoInSequence
;
5210 ULONG temp
= blockNoInSequence
;
5212 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5213 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5214 This
->lastBlockNoInSequence
= temp
;
5217 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5219 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5221 return STG_E_DOCFILECORRUPT
;
5222 blockNoInSequence
--;
5225 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5227 /* BlockChainStream_SetSize should have already been called to ensure we have
5228 * enough blocks in the chain to write into */
5229 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5231 ERR("not enough blocks in chain to write data\n");
5232 return STG_E_DOCFILECORRUPT
;
5236 bufferWalker
= buffer
;
5238 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5240 ULARGE_INTEGER ulOffset
;
5241 DWORD bytesWrittenAt
;
5243 * Calculate how many bytes we can copy from this big block.
5246 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5248 TRACE("block %i\n",blockIndex
);
5249 ulOffset
.u
.HighPart
= 0;
5250 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5253 StorageImpl_WriteAt(This
->parentStorage
,
5260 * Step to the next big block.
5262 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5264 return STG_E_DOCFILECORRUPT
;
5266 bufferWalker
+= bytesWrittenAt
;
5267 size
-= bytesWrittenAt
;
5268 *bytesWritten
+= bytesWrittenAt
;
5269 offsetInBlock
= 0; /* There is no offset on the next block */
5271 if (bytesWrittenAt
!= bytesToWrite
)
5275 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5278 /******************************************************************************
5279 * BlockChainStream_Shrink
5281 * Shrinks this chain in the big block depot.
5283 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5284 ULARGE_INTEGER newSize
)
5286 ULONG blockIndex
, extraBlock
;
5291 * Reset the last accessed block cache.
5293 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5294 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5297 * Figure out how many blocks are needed to contain the new size
5299 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5301 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5304 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5307 * Go to the new end of chain
5309 while (count
< numBlocks
)
5311 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5317 /* Get the next block before marking the new end */
5318 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5322 /* Mark the new end of chain */
5323 StorageImpl_SetNextBlockInChain(
5324 This
->parentStorage
,
5326 BLOCK_END_OF_CHAIN
);
5328 This
->tailIndex
= blockIndex
;
5329 This
->numBlocks
= numBlocks
;
5332 * Mark the extra blocks as free
5334 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5336 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5339 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5340 extraBlock
= blockIndex
;
5346 /******************************************************************************
5347 * BlockChainStream_Enlarge
5349 * Grows this chain in the big block depot.
5351 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5352 ULARGE_INTEGER newSize
)
5354 ULONG blockIndex
, currentBlock
;
5356 ULONG oldNumBlocks
= 0;
5358 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5361 * Empty chain. Create the head.
5363 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5365 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5366 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5368 BLOCK_END_OF_CHAIN
);
5370 if (This
->headOfStreamPlaceHolder
!= 0)
5372 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5376 DirEntry chainEntry
;
5377 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5379 StorageImpl_ReadDirEntry(
5380 This
->parentStorage
,
5381 This
->ownerDirEntry
,
5384 chainEntry
.startingBlock
= blockIndex
;
5386 StorageImpl_WriteDirEntry(
5387 This
->parentStorage
,
5388 This
->ownerDirEntry
,
5392 This
->tailIndex
= blockIndex
;
5393 This
->numBlocks
= 1;
5397 * Figure out how many blocks are needed to contain this stream
5399 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5401 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5405 * Go to the current end of chain
5407 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5409 currentBlock
= blockIndex
;
5411 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5414 currentBlock
= blockIndex
;
5416 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5421 This
->tailIndex
= currentBlock
;
5424 currentBlock
= This
->tailIndex
;
5425 oldNumBlocks
= This
->numBlocks
;
5428 * Add new blocks to the chain
5430 if (oldNumBlocks
< newNumBlocks
)
5432 while (oldNumBlocks
< newNumBlocks
)
5434 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5436 StorageImpl_SetNextBlockInChain(
5437 This
->parentStorage
,
5441 StorageImpl_SetNextBlockInChain(
5442 This
->parentStorage
,
5444 BLOCK_END_OF_CHAIN
);
5446 currentBlock
= blockIndex
;
5450 This
->tailIndex
= blockIndex
;
5451 This
->numBlocks
= newNumBlocks
;
5457 /******************************************************************************
5458 * BlockChainStream_SetSize
5460 * Sets the size of this stream. The big block depot will be updated.
5461 * The file will grow if we grow the chain.
5463 * TODO: Free the actual blocks in the file when we shrink the chain.
5464 * Currently, the blocks are still in the file. So the file size
5465 * doesn't shrink even if we shrink streams.
5467 BOOL
BlockChainStream_SetSize(
5468 BlockChainStream
* This
,
5469 ULARGE_INTEGER newSize
)
5471 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5473 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5476 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5478 BlockChainStream_Shrink(This
, newSize
);
5482 BlockChainStream_Enlarge(This
, newSize
);
5488 /******************************************************************************
5489 * BlockChainStream_GetSize
5491 * Returns the size of this chain.
5492 * Will return the block count if this chain doesn't have a directory entry.
5494 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5496 DirEntry chainEntry
;
5498 if(This
->headOfStreamPlaceHolder
== NULL
)
5501 * This chain has a directory entry so use the size value from there.
5503 StorageImpl_ReadDirEntry(
5504 This
->parentStorage
,
5505 This
->ownerDirEntry
,
5508 return chainEntry
.size
;
5513 * this chain is a chain that does not have a directory entry, figure out the
5514 * size by making the product number of used blocks times the
5517 ULARGE_INTEGER result
;
5518 result
.u
.HighPart
= 0;
5521 BlockChainStream_GetCount(This
) *
5522 This
->parentStorage
->bigBlockSize
;
5528 /******************************************************************************
5529 ** SmallBlockChainStream implementation
5532 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5533 StorageImpl
* parentStorage
,
5534 ULONG
* headOfStreamPlaceHolder
,
5537 SmallBlockChainStream
* newStream
;
5539 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5541 newStream
->parentStorage
= parentStorage
;
5542 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5543 newStream
->ownerDirEntry
= dirEntry
;
5548 void SmallBlockChainStream_Destroy(
5549 SmallBlockChainStream
* This
)
5551 HeapFree(GetProcessHeap(), 0, This
);
5554 /******************************************************************************
5555 * SmallBlockChainStream_GetHeadOfChain
5557 * Returns the head of this chain of small blocks.
5559 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5560 SmallBlockChainStream
* This
)
5562 DirEntry chainEntry
;
5565 if (This
->headOfStreamPlaceHolder
!= NULL
)
5566 return *(This
->headOfStreamPlaceHolder
);
5568 if (This
->ownerDirEntry
)
5570 hr
= StorageImpl_ReadDirEntry(
5571 This
->parentStorage
,
5572 This
->ownerDirEntry
,
5577 return chainEntry
.startingBlock
;
5582 return BLOCK_END_OF_CHAIN
;
5585 /******************************************************************************
5586 * SmallBlockChainStream_GetNextBlockInChain
5588 * Returns the index of the next small block in this chain.
5591 * - BLOCK_END_OF_CHAIN: end of this chain
5592 * - BLOCK_UNUSED: small block 'blockIndex' is free
5594 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5595 SmallBlockChainStream
* This
,
5597 ULONG
* nextBlockInChain
)
5599 ULARGE_INTEGER offsetOfBlockInDepot
;
5604 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5606 offsetOfBlockInDepot
.u
.HighPart
= 0;
5607 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5610 * Read those bytes in the buffer from the small block file.
5612 res
= BlockChainStream_ReadAt(
5613 This
->parentStorage
->smallBlockDepotChain
,
5614 offsetOfBlockInDepot
,
5621 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5628 /******************************************************************************
5629 * SmallBlockChainStream_SetNextBlockInChain
5631 * Writes the index of the next block of the specified block in the small
5633 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5634 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5636 static void SmallBlockChainStream_SetNextBlockInChain(
5637 SmallBlockChainStream
* This
,
5641 ULARGE_INTEGER offsetOfBlockInDepot
;
5645 offsetOfBlockInDepot
.u
.HighPart
= 0;
5646 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5648 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5651 * Read those bytes in the buffer from the small block file.
5653 BlockChainStream_WriteAt(
5654 This
->parentStorage
->smallBlockDepotChain
,
5655 offsetOfBlockInDepot
,
5661 /******************************************************************************
5662 * SmallBlockChainStream_FreeBlock
5664 * Flag small block 'blockIndex' as free in the small block depot.
5666 static void SmallBlockChainStream_FreeBlock(
5667 SmallBlockChainStream
* This
,
5670 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5673 /******************************************************************************
5674 * SmallBlockChainStream_GetNextFreeBlock
5676 * Returns the index of a free small block. The small block depot will be
5677 * enlarged if necessary. The small block chain will also be enlarged if
5680 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5681 SmallBlockChainStream
* This
)
5683 ULARGE_INTEGER offsetOfBlockInDepot
;
5686 ULONG blockIndex
= 0;
5687 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5689 ULONG smallBlocksPerBigBlock
;
5691 offsetOfBlockInDepot
.u
.HighPart
= 0;
5694 * Scan the small block depot for a free block
5696 while (nextBlockIndex
!= BLOCK_UNUSED
)
5698 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5700 res
= BlockChainStream_ReadAt(
5701 This
->parentStorage
->smallBlockDepotChain
,
5702 offsetOfBlockInDepot
,
5708 * If we run out of space for the small block depot, enlarge it
5712 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5714 if (nextBlockIndex
!= BLOCK_UNUSED
)
5720 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5722 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5723 ULONG nextBlock
, newsbdIndex
;
5724 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5726 nextBlock
= sbdIndex
;
5727 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5729 sbdIndex
= nextBlock
;
5730 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5733 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5734 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5735 StorageImpl_SetNextBlockInChain(
5736 This
->parentStorage
,
5740 StorageImpl_SetNextBlockInChain(
5741 This
->parentStorage
,
5743 BLOCK_END_OF_CHAIN
);
5746 * Initialize all the small blocks to free
5748 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5749 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5754 * We have just created the small block depot.
5760 * Save it in the header
5762 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5763 StorageImpl_SaveFileHeader(This
->parentStorage
);
5766 * And allocate the first big block that will contain small blocks
5769 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5771 StorageImpl_SetNextBlockInChain(
5772 This
->parentStorage
,
5774 BLOCK_END_OF_CHAIN
);
5776 StorageImpl_ReadDirEntry(
5777 This
->parentStorage
,
5778 This
->parentStorage
->base
.storageDirEntry
,
5781 rootEntry
.startingBlock
= sbStartIndex
;
5782 rootEntry
.size
.u
.HighPart
= 0;
5783 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5785 StorageImpl_WriteDirEntry(
5786 This
->parentStorage
,
5787 This
->parentStorage
->base
.storageDirEntry
,
5791 StorageImpl_SaveFileHeader(This
->parentStorage
);
5795 smallBlocksPerBigBlock
=
5796 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5799 * Verify if we have to allocate big blocks to contain small blocks
5801 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5804 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5806 StorageImpl_ReadDirEntry(
5807 This
->parentStorage
,
5808 This
->parentStorage
->base
.storageDirEntry
,
5811 if (rootEntry
.size
.u
.LowPart
<
5812 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5814 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5816 BlockChainStream_SetSize(
5817 This
->parentStorage
->smallBlockRootChain
,
5820 StorageImpl_WriteDirEntry(
5821 This
->parentStorage
,
5822 This
->parentStorage
->base
.storageDirEntry
,
5830 /******************************************************************************
5831 * SmallBlockChainStream_ReadAt
5833 * Reads a specified number of bytes from this chain at the specified offset.
5834 * bytesRead may be NULL.
5835 * Failure will be returned if the specified number of bytes has not been read.
5837 HRESULT
SmallBlockChainStream_ReadAt(
5838 SmallBlockChainStream
* This
,
5839 ULARGE_INTEGER offset
,
5845 ULARGE_INTEGER offsetInBigBlockFile
;
5846 ULONG blockNoInSequence
=
5847 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5849 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5850 ULONG bytesToReadInBuffer
;
5852 ULONG bytesReadFromBigBlockFile
;
5856 * This should never happen on a small block file.
5858 assert(offset
.u
.HighPart
==0);
5861 * Find the first block in the stream that contains part of the buffer.
5863 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5865 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5867 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5870 blockNoInSequence
--;
5874 * Start reading the buffer.
5877 bufferWalker
= buffer
;
5879 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5882 * Calculate how many bytes we can copy from this small block.
5884 bytesToReadInBuffer
=
5885 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5888 * Calculate the offset of the small block in the small block file.
5890 offsetInBigBlockFile
.u
.HighPart
= 0;
5891 offsetInBigBlockFile
.u
.LowPart
=
5892 blockIndex
* This
->parentStorage
->smallBlockSize
;
5894 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5897 * Read those bytes in the buffer from the small block file.
5898 * The small block has already been identified so it shouldn't fail
5899 * unless the file is corrupt.
5901 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5902 offsetInBigBlockFile
,
5903 bytesToReadInBuffer
,
5905 &bytesReadFromBigBlockFile
);
5911 * Step to the next big block.
5913 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5915 return STG_E_DOCFILECORRUPT
;
5917 bufferWalker
+= bytesReadFromBigBlockFile
;
5918 size
-= bytesReadFromBigBlockFile
;
5919 *bytesRead
+= bytesReadFromBigBlockFile
;
5920 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5923 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5926 /******************************************************************************
5927 * SmallBlockChainStream_WriteAt
5929 * Writes the specified number of bytes to this chain at the specified offset.
5930 * Will fail if not all specified number of bytes have been written.
5932 HRESULT
SmallBlockChainStream_WriteAt(
5933 SmallBlockChainStream
* This
,
5934 ULARGE_INTEGER offset
,
5937 ULONG
* bytesWritten
)
5939 ULARGE_INTEGER offsetInBigBlockFile
;
5940 ULONG blockNoInSequence
=
5941 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5943 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5944 ULONG bytesToWriteInBuffer
;
5946 ULONG bytesWrittenToBigBlockFile
;
5947 const BYTE
* bufferWalker
;
5951 * This should never happen on a small block file.
5953 assert(offset
.u
.HighPart
==0);
5956 * Find the first block in the stream that contains part of the buffer.
5958 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5960 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5962 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5963 return STG_E_DOCFILECORRUPT
;
5964 blockNoInSequence
--;
5968 * Start writing the buffer.
5971 bufferWalker
= buffer
;
5972 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5975 * Calculate how many bytes we can copy to this small block.
5977 bytesToWriteInBuffer
=
5978 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5981 * Calculate the offset of the small block in the small block file.
5983 offsetInBigBlockFile
.u
.HighPart
= 0;
5984 offsetInBigBlockFile
.u
.LowPart
=
5985 blockIndex
* This
->parentStorage
->smallBlockSize
;
5987 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5990 * Write those bytes in the buffer to the small block file.
5992 res
= BlockChainStream_WriteAt(
5993 This
->parentStorage
->smallBlockRootChain
,
5994 offsetInBigBlockFile
,
5995 bytesToWriteInBuffer
,
5997 &bytesWrittenToBigBlockFile
);
6002 * Step to the next big block.
6004 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6007 bufferWalker
+= bytesWrittenToBigBlockFile
;
6008 size
-= bytesWrittenToBigBlockFile
;
6009 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6010 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6013 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6016 /******************************************************************************
6017 * SmallBlockChainStream_Shrink
6019 * Shrinks this chain in the small block depot.
6021 static BOOL
SmallBlockChainStream_Shrink(
6022 SmallBlockChainStream
* This
,
6023 ULARGE_INTEGER newSize
)
6025 ULONG blockIndex
, extraBlock
;
6029 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6031 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6034 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6037 * Go to the new end of chain
6039 while (count
< numBlocks
)
6041 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6048 * If the count is 0, we have a special case, the head of the chain was
6053 DirEntry chainEntry
;
6055 StorageImpl_ReadDirEntry(This
->parentStorage
,
6056 This
->ownerDirEntry
,
6059 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6061 StorageImpl_WriteDirEntry(This
->parentStorage
,
6062 This
->ownerDirEntry
,
6066 * We start freeing the chain at the head block.
6068 extraBlock
= blockIndex
;
6072 /* Get the next block before marking the new end */
6073 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6077 /* Mark the new end of chain */
6078 SmallBlockChainStream_SetNextBlockInChain(
6081 BLOCK_END_OF_CHAIN
);
6085 * Mark the extra blocks as free
6087 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6089 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6092 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6093 extraBlock
= blockIndex
;
6099 /******************************************************************************
6100 * SmallBlockChainStream_Enlarge
6102 * Grows this chain in the small block depot.
6104 static BOOL
SmallBlockChainStream_Enlarge(
6105 SmallBlockChainStream
* This
,
6106 ULARGE_INTEGER newSize
)
6108 ULONG blockIndex
, currentBlock
;
6110 ULONG oldNumBlocks
= 0;
6112 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6115 * Empty chain. Create the head.
6117 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6119 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6120 SmallBlockChainStream_SetNextBlockInChain(
6123 BLOCK_END_OF_CHAIN
);
6125 if (This
->headOfStreamPlaceHolder
!= NULL
)
6127 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6131 DirEntry chainEntry
;
6133 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6136 chainEntry
.startingBlock
= blockIndex
;
6138 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6143 currentBlock
= blockIndex
;
6146 * Figure out how many blocks are needed to contain this stream
6148 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6150 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6154 * Go to the current end of chain
6156 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6159 currentBlock
= blockIndex
;
6160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6165 * Add new blocks to the chain
6167 while (oldNumBlocks
< newNumBlocks
)
6169 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6170 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6172 SmallBlockChainStream_SetNextBlockInChain(
6175 BLOCK_END_OF_CHAIN
);
6177 currentBlock
= blockIndex
;
6184 /******************************************************************************
6185 * SmallBlockChainStream_SetSize
6187 * Sets the size of this stream.
6188 * The file will grow if we grow the chain.
6190 * TODO: Free the actual blocks in the file when we shrink the chain.
6191 * Currently, the blocks are still in the file. So the file size
6192 * doesn't shrink even if we shrink streams.
6194 BOOL
SmallBlockChainStream_SetSize(
6195 SmallBlockChainStream
* This
,
6196 ULARGE_INTEGER newSize
)
6198 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6200 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6203 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6205 SmallBlockChainStream_Shrink(This
, newSize
);
6209 SmallBlockChainStream_Enlarge(This
, newSize
);
6215 /******************************************************************************
6216 * SmallBlockChainStream_GetCount
6218 * Returns the number of small blocks that comprises this chain.
6219 * This is not the size of the stream as the last block may not be full!
6222 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6227 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6229 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6234 blockIndex
, &blockIndex
)))
6241 /******************************************************************************
6242 * SmallBlockChainStream_GetSize
6244 * Returns the size of this chain.
6246 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6248 DirEntry chainEntry
;
6250 if(This
->headOfStreamPlaceHolder
!= NULL
)
6252 ULARGE_INTEGER result
;
6253 result
.u
.HighPart
= 0;
6255 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6256 This
->parentStorage
->smallBlockSize
;
6261 StorageImpl_ReadDirEntry(
6262 This
->parentStorage
,
6263 This
->ownerDirEntry
,
6266 return chainEntry
.size
;
6269 /******************************************************************************
6270 * StgCreateDocfile [OLE32.@]
6271 * Creates a new compound file storage object
6274 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6275 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6276 * reserved [ ?] unused?, usually 0
6277 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6280 * S_OK if the file was successfully created
6281 * some STG_E_ value if error
6283 * if pwcsName is NULL, create file with new unique name
6284 * the function can returns
6285 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6288 HRESULT WINAPI
StgCreateDocfile(
6292 IStorage
**ppstgOpen
)
6294 StorageBaseImpl
* newStorage
= 0;
6295 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6296 HRESULT hr
= STG_E_INVALIDFLAG
;
6300 DWORD fileAttributes
;
6301 WCHAR tempFileName
[MAX_PATH
];
6303 TRACE("(%s, %x, %d, %p)\n",
6304 debugstr_w(pwcsName
), grfMode
,
6305 reserved
, ppstgOpen
);
6308 return STG_E_INVALIDPOINTER
;
6310 return STG_E_INVALIDPARAMETER
;
6312 /* if no share mode given then DENY_NONE is the default */
6313 if (STGM_SHARE_MODE(grfMode
) == 0)
6314 grfMode
|= STGM_SHARE_DENY_NONE
;
6316 if ( FAILED( validateSTGM(grfMode
) ))
6319 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6320 switch(STGM_ACCESS_MODE(grfMode
))
6323 case STGM_READWRITE
:
6329 /* in direct mode, can only use SHARE_EXCLUSIVE */
6330 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6333 /* but in transacted mode, any share mode is valid */
6336 * Generate a unique name.
6340 WCHAR tempPath
[MAX_PATH
];
6341 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6343 memset(tempPath
, 0, sizeof(tempPath
));
6344 memset(tempFileName
, 0, sizeof(tempFileName
));
6346 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6349 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6350 pwcsName
= tempFileName
;
6353 hr
= STG_E_INSUFFICIENTMEMORY
;
6357 creationMode
= TRUNCATE_EXISTING
;
6361 creationMode
= GetCreationModeFromSTGM(grfMode
);
6365 * Interpret the STGM value grfMode
6367 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6368 accessMode
= GetAccessModeFromSTGM(grfMode
);
6370 if (grfMode
& STGM_DELETEONRELEASE
)
6371 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6373 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6375 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6379 FIXME("Storage share mode not implemented.\n");
6384 hFile
= CreateFileW(pwcsName
,
6392 if (hFile
== INVALID_HANDLE_VALUE
)
6394 if(GetLastError() == ERROR_FILE_EXISTS
)
6395 hr
= STG_E_FILEALREADYEXISTS
;
6402 * Allocate and initialize the new IStorage32object.
6404 hr
= Storage_Construct(
6419 * Get an "out" pointer for the caller.
6421 *ppstgOpen
= (IStorage
*)newStorage
;
6424 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6429 /******************************************************************************
6430 * StgCreateStorageEx [OLE32.@]
6432 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6434 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6435 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6437 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6439 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6440 return STG_E_INVALIDPARAMETER
;
6443 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6445 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6446 return STG_E_INVALIDPARAMETER
;
6449 if (stgfmt
== STGFMT_FILE
)
6451 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6452 return STG_E_INVALIDPARAMETER
;
6455 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6457 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6458 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6461 ERR("Invalid stgfmt argument\n");
6462 return STG_E_INVALIDPARAMETER
;
6465 /******************************************************************************
6466 * StgCreatePropSetStg [OLE32.@]
6468 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6469 IPropertySetStorage
**ppPropSetStg
)
6473 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6475 hr
= STG_E_INVALIDPARAMETER
;
6477 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6478 (void**)ppPropSetStg
);
6482 /******************************************************************************
6483 * StgOpenStorageEx [OLE32.@]
6485 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6487 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6488 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6490 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6492 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6493 return STG_E_INVALIDPARAMETER
;
6499 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6500 return STG_E_INVALIDPARAMETER
;
6502 case STGFMT_STORAGE
:
6505 case STGFMT_DOCFILE
:
6506 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6508 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6509 return STG_E_INVALIDPARAMETER
;
6511 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6515 WARN("STGFMT_ANY assuming storage\n");
6519 return STG_E_INVALIDPARAMETER
;
6522 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6526 /******************************************************************************
6527 * StgOpenStorage [OLE32.@]
6529 HRESULT WINAPI
StgOpenStorage(
6530 const OLECHAR
*pwcsName
,
6531 IStorage
*pstgPriority
,
6535 IStorage
**ppstgOpen
)
6537 StorageBaseImpl
* newStorage
= 0;
6543 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6544 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6545 snbExclude
, reserved
, ppstgOpen
);
6549 hr
= STG_E_INVALIDNAME
;
6555 hr
= STG_E_INVALIDPOINTER
;
6561 hr
= STG_E_INVALIDPARAMETER
;
6565 if (grfMode
& STGM_PRIORITY
)
6567 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6568 return STG_E_INVALIDFLAG
;
6569 if (grfMode
& STGM_DELETEONRELEASE
)
6570 return STG_E_INVALIDFUNCTION
;
6571 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6572 return STG_E_INVALIDFLAG
;
6573 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6574 grfMode
|= STGM_SHARE_DENY_NONE
;
6576 /* STGM_PRIORITY stops other IStorage objects on the same file from
6577 * committing until the STGM_PRIORITY IStorage is closed. it also
6578 * stops non-transacted mode StgOpenStorage calls with write access from
6579 * succeeding. obviously, both of these cannot be achieved through just
6580 * file share flags */
6581 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6585 * Validate the sharing mode
6587 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6588 switch(STGM_SHARE_MODE(grfMode
))
6590 case STGM_SHARE_EXCLUSIVE
:
6591 case STGM_SHARE_DENY_WRITE
:
6594 hr
= STG_E_INVALIDFLAG
;
6598 if ( FAILED( validateSTGM(grfMode
) ) ||
6599 (grfMode
&STGM_CREATE
))
6601 hr
= STG_E_INVALIDFLAG
;
6605 /* shared reading requires transacted mode */
6606 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6607 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6608 !(grfMode
&STGM_TRANSACTED
) )
6610 hr
= STG_E_INVALIDFLAG
;
6615 * Interpret the STGM value grfMode
6617 shareMode
= GetShareModeFromSTGM(grfMode
);
6618 accessMode
= GetAccessModeFromSTGM(grfMode
);
6622 hFile
= CreateFileW( pwcsName
,
6627 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6630 if (hFile
==INVALID_HANDLE_VALUE
)
6632 DWORD last_error
= GetLastError();
6638 case ERROR_FILE_NOT_FOUND
:
6639 hr
= STG_E_FILENOTFOUND
;
6642 case ERROR_PATH_NOT_FOUND
:
6643 hr
= STG_E_PATHNOTFOUND
;
6646 case ERROR_ACCESS_DENIED
:
6647 case ERROR_WRITE_PROTECT
:
6648 hr
= STG_E_ACCESSDENIED
;
6651 case ERROR_SHARING_VIOLATION
:
6652 hr
= STG_E_SHAREVIOLATION
;
6663 * Refuse to open the file if it's too small to be a structured storage file
6664 * FIXME: verify the file when reading instead of here
6666 if (GetFileSize(hFile
, NULL
) < 0x100)
6669 hr
= STG_E_FILEALREADYEXISTS
;
6674 * Allocate and initialize the new IStorage32object.
6676 hr
= Storage_Construct(
6688 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6690 if(hr
== STG_E_INVALIDHEADER
)
6691 hr
= STG_E_FILEALREADYEXISTS
;
6696 * Get an "out" pointer for the caller.
6698 *ppstgOpen
= (IStorage
*)newStorage
;
6701 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6705 /******************************************************************************
6706 * StgCreateDocfileOnILockBytes [OLE32.@]
6708 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6712 IStorage
** ppstgOpen
)
6714 StorageBaseImpl
* newStorage
= 0;
6717 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6718 return STG_E_INVALIDPOINTER
;
6721 * Allocate and initialize the new IStorage object.
6723 hr
= Storage_Construct(
6738 * Get an "out" pointer for the caller.
6740 *ppstgOpen
= (IStorage
*)newStorage
;
6745 /******************************************************************************
6746 * StgOpenStorageOnILockBytes [OLE32.@]
6748 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6750 IStorage
*pstgPriority
,
6754 IStorage
**ppstgOpen
)
6756 StorageBaseImpl
* newStorage
= 0;
6759 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6760 return STG_E_INVALIDPOINTER
;
6762 if ( FAILED( validateSTGM(grfMode
) ))
6763 return STG_E_INVALIDFLAG
;
6768 * Allocate and initialize the new IStorage object.
6770 hr
= Storage_Construct(
6785 * Get an "out" pointer for the caller.
6787 *ppstgOpen
= (IStorage
*)newStorage
;
6792 /******************************************************************************
6793 * StgSetTimes [ole32.@]
6794 * StgSetTimes [OLE32.@]
6798 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6799 FILETIME
const *patime
, FILETIME
const *pmtime
)
6801 IStorage
*stg
= NULL
;
6804 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6806 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6810 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6811 IStorage_Release(stg
);
6817 /******************************************************************************
6818 * StgIsStorageILockBytes [OLE32.@]
6820 * Determines if the ILockBytes contains a storage object.
6822 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6825 ULARGE_INTEGER offset
;
6827 offset
.u
.HighPart
= 0;
6828 offset
.u
.LowPart
= 0;
6830 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6832 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6838 /******************************************************************************
6839 * WriteClassStg [OLE32.@]
6841 * This method will store the specified CLSID in the specified storage object
6843 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6848 return E_INVALIDARG
;
6851 return STG_E_INVALIDPOINTER
;
6853 hRes
= IStorage_SetClass(pStg
, rclsid
);
6858 /***********************************************************************
6859 * ReadClassStg (OLE32.@)
6861 * This method reads the CLSID previously written to a storage object with
6862 * the WriteClassStg.
6865 * pstg [I] IStorage pointer
6866 * pclsid [O] Pointer to where the CLSID is written
6870 * Failure: HRESULT code.
6872 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6877 TRACE("(%p, %p)\n", pstg
, pclsid
);
6879 if(!pstg
|| !pclsid
)
6880 return E_INVALIDARG
;
6883 * read a STATSTG structure (contains the clsid) from the storage
6885 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6888 *pclsid
=pstatstg
.clsid
;
6893 /***********************************************************************
6894 * OleLoadFromStream (OLE32.@)
6896 * This function loads an object from stream
6898 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6902 LPPERSISTSTREAM xstm
;
6904 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6906 res
=ReadClassStm(pStm
,&clsid
);
6909 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6912 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6914 IUnknown_Release((IUnknown
*)*ppvObj
);
6917 res
=IPersistStream_Load(xstm
,pStm
);
6918 IPersistStream_Release(xstm
);
6919 /* FIXME: all refcounts ok at this point? I think they should be:
6922 * xstm : 0 (released)
6927 /***********************************************************************
6928 * OleSaveToStream (OLE32.@)
6930 * This function saves an object with the IPersistStream interface on it
6931 * to the specified stream.
6933 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6939 TRACE("(%p,%p)\n",pPStm
,pStm
);
6941 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6943 if (SUCCEEDED(res
)){
6945 res
=WriteClassStm(pStm
,&clsid
);
6949 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6952 TRACE("Finished Save\n");
6956 /****************************************************************************
6957 * This method validate a STGM parameter that can contain the values below
6959 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6960 * The stgm values contained in 0xffff0000 are bitmasks.
6962 * STGM_DIRECT 0x00000000
6963 * STGM_TRANSACTED 0x00010000
6964 * STGM_SIMPLE 0x08000000
6966 * STGM_READ 0x00000000
6967 * STGM_WRITE 0x00000001
6968 * STGM_READWRITE 0x00000002
6970 * STGM_SHARE_DENY_NONE 0x00000040
6971 * STGM_SHARE_DENY_READ 0x00000030
6972 * STGM_SHARE_DENY_WRITE 0x00000020
6973 * STGM_SHARE_EXCLUSIVE 0x00000010
6975 * STGM_PRIORITY 0x00040000
6976 * STGM_DELETEONRELEASE 0x04000000
6978 * STGM_CREATE 0x00001000
6979 * STGM_CONVERT 0x00020000
6980 * STGM_FAILIFTHERE 0x00000000
6982 * STGM_NOSCRATCH 0x00100000
6983 * STGM_NOSNAPSHOT 0x00200000
6985 static HRESULT
validateSTGM(DWORD stgm
)
6987 DWORD access
= STGM_ACCESS_MODE(stgm
);
6988 DWORD share
= STGM_SHARE_MODE(stgm
);
6989 DWORD create
= STGM_CREATE_MODE(stgm
);
6991 if (stgm
&~STGM_KNOWN_FLAGS
)
6993 ERR("unknown flags %08x\n", stgm
);
7001 case STGM_READWRITE
:
7009 case STGM_SHARE_DENY_NONE
:
7010 case STGM_SHARE_DENY_READ
:
7011 case STGM_SHARE_DENY_WRITE
:
7012 case STGM_SHARE_EXCLUSIVE
:
7021 case STGM_FAILIFTHERE
:
7028 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7030 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7034 * STGM_CREATE | STGM_CONVERT
7035 * if both are false, STGM_FAILIFTHERE is set to TRUE
7037 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7041 * STGM_NOSCRATCH requires STGM_TRANSACTED
7043 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7047 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7048 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7050 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7051 (!(stgm
& STGM_TRANSACTED
) ||
7052 share
== STGM_SHARE_EXCLUSIVE
||
7053 share
== STGM_SHARE_DENY_WRITE
) )
7059 /****************************************************************************
7060 * GetShareModeFromSTGM
7062 * This method will return a share mode flag from a STGM value.
7063 * The STGM value is assumed valid.
7065 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7067 switch (STGM_SHARE_MODE(stgm
))
7069 case STGM_SHARE_DENY_NONE
:
7070 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7071 case STGM_SHARE_DENY_READ
:
7072 return FILE_SHARE_WRITE
;
7073 case STGM_SHARE_DENY_WRITE
:
7074 return FILE_SHARE_READ
;
7075 case STGM_SHARE_EXCLUSIVE
:
7078 ERR("Invalid share mode!\n");
7083 /****************************************************************************
7084 * GetAccessModeFromSTGM
7086 * This method will return an access mode flag from a STGM value.
7087 * The STGM value is assumed valid.
7089 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7091 switch (STGM_ACCESS_MODE(stgm
))
7094 return GENERIC_READ
;
7096 case STGM_READWRITE
:
7097 return GENERIC_READ
| GENERIC_WRITE
;
7099 ERR("Invalid access mode!\n");
7104 /****************************************************************************
7105 * GetCreationModeFromSTGM
7107 * This method will return a creation mode flag from a STGM value.
7108 * The STGM value is assumed valid.
7110 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7112 switch(STGM_CREATE_MODE(stgm
))
7115 return CREATE_ALWAYS
;
7117 FIXME("STGM_CONVERT not implemented!\n");
7119 case STGM_FAILIFTHERE
:
7122 ERR("Invalid create mode!\n");
7128 /*************************************************************************
7129 * OLECONVERT_LoadOLE10 [Internal]
7131 * Loads the OLE10 STREAM to memory
7134 * pOleStream [I] The OLESTREAM
7135 * pData [I] Data Structure for the OLESTREAM Data
7139 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7140 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7143 * This function is used by OleConvertOLESTREAMToIStorage only.
7145 * Memory allocated for pData must be freed by the caller
7147 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7150 HRESULT hRes
= S_OK
;
7154 pData
->pData
= NULL
;
7155 pData
->pstrOleObjFileName
= NULL
;
7157 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7160 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7161 if(dwSize
!= sizeof(pData
->dwOleID
))
7163 hRes
= CONVERT10_E_OLESTREAM_GET
;
7165 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7167 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7178 /* Get the TypeID... more info needed for this field */
7179 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7180 if(dwSize
!= sizeof(pData
->dwTypeID
))
7182 hRes
= CONVERT10_E_OLESTREAM_GET
;
7187 if(pData
->dwTypeID
!= 0)
7189 /* Get the length of the OleTypeName */
7190 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7191 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7193 hRes
= CONVERT10_E_OLESTREAM_GET
;
7198 if(pData
->dwOleTypeNameLength
> 0)
7200 /* Get the OleTypeName */
7201 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7202 if(dwSize
!= pData
->dwOleTypeNameLength
)
7204 hRes
= CONVERT10_E_OLESTREAM_GET
;
7210 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7211 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7213 hRes
= CONVERT10_E_OLESTREAM_GET
;
7217 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7218 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7219 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7220 if(pData
->pstrOleObjFileName
)
7222 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7223 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7225 hRes
= CONVERT10_E_OLESTREAM_GET
;
7229 hRes
= CONVERT10_E_OLESTREAM_GET
;
7234 /* Get the Width of the Metafile */
7235 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7236 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7238 hRes
= CONVERT10_E_OLESTREAM_GET
;
7242 /* Get the Height of the Metafile */
7243 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7244 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7246 hRes
= CONVERT10_E_OLESTREAM_GET
;
7252 /* Get the Length of the Data */
7253 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7254 if(dwSize
!= sizeof(pData
->dwDataLength
))
7256 hRes
= CONVERT10_E_OLESTREAM_GET
;
7260 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7262 if(!bStrem1
) /* if it is a second OLE stream data */
7264 pData
->dwDataLength
-= 8;
7265 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7266 if(dwSize
!= sizeof(pData
->strUnknown
))
7268 hRes
= CONVERT10_E_OLESTREAM_GET
;
7274 if(pData
->dwDataLength
> 0)
7276 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7278 /* Get Data (ex. IStorage, Metafile, or BMP) */
7281 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7282 if(dwSize
!= pData
->dwDataLength
)
7284 hRes
= CONVERT10_E_OLESTREAM_GET
;
7289 hRes
= CONVERT10_E_OLESTREAM_GET
;
7298 /*************************************************************************
7299 * OLECONVERT_SaveOLE10 [Internal]
7301 * Saves the OLE10 STREAM From memory
7304 * pData [I] Data Structure for the OLESTREAM Data
7305 * pOleStream [I] The OLESTREAM to save
7309 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7312 * This function is used by OleConvertIStorageToOLESTREAM only.
7315 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7318 HRESULT hRes
= S_OK
;
7322 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7323 if(dwSize
!= sizeof(pData
->dwOleID
))
7325 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7330 /* Set the TypeID */
7331 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7332 if(dwSize
!= sizeof(pData
->dwTypeID
))
7334 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7338 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7340 /* Set the Length of the OleTypeName */
7341 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7342 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7344 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7349 if(pData
->dwOleTypeNameLength
> 0)
7351 /* Set the OleTypeName */
7352 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7353 if(dwSize
!= pData
->dwOleTypeNameLength
)
7355 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7362 /* Set the width of the Metafile */
7363 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7364 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7366 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7372 /* Set the height of the Metafile */
7373 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7374 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7376 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7382 /* Set the length of the Data */
7383 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7384 if(dwSize
!= sizeof(pData
->dwDataLength
))
7386 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7392 if(pData
->dwDataLength
> 0)
7394 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7395 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7396 if(dwSize
!= pData
->dwDataLength
)
7398 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7406 /*************************************************************************
7407 * OLECONVERT_GetOLE20FromOLE10[Internal]
7409 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7410 * opens it, and copies the content to the dest IStorage for
7411 * OleConvertOLESTREAMToIStorage
7415 * pDestStorage [I] The IStorage to copy the data to
7416 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7417 * nBufferLength [I] The size of the buffer
7426 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7430 IStorage
*pTempStorage
;
7431 DWORD dwNumOfBytesWritten
;
7432 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7433 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7435 /* Create a temp File */
7436 GetTempPathW(MAX_PATH
, wstrTempDir
);
7437 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7438 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7440 if(hFile
!= INVALID_HANDLE_VALUE
)
7442 /* Write IStorage Data to File */
7443 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7446 /* Open and copy temp storage to the Dest Storage */
7447 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7450 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7451 IStorage_Release(pTempStorage
);
7453 DeleteFileW(wstrTempFile
);
7458 /*************************************************************************
7459 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7461 * Saves the OLE10 STREAM From memory
7464 * pStorage [I] The Src IStorage to copy
7465 * pData [I] The Dest Memory to write to.
7468 * The size in bytes allocated for pData
7471 * Memory allocated for pData must be freed by the caller
7473 * Used by OleConvertIStorageToOLESTREAM only.
7476 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7480 DWORD nDataLength
= 0;
7481 IStorage
*pTempStorage
;
7482 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7483 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7487 /* Create temp Storage */
7488 GetTempPathW(MAX_PATH
, wstrTempDir
);
7489 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7490 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7494 /* Copy Src Storage to the Temp Storage */
7495 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7496 IStorage_Release(pTempStorage
);
7498 /* Open Temp Storage as a file and copy to memory */
7499 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7500 if(hFile
!= INVALID_HANDLE_VALUE
)
7502 nDataLength
= GetFileSize(hFile
, NULL
);
7503 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7504 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7507 DeleteFileW(wstrTempFile
);
7512 /*************************************************************************
7513 * OLECONVERT_CreateOleStream [Internal]
7515 * Creates the "\001OLE" stream in the IStorage if necessary.
7518 * pStorage [I] Dest storage to create the stream in
7524 * This function is used by OleConvertOLESTREAMToIStorage only.
7526 * This stream is still unknown, MS Word seems to have extra data
7527 * but since the data is stored in the OLESTREAM there should be
7528 * no need to recreate the stream. If the stream is manually
7529 * deleted it will create it with this default data.
7532 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7536 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7537 BYTE pOleStreamHeader
[] =
7539 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7540 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7541 0x00, 0x00, 0x00, 0x00
7544 /* Create stream if not present */
7545 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7546 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7550 /* Write default Data */
7551 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7552 IStream_Release(pStream
);
7556 /* write a string to a stream, preceded by its length */
7557 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7564 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7565 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7570 str
= CoTaskMemAlloc( len
);
7571 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7572 r
= IStream_Write( stm
, str
, len
, NULL
);
7573 CoTaskMemFree( str
);
7577 /* read a string preceded by its length from a stream */
7578 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7581 DWORD len
, count
= 0;
7585 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7588 if( count
!= sizeof(len
) )
7589 return E_OUTOFMEMORY
;
7591 TRACE("%d bytes\n",len
);
7593 str
= CoTaskMemAlloc( len
);
7595 return E_OUTOFMEMORY
;
7597 r
= IStream_Read( stm
, str
, len
, &count
);
7602 CoTaskMemFree( str
);
7603 return E_OUTOFMEMORY
;
7606 TRACE("Read string %s\n",debugstr_an(str
,len
));
7608 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7609 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7611 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7612 CoTaskMemFree( str
);
7620 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7621 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7625 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7627 static const BYTE unknown1
[12] =
7628 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7629 0xFF, 0xFF, 0xFF, 0xFF};
7630 static const BYTE unknown2
[16] =
7631 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7634 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7635 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7636 debugstr_w(szProgIDName
));
7638 /* Create a CompObj stream */
7639 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7640 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7644 /* Write CompObj Structure to stream */
7645 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7647 if( SUCCEEDED( r
) )
7648 r
= WriteClassStm( pstm
, clsid
);
7650 if( SUCCEEDED( r
) )
7651 r
= STREAM_WriteString( pstm
, lpszUserType
);
7652 if( SUCCEEDED( r
) )
7653 r
= STREAM_WriteString( pstm
, szClipName
);
7654 if( SUCCEEDED( r
) )
7655 r
= STREAM_WriteString( pstm
, szProgIDName
);
7656 if( SUCCEEDED( r
) )
7657 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7659 IStream_Release( pstm
);
7664 /***********************************************************************
7665 * WriteFmtUserTypeStg (OLE32.@)
7667 HRESULT WINAPI
WriteFmtUserTypeStg(
7668 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7671 WCHAR szwClipName
[0x40];
7672 CLSID clsid
= CLSID_NULL
;
7673 LPWSTR wstrProgID
= NULL
;
7676 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7678 /* get the clipboard format name */
7679 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7682 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7684 /* FIXME: There's room to save a CLSID and its ProgID, but
7685 the CLSID is not looked up in the registry and in all the
7686 tests I wrote it was CLSID_NULL. Where does it come from?
7689 /* get the real program ID. This may fail, but that's fine */
7690 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7692 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7694 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7695 lpszUserType
, szwClipName
, wstrProgID
);
7697 CoTaskMemFree(wstrProgID
);
7703 /******************************************************************************
7704 * ReadFmtUserTypeStg [OLE32.@]
7706 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7710 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7711 unsigned char unknown1
[12];
7712 unsigned char unknown2
[16];
7714 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7717 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7719 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7720 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7723 WARN("Failed to open stream r = %08x\n", r
);
7727 /* read the various parts of the structure */
7728 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7729 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7731 r
= ReadClassStm( stm
, &clsid
);
7735 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7739 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7743 r
= STREAM_ReadString( stm
, &szProgIDName
);
7747 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7748 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7751 /* ok, success... now we just need to store what we found */
7753 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7754 CoTaskMemFree( szOleTypeName
);
7756 if( lplpszUserType
)
7757 *lplpszUserType
= szCLSIDName
;
7758 CoTaskMemFree( szProgIDName
);
7761 IStream_Release( stm
);
7767 /*************************************************************************
7768 * OLECONVERT_CreateCompObjStream [Internal]
7770 * Creates a "\001CompObj" is the destination IStorage if necessary.
7773 * pStorage [I] The dest IStorage to create the CompObj Stream
7775 * strOleTypeName [I] The ProgID
7779 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7782 * This function is used by OleConvertOLESTREAMToIStorage only.
7784 * The stream data is stored in the OLESTREAM and there should be
7785 * no need to recreate the stream. If the stream is manually
7786 * deleted it will attempt to create it by querying the registry.
7790 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7793 HRESULT hStorageRes
, hRes
= S_OK
;
7794 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7795 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7796 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7798 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7799 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7801 /* Initialize the CompObj structure */
7802 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7803 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7804 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7807 /* Create a CompObj stream if it doesn't exist */
7808 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7809 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7810 if(hStorageRes
== S_OK
)
7812 /* copy the OleTypeName to the compobj struct */
7813 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7814 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7816 /* copy the OleTypeName to the compobj struct */
7817 /* Note: in the test made, these were Identical */
7818 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7819 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7822 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7823 bufferW
, OLESTREAM_MAX_STR_LEN
);
7824 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7830 /* Get the CLSID Default Name from the Registry */
7831 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7832 if(hErr
== ERROR_SUCCESS
)
7834 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7835 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7836 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7837 if(hErr
== ERROR_SUCCESS
)
7839 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7845 /* Write CompObj Structure to stream */
7846 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7848 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7850 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7851 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7853 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7855 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7856 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7858 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7860 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7861 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7863 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7865 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7866 IStream_Release(pStream
);
7872 /*************************************************************************
7873 * OLECONVERT_CreateOlePresStream[Internal]
7875 * Creates the "\002OlePres000" Stream with the Metafile data
7878 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7879 * dwExtentX [I] Width of the Metafile
7880 * dwExtentY [I] Height of the Metafile
7881 * pData [I] Metafile data
7882 * dwDataLength [I] Size of the Metafile data
7886 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7889 * This function is used by OleConvertOLESTREAMToIStorage only.
7892 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7896 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7897 BYTE pOlePresStreamHeader
[] =
7899 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7900 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7901 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7902 0x00, 0x00, 0x00, 0x00
7905 BYTE pOlePresStreamHeaderEmpty
[] =
7907 0x00, 0x00, 0x00, 0x00,
7908 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7909 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7910 0x00, 0x00, 0x00, 0x00
7913 /* Create the OlePres000 Stream */
7914 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7915 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7920 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7922 memset(&OlePres
, 0, sizeof(OlePres
));
7923 /* Do we have any metafile data to save */
7924 if(dwDataLength
> 0)
7926 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7927 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7931 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7932 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7934 /* Set width and height of the metafile */
7935 OlePres
.dwExtentX
= dwExtentX
;
7936 OlePres
.dwExtentY
= -dwExtentY
;
7938 /* Set Data and Length */
7939 if(dwDataLength
> sizeof(METAFILEPICT16
))
7941 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7942 OlePres
.pData
= &(pData
[8]);
7944 /* Save OlePres000 Data to Stream */
7945 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7946 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7947 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7948 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7949 if(OlePres
.dwSize
> 0)
7951 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7953 IStream_Release(pStream
);
7957 /*************************************************************************
7958 * OLECONVERT_CreateOle10NativeStream [Internal]
7960 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7963 * pStorage [I] Dest storage to create the stream in
7964 * pData [I] Ole10 Native Data (ex. bmp)
7965 * dwDataLength [I] Size of the Ole10 Native Data
7971 * This function is used by OleConvertOLESTREAMToIStorage only.
7973 * Might need to verify the data and return appropriate error message
7976 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7980 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7982 /* Create the Ole10Native Stream */
7983 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7984 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7988 /* Write info to stream */
7989 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7990 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7991 IStream_Release(pStream
);
7996 /*************************************************************************
7997 * OLECONVERT_GetOLE10ProgID [Internal]
7999 * Finds the ProgID (or OleTypeID) from the IStorage
8002 * pStorage [I] The Src IStorage to get the ProgID
8003 * strProgID [I] the ProgID string to get
8004 * dwSize [I] the size of the string
8008 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8011 * This function is used by OleConvertIStorageToOLESTREAM only.
8015 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8019 LARGE_INTEGER iSeekPos
;
8020 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8021 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8023 /* Open the CompObj Stream */
8024 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8025 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8029 /*Get the OleType from the CompObj Stream */
8030 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8031 iSeekPos
.u
.HighPart
= 0;
8033 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8034 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8035 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8036 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8037 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8038 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8039 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8041 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8044 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8046 IStream_Release(pStream
);
8051 LPOLESTR wstrProgID
;
8053 /* Get the OleType from the registry */
8054 REFCLSID clsid
= &(stat
.clsid
);
8055 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8056 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8059 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8066 /*************************************************************************
8067 * OLECONVERT_GetOle10PresData [Internal]
8069 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8072 * pStorage [I] Src IStroage
8073 * pOleStream [I] Dest OleStream Mem Struct
8079 * This function is used by OleConvertIStorageToOLESTREAM only.
8081 * Memory allocated for pData must be freed by the caller
8085 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8090 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8092 /* Initialize Default data for OLESTREAM */
8093 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8094 pOleStreamData
[0].dwTypeID
= 2;
8095 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8096 pOleStreamData
[1].dwTypeID
= 0;
8097 pOleStreamData
[0].dwMetaFileWidth
= 0;
8098 pOleStreamData
[0].dwMetaFileHeight
= 0;
8099 pOleStreamData
[0].pData
= NULL
;
8100 pOleStreamData
[1].pData
= NULL
;
8102 /* Open Ole10Native Stream */
8103 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8104 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8108 /* Read Size and Data */
8109 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8110 if(pOleStreamData
->dwDataLength
> 0)
8112 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8113 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8115 IStream_Release(pStream
);
8121 /*************************************************************************
8122 * OLECONVERT_GetOle20PresData[Internal]
8124 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8127 * pStorage [I] Src IStroage
8128 * pOleStreamData [I] Dest OleStream Mem Struct
8134 * This function is used by OleConvertIStorageToOLESTREAM only.
8136 * Memory allocated for pData must be freed by the caller
8138 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8142 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8143 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8145 /* Initialize Default data for OLESTREAM */
8146 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8147 pOleStreamData
[0].dwTypeID
= 2;
8148 pOleStreamData
[0].dwMetaFileWidth
= 0;
8149 pOleStreamData
[0].dwMetaFileHeight
= 0;
8150 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8151 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8152 pOleStreamData
[1].dwTypeID
= 0;
8153 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8154 pOleStreamData
[1].strOleTypeName
[0] = 0;
8155 pOleStreamData
[1].dwMetaFileWidth
= 0;
8156 pOleStreamData
[1].dwMetaFileHeight
= 0;
8157 pOleStreamData
[1].pData
= NULL
;
8158 pOleStreamData
[1].dwDataLength
= 0;
8161 /* Open OlePress000 stream */
8162 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8163 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8166 LARGE_INTEGER iSeekPos
;
8167 METAFILEPICT16 MetaFilePict
;
8168 static const char strMetafilePictName
[] = "METAFILEPICT";
8170 /* Set the TypeID for a Metafile */
8171 pOleStreamData
[1].dwTypeID
= 5;
8173 /* Set the OleTypeName to Metafile */
8174 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8175 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8177 iSeekPos
.u
.HighPart
= 0;
8178 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8180 /* Get Presentation Data */
8181 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8182 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8183 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8184 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8186 /*Set width and Height */
8187 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8188 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8189 if(olePress
.dwSize
> 0)
8192 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8194 /* Set MetaFilePict struct */
8195 MetaFilePict
.mm
= 8;
8196 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8197 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8198 MetaFilePict
.hMF
= 0;
8200 /* Get Metafile Data */
8201 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8202 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8203 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8205 IStream_Release(pStream
);
8209 /*************************************************************************
8210 * OleConvertOLESTREAMToIStorage [OLE32.@]
8215 * DVTARGETDEVICE parameter is not handled
8216 * Still unsure of some mem fields for OLE 10 Stream
8217 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8218 * and "\001OLE" streams
8221 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8222 LPOLESTREAM pOleStream
,
8224 const DVTARGETDEVICE
* ptd
)
8228 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8230 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8232 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8236 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8239 if(pstg
== NULL
|| pOleStream
== NULL
)
8241 hRes
= E_INVALIDARG
;
8246 /* Load the OLESTREAM to Memory */
8247 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8252 /* Load the OLESTREAM to Memory (part 2)*/
8253 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8259 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8261 /* Do we have the IStorage Data in the OLESTREAM */
8262 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8264 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8265 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8269 /* It must be an original OLE 1.0 source */
8270 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8275 /* It must be an original OLE 1.0 source */
8276 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8279 /* Create CompObj Stream if necessary */
8280 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8283 /*Create the Ole Stream if necessary */
8284 OLECONVERT_CreateOleStream(pstg
);
8289 /* Free allocated memory */
8290 for(i
=0; i
< 2; i
++)
8292 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8293 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8294 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8299 /*************************************************************************
8300 * OleConvertIStorageToOLESTREAM [OLE32.@]
8307 * Still unsure of some mem fields for OLE 10 Stream
8308 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8309 * and "\001OLE" streams.
8312 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8314 LPOLESTREAM pOleStream
)
8317 HRESULT hRes
= S_OK
;
8319 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8320 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8322 TRACE("%p %p\n", pstg
, pOleStream
);
8324 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8326 if(pstg
== NULL
|| pOleStream
== NULL
)
8328 hRes
= E_INVALIDARG
;
8332 /* Get the ProgID */
8333 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8334 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8338 /* Was it originally Ole10 */
8339 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8342 IStream_Release(pStream
);
8343 /* Get Presentation Data for Ole10Native */
8344 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8348 /* Get Presentation Data (OLE20) */
8349 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8352 /* Save OLESTREAM */
8353 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8356 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8361 /* Free allocated memory */
8362 for(i
=0; i
< 2; i
++)
8364 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8370 /***********************************************************************
8371 * GetConvertStg (OLE32.@)
8373 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8374 FIXME("unimplemented stub!\n");
8378 /******************************************************************************
8379 * StgIsStorageFile [OLE32.@]
8380 * Verify if the file contains a storage object
8386 * S_OK if file has magic bytes as a storage object
8387 * S_FALSE if file is not storage
8390 StgIsStorageFile(LPCOLESTR fn
)
8396 TRACE("%s\n", debugstr_w(fn
));
8397 hf
= CreateFileW(fn
, GENERIC_READ
,
8398 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8399 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8401 if (hf
== INVALID_HANDLE_VALUE
)
8402 return STG_E_FILENOTFOUND
;
8404 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8406 WARN(" unable to read file\n");
8413 if (bytes_read
!= 8) {
8414 TRACE(" too short\n");
8418 if (!memcmp(magic
,STORAGE_magic
,8)) {
8423 TRACE(" -> Invalid header.\n");
8427 /***********************************************************************
8428 * WriteClassStm (OLE32.@)
8430 * Writes a CLSID to a stream.
8433 * pStm [I] Stream to write to.
8434 * rclsid [I] CLSID to write.
8438 * Failure: HRESULT code.
8440 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8442 TRACE("(%p,%p)\n",pStm
,rclsid
);
8444 if (!pStm
|| !rclsid
)
8445 return E_INVALIDARG
;
8447 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8450 /***********************************************************************
8451 * ReadClassStm (OLE32.@)
8453 * Reads a CLSID from a stream.
8456 * pStm [I] Stream to read from.
8457 * rclsid [O] CLSID to read.
8461 * Failure: HRESULT code.
8463 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8468 TRACE("(%p,%p)\n",pStm
,pclsid
);
8470 if (!pStm
|| !pclsid
)
8471 return E_INVALIDARG
;
8473 /* clear the output args */
8474 *pclsid
= CLSID_NULL
;
8476 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8481 if (nbByte
!= sizeof(CLSID
))
8482 return STG_E_READFAULT
;