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 typedef struct StorageInternalImpl StorageInternalImpl
;
88 /* Method definitions for the Storage32InternalImpl class. */
89 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageImpl
* ancestorStorage
,
90 DWORD openFlags
, DirRef storageDirEntry
);
91 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
92 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
93 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
94 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
95 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
96 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
98 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
99 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
100 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
101 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
104 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
105 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
106 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
108 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
109 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
110 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
111 ULONG blockIndex
, ULONG offset
, DWORD value
);
112 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
113 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
115 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
116 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
117 static void StorageInternalImpl_Invalidate( StorageInternalImpl
*This
);
119 /* OLESTREAM memory structure to use for Get and Put Routines */
120 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
125 DWORD dwOleTypeNameLength
;
126 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
127 CHAR
*pstrOleObjFileName
;
128 DWORD dwOleObjFileNameLength
;
129 DWORD dwMetaFileWidth
;
130 DWORD dwMetaFileHeight
;
131 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
134 }OLECONVERT_OLESTREAM_DATA
;
136 /* CompObj Stream structure */
137 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
142 DWORD dwCLSIDNameLength
;
143 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
144 DWORD dwOleTypeNameLength
;
145 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
146 DWORD dwProgIDNameLength
;
147 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
149 }OLECONVERT_ISTORAGE_COMPOBJ
;
152 /* Ole Presentation Stream structure */
153 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 }OLECONVERT_ISTORAGE_OLEPRES
;
165 /***********************************************************************
166 * Forward declaration of internal functions used by the method DestroyElement
168 static HRESULT
deleteStorageContents(
169 StorageBaseImpl
*parentStorage
,
170 DirRef indexToDelete
,
171 DirEntry entryDataToDelete
);
173 static HRESULT
deleteStreamContents(
174 StorageBaseImpl
*parentStorage
,
175 DirRef indexToDelete
,
176 DirEntry entryDataToDelete
);
178 static HRESULT
removeFromTree(
180 DirRef parentStorageIndex
,
181 DirRef deletedIndex
);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate DirEntry
187 static HRESULT
destroyDirEntry(
188 StorageImpl
*storage
,
191 static HRESULT
insertIntoTree(
193 DirRef parentStorageIndex
,
194 DirRef newEntryIndex
);
196 static LONG
entryNameCmp(
197 const OLECHAR
*name1
,
198 const OLECHAR
*name2
);
200 static DirRef
findElement(
201 StorageImpl
*storage
,
206 static HRESULT
findTreeParent(
207 StorageImpl
*storage
,
209 const OLECHAR
*childName
,
210 DirEntry
*parentData
,
214 /***********************************************************************
215 * Declaration of miscellaneous functions...
217 static HRESULT
validateSTGM(DWORD stgmValue
);
219 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
220 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
221 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
223 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
226 /****************************************************************************
227 * IEnumSTATSTGImpl definitions.
229 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
230 * This class allows iterating through the content of a storage and to find
231 * specific items inside it.
233 struct IEnumSTATSTGImpl
235 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
236 * since we want to cast this in an IEnumSTATSTG pointer */
238 LONG ref
; /* Reference count */
239 StorageImpl
* parentStorage
; /* Reference to the parent storage */
240 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
243 * The current implementation of the IEnumSTATSTGImpl class uses a stack
244 * to walk the directory entries to get the content of a storage. This stack
245 * is implemented by the following 3 data members
249 DirRef
* stackToVisit
;
251 #define ENUMSTATSGT_SIZE_INCREMENT 10
255 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageImpl
* This
, DirRef storageDirEntry
);
256 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
257 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, DirRef nodeToPush
);
258 static DirRef
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
260 /************************************************************************
264 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
266 if (index
== 0xffffffff)
271 return index
* BIG_BLOCK_SIZE
;
274 /************************************************************************
275 ** Storage32BaseImpl implementation
277 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
278 ULARGE_INTEGER offset
,
283 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
286 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
287 ULARGE_INTEGER offset
,
292 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
295 /************************************************************************
296 * Storage32BaseImpl_QueryInterface (IUnknown)
298 * This method implements the common QueryInterface for all IStorage32
299 * implementations contained in this file.
301 * See Windows documentation for more details on IUnknown methods.
303 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
308 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
310 if ( (This
==0) || (ppvObject
==0) )
315 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
316 IsEqualGUID(&IID_IStorage
, riid
))
320 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
322 *ppvObject
= &This
->pssVtbl
;
326 return E_NOINTERFACE
;
328 IStorage_AddRef(iface
);
333 /************************************************************************
334 * Storage32BaseImpl_AddRef (IUnknown)
336 * This method implements the common AddRef for all IStorage32
337 * implementations contained in this file.
339 * See Windows documentation for more details on IUnknown methods.
341 static ULONG WINAPI
StorageBaseImpl_AddRef(
344 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
345 ULONG ref
= InterlockedIncrement(&This
->ref
);
347 TRACE("(%p) AddRef to %d\n", This
, ref
);
352 /************************************************************************
353 * Storage32BaseImpl_Release (IUnknown)
355 * This method implements the common Release for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI
StorageBaseImpl_Release(
363 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
365 ULONG ref
= InterlockedDecrement(&This
->ref
);
367 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
372 * Since we are using a system of base-classes, we want to call the
373 * destructor of the appropriate derived class. To do this, we are
374 * using virtual functions to implement the destructor.
376 StorageBaseImpl_Destroy(This
);
382 /************************************************************************
383 * Storage32BaseImpl_OpenStream (IStorage)
385 * This method will open the specified stream object from the current storage.
387 * See Windows documentation for more details on IStorage methods.
389 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
391 const OLECHAR
* pwcsName
, /* [string][in] */
392 void* reserved1
, /* [unique][in] */
393 DWORD grfMode
, /* [in] */
394 DWORD reserved2
, /* [in] */
395 IStream
** ppstm
) /* [out] */
397 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
398 StgStreamImpl
* newStream
;
399 DirEntry currentEntry
;
400 DirRef streamEntryRef
;
401 HRESULT res
= STG_E_UNKNOWN
;
403 TRACE("(%p, %s, %p, %x, %d, %p)\n",
404 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
406 if ( (pwcsName
==NULL
) || (ppstm
==0) )
414 if ( FAILED( validateSTGM(grfMode
) ) ||
415 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
417 res
= STG_E_INVALIDFLAG
;
424 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
426 res
= STG_E_INVALIDFUNCTION
;
430 if (!This
->ancestorStorage
)
432 res
= STG_E_REVERTED
;
437 * Check that we're compatible with the parent's storage mode, but
438 * only if we are not in transacted mode
440 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
441 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
443 res
= STG_E_ACCESSDENIED
;
449 * Search for the element with the given name
451 streamEntryRef
= findElement(
452 This
->ancestorStorage
,
453 This
->storageDirEntry
,
458 * If it was found, construct the stream object and return a pointer to it.
460 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
461 (currentEntry
.stgType
==STGTY_STREAM
) )
463 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
465 /* A single stream cannot be opened a second time. */
466 res
= STG_E_ACCESSDENIED
;
470 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
474 newStream
->grfMode
= grfMode
;
475 *ppstm
= (IStream
*)newStream
;
477 IStream_AddRef(*ppstm
);
487 res
= STG_E_FILENOTFOUND
;
491 TRACE("<-- IStream %p\n", *ppstm
);
492 TRACE("<-- %08x\n", res
);
496 /************************************************************************
497 * Storage32BaseImpl_OpenStorage (IStorage)
499 * This method will open a new storage object from the current storage.
501 * See Windows documentation for more details on IStorage methods.
503 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
505 const OLECHAR
* pwcsName
, /* [string][unique][in] */
506 IStorage
* pstgPriority
, /* [unique][in] */
507 DWORD grfMode
, /* [in] */
508 SNB snbExclude
, /* [unique][in] */
509 DWORD reserved
, /* [in] */
510 IStorage
** ppstg
) /* [out] */
512 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
513 StorageInternalImpl
* newStorage
;
514 DirEntry currentEntry
;
515 DirRef storageEntryRef
;
516 HRESULT res
= STG_E_UNKNOWN
;
518 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
519 iface
, debugstr_w(pwcsName
), pstgPriority
,
520 grfMode
, snbExclude
, reserved
, ppstg
);
522 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
528 if (This
->openFlags
& STGM_SIMPLE
)
530 res
= STG_E_INVALIDFUNCTION
;
535 if (snbExclude
!= NULL
)
537 res
= STG_E_INVALIDPARAMETER
;
541 if ( FAILED( validateSTGM(grfMode
) ))
543 res
= STG_E_INVALIDFLAG
;
550 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
551 (grfMode
& STGM_DELETEONRELEASE
) ||
552 (grfMode
& STGM_PRIORITY
) )
554 res
= STG_E_INVALIDFUNCTION
;
558 if (!This
->ancestorStorage
)
559 return STG_E_REVERTED
;
562 * Check that we're compatible with the parent's storage mode,
563 * but only if we are not transacted
565 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
566 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
568 res
= STG_E_ACCESSDENIED
;
575 storageEntryRef
= findElement(
576 This
->ancestorStorage
,
577 This
->storageDirEntry
,
581 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
582 (currentEntry
.stgType
==STGTY_STORAGE
) )
584 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
586 /* A single storage cannot be opened a second time. */
587 res
= STG_E_ACCESSDENIED
;
591 newStorage
= StorageInternalImpl_Construct(
592 This
->ancestorStorage
,
598 *ppstg
= (IStorage
*)newStorage
;
600 StorageBaseImpl_AddRef(*ppstg
);
602 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
608 res
= STG_E_INSUFFICIENTMEMORY
;
612 res
= STG_E_FILENOTFOUND
;
615 TRACE("<-- %08x\n", res
);
619 /************************************************************************
620 * Storage32BaseImpl_EnumElements (IStorage)
622 * This method will create an enumerator object that can be used to
623 * retrieve information about all the elements in the storage object.
625 * See Windows documentation for more details on IStorage methods.
627 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
629 DWORD reserved1
, /* [in] */
630 void* reserved2
, /* [size_is][unique][in] */
631 DWORD reserved3
, /* [in] */
632 IEnumSTATSTG
** ppenum
) /* [out] */
634 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
635 IEnumSTATSTGImpl
* newEnum
;
637 TRACE("(%p, %d, %p, %d, %p)\n",
638 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
640 if ( (This
==0) || (ppenum
==0))
643 if (!This
->ancestorStorage
)
644 return STG_E_REVERTED
;
646 newEnum
= IEnumSTATSTGImpl_Construct(
647 This
->ancestorStorage
,
648 This
->storageDirEntry
);
652 *ppenum
= (IEnumSTATSTG
*)newEnum
;
654 IEnumSTATSTG_AddRef(*ppenum
);
659 return E_OUTOFMEMORY
;
662 /************************************************************************
663 * Storage32BaseImpl_Stat (IStorage)
665 * This method will retrieve information about this storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI
StorageBaseImpl_Stat(
671 STATSTG
* pstatstg
, /* [out] */
672 DWORD grfStatFlag
) /* [in] */
674 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
675 DirEntry currentEntry
;
677 HRESULT res
= STG_E_UNKNOWN
;
679 TRACE("(%p, %p, %x)\n",
680 iface
, pstatstg
, grfStatFlag
);
682 if ( (This
==0) || (pstatstg
==0))
688 if (!This
->ancestorStorage
)
690 res
= STG_E_REVERTED
;
694 readSuccessful
= StorageImpl_ReadDirEntry(
695 This
->ancestorStorage
,
696 This
->storageDirEntry
,
701 StorageUtl_CopyDirEntryToSTATSTG(
707 pstatstg
->grfMode
= This
->openFlags
;
708 pstatstg
->grfStateBits
= This
->stateBits
;
719 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
);
721 TRACE("<-- %08x\n", res
);
725 /************************************************************************
726 * Storage32BaseImpl_RenameElement (IStorage)
728 * This method will rename the specified element.
730 * See Windows documentation for more details on IStorage methods.
732 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
734 const OLECHAR
* pwcsOldName
, /* [in] */
735 const OLECHAR
* pwcsNewName
) /* [in] */
737 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
738 DirEntry currentEntry
;
739 DirRef currentEntryRef
;
741 TRACE("(%p, %s, %s)\n",
742 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
744 if (!This
->ancestorStorage
)
745 return STG_E_REVERTED
;
747 currentEntryRef
= findElement(This
->ancestorStorage
,
748 This
->storageDirEntry
,
752 if (currentEntryRef
!= DIRENTRY_NULL
)
755 * There is already an element with the new name
757 return STG_E_FILEALREADYEXISTS
;
761 * Search for the old element name
763 currentEntryRef
= findElement(This
->ancestorStorage
,
764 This
->storageDirEntry
,
768 if (currentEntryRef
!= DIRENTRY_NULL
)
770 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
771 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
773 WARN("Element is already open; cannot rename.\n");
774 return STG_E_ACCESSDENIED
;
777 /* Remove the element from its current position in the tree */
778 removeFromTree(This
->ancestorStorage
, This
->storageDirEntry
,
781 /* Change the name of the element */
782 strcpyW(currentEntry
.name
, pwcsNewName
);
784 StorageImpl_WriteDirEntry(This
->ancestorStorage
, currentEntryRef
,
787 /* Insert the element in a new position in the tree */
788 insertIntoTree(This
->ancestorStorage
, This
->storageDirEntry
,
794 * There is no element with the old name
796 return STG_E_FILENOTFOUND
;
802 /************************************************************************
803 * Storage32BaseImpl_CreateStream (IStorage)
805 * This method will create a stream object within this storage
807 * See Windows documentation for more details on IStorage methods.
809 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
811 const OLECHAR
* pwcsName
, /* [string][in] */
812 DWORD grfMode
, /* [in] */
813 DWORD reserved1
, /* [in] */
814 DWORD reserved2
, /* [in] */
815 IStream
** ppstm
) /* [out] */
817 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
818 StgStreamImpl
* newStream
;
819 DirEntry currentEntry
, newStreamEntry
;
820 DirRef currentEntryRef
, newStreamEntryRef
;
822 TRACE("(%p, %s, %x, %d, %d, %p)\n",
823 iface
, debugstr_w(pwcsName
), grfMode
,
824 reserved1
, reserved2
, ppstm
);
827 return STG_E_INVALIDPOINTER
;
830 return STG_E_INVALIDNAME
;
832 if (reserved1
|| reserved2
)
833 return STG_E_INVALIDPARAMETER
;
835 if ( FAILED( validateSTGM(grfMode
) ))
836 return STG_E_INVALIDFLAG
;
838 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
839 return STG_E_INVALIDFLAG
;
841 if (!This
->ancestorStorage
)
842 return STG_E_REVERTED
;
847 if ((grfMode
& STGM_DELETEONRELEASE
) ||
848 (grfMode
& STGM_TRANSACTED
))
849 return STG_E_INVALIDFUNCTION
;
851 /* Can't create a stream on read-only storage */
852 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
853 return STG_E_ACCESSDENIED
;
856 * Check that we're compatible with the parent's storage mode
857 * if not in transacted mode
859 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
860 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
861 return STG_E_ACCESSDENIED
;
864 if(This
->openFlags
& STGM_SIMPLE
)
865 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
869 currentEntryRef
= findElement(This
->ancestorStorage
,
870 This
->storageDirEntry
,
874 if (currentEntryRef
!= DIRENTRY_NULL
)
877 * An element with this name already exists
879 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
881 IStorage_DestroyElement(iface
, pwcsName
);
884 return STG_E_FILEALREADYEXISTS
;
886 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
888 WARN("read-only storage\n");
889 return STG_E_ACCESSDENIED
;
893 * memset the empty entry
895 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
897 newStreamEntry
.sizeOfNameString
=
898 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
900 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
901 return STG_E_INVALIDNAME
;
903 strcpyW(newStreamEntry
.name
, pwcsName
);
905 newStreamEntry
.stgType
= STGTY_STREAM
;
906 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
907 newStreamEntry
.size
.u
.LowPart
= 0;
908 newStreamEntry
.size
.u
.HighPart
= 0;
910 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
911 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
912 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
914 /* call CoFileTime to get the current time
919 /* newStreamEntry.clsid */
922 * Create an entry with the new data
924 StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
926 * Insert the new entry in the parent storage's tree.
929 This
->ancestorStorage
,
930 This
->storageDirEntry
,
934 * Open the stream to return it.
936 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
940 *ppstm
= (IStream
*)newStream
;
942 IStream_AddRef(*ppstm
);
946 return STG_E_INSUFFICIENTMEMORY
;
952 /************************************************************************
953 * Storage32BaseImpl_SetClass (IStorage)
955 * This method will write the specified CLSID in the directory entry of this
958 * See Windows documentation for more details on IStorage methods.
960 static HRESULT WINAPI
StorageBaseImpl_SetClass(
962 REFCLSID clsid
) /* [in] */
964 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
965 HRESULT hRes
= E_FAIL
;
966 DirEntry currentEntry
;
969 TRACE("(%p, %p)\n", iface
, clsid
);
971 if (!This
->ancestorStorage
)
972 return STG_E_REVERTED
;
974 success
= StorageImpl_ReadDirEntry(This
->ancestorStorage
,
975 This
->storageDirEntry
,
979 currentEntry
.clsid
= *clsid
;
981 success
= StorageImpl_WriteDirEntry(This
->ancestorStorage
,
982 This
->storageDirEntry
,
991 /************************************************************************
992 ** Storage32Impl implementation
995 /************************************************************************
996 * Storage32BaseImpl_CreateStorage (IStorage)
998 * This method will create the storage object within the provided storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1004 const OLECHAR
*pwcsName
, /* [string][in] */
1005 DWORD grfMode
, /* [in] */
1006 DWORD reserved1
, /* [in] */
1007 DWORD reserved2
, /* [in] */
1008 IStorage
**ppstg
) /* [out] */
1010 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1012 DirEntry currentEntry
;
1014 DirRef currentEntryRef
;
1018 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1019 iface
, debugstr_w(pwcsName
), grfMode
,
1020 reserved1
, reserved2
, ppstg
);
1023 return STG_E_INVALIDPOINTER
;
1025 if (This
->openFlags
& STGM_SIMPLE
)
1027 return STG_E_INVALIDFUNCTION
;
1031 return STG_E_INVALIDNAME
;
1035 if ( FAILED( validateSTGM(grfMode
) ) ||
1036 (grfMode
& STGM_DELETEONRELEASE
) )
1038 WARN("bad grfMode: 0x%x\n", grfMode
);
1039 return STG_E_INVALIDFLAG
;
1042 if (!This
->ancestorStorage
)
1043 return STG_E_REVERTED
;
1046 * Check that we're compatible with the parent's storage mode
1048 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1050 WARN("access denied\n");
1051 return STG_E_ACCESSDENIED
;
1054 currentEntryRef
= findElement(This
->ancestorStorage
,
1055 This
->storageDirEntry
,
1059 if (currentEntryRef
!= DIRENTRY_NULL
)
1062 * An element with this name already exists
1064 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1065 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
)
1067 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1073 WARN("file already exists\n");
1074 return STG_E_FILEALREADYEXISTS
;
1077 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1079 WARN("read-only storage\n");
1080 return STG_E_ACCESSDENIED
;
1083 memset(&newEntry
, 0, sizeof(DirEntry
));
1085 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1087 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1089 FIXME("name too long\n");
1090 return STG_E_INVALIDNAME
;
1093 strcpyW(newEntry
.name
, pwcsName
);
1095 newEntry
.stgType
= STGTY_STORAGE
;
1096 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1097 newEntry
.size
.u
.LowPart
= 0;
1098 newEntry
.size
.u
.HighPart
= 0;
1100 newEntry
.leftChild
= DIRENTRY_NULL
;
1101 newEntry
.rightChild
= DIRENTRY_NULL
;
1102 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1104 /* call CoFileTime to get the current time
1109 /* newEntry.clsid */
1112 * Create a new directory entry for the storage
1114 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1117 * Insert the new directory entry into the parent storage's tree
1120 This
->ancestorStorage
,
1121 This
->storageDirEntry
,
1125 * Open it to get a pointer to return.
1127 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1129 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1139 /***************************************************************************
1143 * Reserve a directory entry in the file and initialize it.
1145 static HRESULT
StorageImpl_CreateDirEntry(
1146 StorageBaseImpl
*base
,
1147 const DirEntry
*newData
,
1150 StorageImpl
*storage
= (StorageImpl
*)base
;
1151 ULONG currentEntryIndex
= 0;
1152 ULONG newEntryIndex
= DIRENTRY_NULL
;
1154 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1155 WORD sizeOfNameString
;
1159 hr
= StorageImpl_ReadRawDirEntry(storage
,
1165 StorageUtl_ReadWord(
1167 OFFSET_PS_NAMELENGTH
,
1170 if (sizeOfNameString
== 0)
1173 * The entry exists and is available, we found it.
1175 newEntryIndex
= currentEntryIndex
;
1181 * We exhausted the directory entries, we will create more space below
1183 newEntryIndex
= currentEntryIndex
;
1185 currentEntryIndex
++;
1187 } while (newEntryIndex
== DIRENTRY_NULL
);
1190 * grow the directory stream
1194 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1195 ULARGE_INTEGER newSize
;
1197 ULONG lastEntry
= 0;
1198 ULONG blockCount
= 0;
1201 * obtain the new count of blocks in the directory stream
1203 blockCount
= BlockChainStream_GetCount(
1204 storage
->rootBlockChain
)+1;
1207 * initialize the size used by the directory stream
1209 newSize
.u
.HighPart
= 0;
1210 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1213 * add a block to the directory stream
1215 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1218 * memset the empty entry in order to initialize the unused newly
1221 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1226 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1229 entryIndex
= newEntryIndex
+ 1;
1230 entryIndex
< lastEntry
;
1233 StorageImpl_WriteRawDirEntry(
1240 UpdateRawDirEntry(currentData
, newData
);
1242 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1245 *index
= newEntryIndex
;
1250 /***************************************************************************
1254 * Mark a directory entry in the file as free.
1256 static HRESULT
destroyDirEntry(
1257 StorageImpl
*storage
,
1261 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1263 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1265 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1271 /****************************************************************************
1275 * Case insensitive comparison of DirEntry.name by first considering
1278 * Returns <0 when name1 < name2
1279 * >0 when name1 > name2
1280 * 0 when name1 == name2
1282 static LONG
entryNameCmp(
1283 const OLECHAR
*name1
,
1284 const OLECHAR
*name2
)
1286 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1291 * We compare the string themselves only when they are of the same length
1293 diff
= lstrcmpiW( name1
, name2
);
1299 /****************************************************************************
1303 * Add a directory entry to a storage
1305 static HRESULT
insertIntoTree(
1307 DirRef parentStorageIndex
,
1308 DirRef newEntryIndex
)
1310 DirEntry currentEntry
;
1314 * Read the inserted entry
1316 StorageImpl_ReadDirEntry(This
,
1321 * Read the storage entry
1323 StorageImpl_ReadDirEntry(This
,
1327 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1330 * The root storage contains some element, therefore, start the research
1331 * for the appropriate location.
1334 DirRef current
, next
, previous
, currentEntryId
;
1337 * Keep a reference to the root of the storage's element tree
1339 currentEntryId
= currentEntry
.dirRootEntry
;
1344 StorageImpl_ReadDirEntry(This
,
1345 currentEntry
.dirRootEntry
,
1348 previous
= currentEntry
.leftChild
;
1349 next
= currentEntry
.rightChild
;
1350 current
= currentEntryId
;
1354 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1358 if (previous
!= DIRENTRY_NULL
)
1360 StorageImpl_ReadDirEntry(This
,
1367 currentEntry
.leftChild
= newEntryIndex
;
1368 StorageImpl_WriteDirEntry(This
,
1376 if (next
!= DIRENTRY_NULL
)
1378 StorageImpl_ReadDirEntry(This
,
1385 currentEntry
.rightChild
= newEntryIndex
;
1386 StorageImpl_WriteDirEntry(This
,
1395 * Trying to insert an item with the same name in the
1396 * subtree structure.
1398 return STG_E_FILEALREADYEXISTS
;
1401 previous
= currentEntry
.leftChild
;
1402 next
= currentEntry
.rightChild
;
1408 * The storage is empty, make the new entry the root of its element tree
1410 currentEntry
.dirRootEntry
= newEntryIndex
;
1411 StorageImpl_WriteDirEntry(This
,
1419 /****************************************************************************
1423 * Find and read the element of a storage with the given name.
1425 static DirRef
findElement(StorageImpl
*storage
, DirRef storageEntry
,
1426 const OLECHAR
*name
, DirEntry
*data
)
1428 DirRef currentEntry
;
1430 /* Read the storage entry to find the root of the tree. */
1431 StorageImpl_ReadDirEntry(storage
, storageEntry
, data
);
1433 currentEntry
= data
->dirRootEntry
;
1435 while (currentEntry
!= DIRENTRY_NULL
)
1439 StorageImpl_ReadDirEntry(storage
, currentEntry
, data
);
1441 cmp
= entryNameCmp(name
, data
->name
);
1448 currentEntry
= data
->leftChild
;
1451 currentEntry
= data
->rightChild
;
1454 return currentEntry
;
1457 /****************************************************************************
1461 * Find and read the binary tree parent of the element with the given name.
1463 * If there is no such element, find a place where it could be inserted and
1464 * return STG_E_FILENOTFOUND.
1466 static HRESULT
findTreeParent(StorageImpl
*storage
, DirRef storageEntry
,
1467 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1473 /* Read the storage entry to find the root of the tree. */
1474 StorageImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1476 *parentEntry
= storageEntry
;
1477 *relation
= DIRENTRY_RELATION_DIR
;
1479 childEntry
= parentData
->dirRootEntry
;
1481 while (childEntry
!= DIRENTRY_NULL
)
1485 StorageImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1487 cmp
= entryNameCmp(childName
, childData
.name
);
1495 *parentData
= childData
;
1496 *parentEntry
= childEntry
;
1497 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1499 childEntry
= parentData
->leftChild
;
1504 *parentData
= childData
;
1505 *parentEntry
= childEntry
;
1506 *relation
= DIRENTRY_RELATION_NEXT
;
1508 childEntry
= parentData
->rightChild
;
1512 if (childEntry
== DIRENTRY_NULL
)
1513 return STG_E_FILENOTFOUND
;
1519 /*************************************************************************
1522 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1524 DWORD ciidExclude
, /* [in] */
1525 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1526 SNB snbExclude
, /* [unique][in] */
1527 IStorage
* pstgDest
) /* [unique][in] */
1529 IEnumSTATSTG
*elements
= 0;
1530 STATSTG curElement
, strStat
;
1532 IStorage
*pstgTmp
, *pstgChild
;
1533 IStream
*pstrTmp
, *pstrChild
;
1534 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1537 TRACE("(%p, %d, %p, %p, %p)\n",
1538 iface
, ciidExclude
, rgiidExclude
,
1539 snbExclude
, pstgDest
);
1541 if ( pstgDest
== 0 )
1542 return STG_E_INVALIDPOINTER
;
1545 * Enumerate the elements
1547 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1555 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1556 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1558 for(i
= 0; i
< ciidExclude
; ++i
)
1560 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1561 skip_storage
= TRUE
;
1562 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1565 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1571 * Obtain the next element
1573 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1575 if ( hr
== S_FALSE
)
1577 hr
= S_OK
; /* done, every element has been copied */
1583 WCHAR
**snb
= snbExclude
;
1585 while ( *snb
!= NULL
&& !skip
)
1587 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1596 if (curElement
.type
== STGTY_STORAGE
)
1602 * open child source storage
1604 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1605 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1606 NULL
, 0, &pstgChild
);
1612 * Check if destination storage is not a child of the source
1613 * storage, which will cause an infinite loop
1615 if (pstgChild
== pstgDest
)
1617 IEnumSTATSTG_Release(elements
);
1619 return STG_E_ACCESSDENIED
;
1623 * create a new storage in destination storage
1625 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1626 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1630 * if it already exist, don't create a new one use this one
1632 if (hr
== STG_E_FILEALREADYEXISTS
)
1634 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1635 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1636 NULL
, 0, &pstgTmp
);
1644 * do the copy recursively
1646 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1649 IStorage_Release( pstgTmp
);
1650 IStorage_Release( pstgChild
);
1652 else if (curElement
.type
== STGTY_STREAM
)
1658 * create a new stream in destination storage. If the stream already
1659 * exist, it will be deleted and a new one will be created.
1661 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1662 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1669 * open child stream storage
1671 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1672 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1679 * Get the size of the source stream
1681 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1684 * Set the size of the destination stream.
1686 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1691 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1694 IStream_Release( pstrTmp
);
1695 IStream_Release( pstrChild
);
1699 WARN("unknown element type: %d\n", curElement
.type
);
1702 } while (hr
== S_OK
);
1707 IEnumSTATSTG_Release(elements
);
1712 /*************************************************************************
1713 * MoveElementTo (IStorage)
1715 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1717 const OLECHAR
*pwcsName
, /* [string][in] */
1718 IStorage
*pstgDest
, /* [unique][in] */
1719 const OLECHAR
*pwcsNewName
,/* [string][in] */
1720 DWORD grfFlags
) /* [in] */
1722 FIXME("(%p %s %p %s %u): stub\n", iface
,
1723 debugstr_w(pwcsName
), pstgDest
,
1724 debugstr_w(pwcsNewName
), grfFlags
);
1728 /*************************************************************************
1731 * Ensures that any changes made to a storage object open in transacted mode
1732 * are reflected in the parent storage
1735 * Wine doesn't implement transacted mode, which seems to be a basic
1736 * optimization, so we can ignore this stub for now.
1738 static HRESULT WINAPI
StorageImpl_Commit(
1740 DWORD grfCommitFlags
)/* [in] */
1742 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1746 /*************************************************************************
1749 * Discard all changes that have been made since the last commit operation
1751 static HRESULT WINAPI
StorageImpl_Revert(
1754 FIXME("(%p): stub\n", iface
);
1758 /*************************************************************************
1759 * DestroyElement (IStorage)
1761 * Strategy: This implementation is built this way for simplicity not for speed.
1762 * I always delete the topmost element of the enumeration and adjust
1763 * the deleted element pointer all the time. This takes longer to
1764 * do but allow to reinvoke DestroyElement whenever we encounter a
1765 * storage object. The optimisation resides in the usage of another
1766 * enumeration strategy that would give all the leaves of a storage
1767 * first. (postfix order)
1769 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1771 const OLECHAR
*pwcsName
)/* [string][in] */
1773 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1776 DirEntry entryToDelete
;
1777 DirRef entryToDeleteRef
;
1780 iface
, debugstr_w(pwcsName
));
1783 return STG_E_INVALIDPOINTER
;
1785 if (!This
->ancestorStorage
)
1786 return STG_E_REVERTED
;
1788 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1789 return STG_E_ACCESSDENIED
;
1791 entryToDeleteRef
= findElement(
1792 This
->ancestorStorage
,
1793 This
->storageDirEntry
,
1797 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1799 return STG_E_FILENOTFOUND
;
1802 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1804 hr
= deleteStorageContents(
1809 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1811 hr
= deleteStreamContents(
1821 * Remove the entry from its parent storage
1823 hr
= removeFromTree(
1824 This
->ancestorStorage
,
1825 This
->storageDirEntry
,
1829 * Invalidate the entry
1832 destroyDirEntry(This
->ancestorStorage
,
1839 /******************************************************************************
1840 * Internal stream list handlers
1843 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1845 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1846 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1849 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1851 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1852 list_remove(&(strm
->StrmListEntry
));
1855 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1857 StgStreamImpl
*strm
;
1859 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1861 if (strm
->dirEntry
== streamEntry
)
1870 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1872 StorageInternalImpl
*childstg
;
1874 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1876 if (childstg
->base
.storageDirEntry
== storageEntry
)
1885 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1887 struct list
*cur
, *cur2
;
1888 StgStreamImpl
*strm
=NULL
;
1889 StorageInternalImpl
*childstg
=NULL
;
1891 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1892 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1893 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1894 strm
->parentStorage
= NULL
;
1898 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1899 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1900 StorageInternalImpl_Invalidate( childstg
);
1905 /*********************************************************************
1909 * Delete the contents of a storage entry.
1912 static HRESULT
deleteStorageContents(
1913 StorageBaseImpl
*parentStorage
,
1914 DirRef indexToDelete
,
1915 DirEntry entryDataToDelete
)
1917 IEnumSTATSTG
*elements
= 0;
1918 IStorage
*childStorage
= 0;
1919 STATSTG currentElement
;
1921 HRESULT destroyHr
= S_OK
;
1922 StorageInternalImpl
*stg
, *stg2
;
1924 /* Invalidate any open storage objects. */
1925 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1927 if (stg
->base
.storageDirEntry
== indexToDelete
)
1929 StorageInternalImpl_Invalidate(stg
);
1934 * Open the storage and enumerate it
1936 hr
= StorageBaseImpl_OpenStorage(
1937 (IStorage
*)parentStorage
,
1938 entryDataToDelete
.name
,
1940 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1951 * Enumerate the elements
1953 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1958 * Obtain the next element
1960 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1963 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
1965 CoTaskMemFree(currentElement
.pwcsName
);
1969 * We need to Reset the enumeration every time because we delete elements
1970 * and the enumeration could be invalid
1972 IEnumSTATSTG_Reset(elements
);
1974 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1976 IStorage_Release(childStorage
);
1977 IEnumSTATSTG_Release(elements
);
1982 /*********************************************************************
1986 * Perform the deletion of a stream's data
1989 static HRESULT
deleteStreamContents(
1990 StorageBaseImpl
*parentStorage
,
1991 DirRef indexToDelete
,
1992 DirEntry entryDataToDelete
)
1996 ULARGE_INTEGER size
;
1997 StgStreamImpl
*strm
, *strm2
;
1999 /* Invalidate any open stream objects. */
2000 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2002 if (strm
->dirEntry
== indexToDelete
)
2004 TRACE("Stream deleted %p\n", strm
);
2005 strm
->parentStorage
= NULL
;
2006 list_remove(&strm
->StrmListEntry
);
2010 size
.u
.HighPart
= 0;
2013 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2014 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2024 hr
= IStream_SetSize(pis
, size
);
2032 * Release the stream object.
2034 IStream_Release(pis
);
2039 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2043 case DIRENTRY_RELATION_PREVIOUS
:
2044 entry
->leftChild
= new_target
;
2046 case DIRENTRY_RELATION_NEXT
:
2047 entry
->rightChild
= new_target
;
2049 case DIRENTRY_RELATION_DIR
:
2050 entry
->dirRootEntry
= new_target
;
2057 /*************************************************************************
2061 * This method removes a directory entry from its parent storage tree without
2062 * freeing any resources attached to it.
2064 static HRESULT
removeFromTree(
2066 DirRef parentStorageIndex
,
2067 DirRef deletedIndex
)
2071 DirEntry entryToDelete
;
2072 DirEntry parentEntry
;
2073 DirRef parentEntryRef
;
2074 ULONG typeOfRelation
;
2076 res
= StorageImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2079 * Find the element that links to the one we want to delete.
2081 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2082 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2087 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2090 * Replace the deleted entry with its left child
2092 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2094 res
= StorageImpl_WriteDirEntry(
2103 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2106 * We need to reinsert the right child somewhere. We already know it and
2107 * its children are greater than everything in the left tree, so we
2108 * insert it at the rightmost point in the left tree.
2110 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2111 DirEntry newRightChildParentEntry
;
2115 res
= StorageImpl_ReadDirEntry(
2117 newRightChildParent
,
2118 &newRightChildParentEntry
);
2124 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2125 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2126 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2128 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2130 res
= StorageImpl_WriteDirEntry(
2132 newRightChildParent
,
2133 &newRightChildParentEntry
);
2143 * Replace the deleted entry with its right child
2145 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2147 res
= StorageImpl_WriteDirEntry(
2161 /******************************************************************************
2162 * SetElementTimes (IStorage)
2164 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2166 const OLECHAR
*pwcsName
,/* [string][in] */
2167 const FILETIME
*pctime
, /* [in] */
2168 const FILETIME
*patime
, /* [in] */
2169 const FILETIME
*pmtime
) /* [in] */
2171 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2175 /******************************************************************************
2176 * SetStateBits (IStorage)
2178 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2180 DWORD grfStateBits
,/* [in] */
2181 DWORD grfMask
) /* [in] */
2183 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2185 if (!This
->ancestorStorage
)
2186 return STG_E_REVERTED
;
2188 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2193 * Virtual function table for the IStorage32Impl class.
2195 static const IStorageVtbl Storage32Impl_Vtbl
=
2197 StorageBaseImpl_QueryInterface
,
2198 StorageBaseImpl_AddRef
,
2199 StorageBaseImpl_Release
,
2200 StorageBaseImpl_CreateStream
,
2201 StorageBaseImpl_OpenStream
,
2202 StorageBaseImpl_CreateStorage
,
2203 StorageBaseImpl_OpenStorage
,
2204 StorageBaseImpl_CopyTo
,
2205 StorageBaseImpl_MoveElementTo
,
2208 StorageBaseImpl_EnumElements
,
2209 StorageBaseImpl_DestroyElement
,
2210 StorageBaseImpl_RenameElement
,
2211 StorageBaseImpl_SetElementTimes
,
2212 StorageBaseImpl_SetClass
,
2213 StorageBaseImpl_SetStateBits
,
2214 StorageBaseImpl_Stat
2217 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2219 StorageImpl_Destroy
,
2220 StorageImpl_CreateDirEntry
2223 static HRESULT
StorageImpl_Construct(
2230 StorageImpl
** result
)
2234 DirEntry currentEntry
;
2235 BOOL readSuccessful
;
2236 DirRef currentEntryRef
;
2238 if ( FAILED( validateSTGM(openFlags
) ))
2239 return STG_E_INVALIDFLAG
;
2241 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2243 return E_OUTOFMEMORY
;
2245 memset(This
, 0, sizeof(StorageImpl
));
2247 list_init(&This
->base
.strmHead
);
2249 list_init(&This
->base
.storageHead
);
2251 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2252 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2253 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2254 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2256 This
->base
.create
= create
;
2259 * This is the top-level storage so initialize the ancestor pointer
2262 This
->base
.ancestorStorage
= This
;
2264 This
->hFile
= hFile
;
2267 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2268 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2269 if (!This
->pwcsName
)
2271 hr
= STG_E_INSUFFICIENTMEMORY
;
2274 strcpyW(This
->pwcsName
, pwcsName
);
2276 memcpy(This
->base
.filename
, pwcsName
, DIRENTRY_NAME_BUFFER_LEN
-1);
2277 This
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = 0;
2281 * Initialize the big block cache.
2283 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2284 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2285 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2291 if (This
->bigBlockFile
== 0)
2299 ULARGE_INTEGER size
;
2300 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2303 * Initialize all header variables:
2304 * - The big block depot consists of one block and it is at block 0
2305 * - The directory table starts at block 1
2306 * - There is no small block depot
2308 memset( This
->bigBlockDepotStart
,
2310 sizeof(This
->bigBlockDepotStart
));
2312 This
->bigBlockDepotCount
= 1;
2313 This
->bigBlockDepotStart
[0] = 0;
2314 This
->rootStartBlock
= 1;
2315 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2316 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2317 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2318 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2319 This
->extBigBlockDepotCount
= 0;
2321 StorageImpl_SaveFileHeader(This
);
2324 * Add one block for the big block depot and one block for the directory table
2326 size
.u
.HighPart
= 0;
2327 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2328 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2331 * Initialize the big block depot
2333 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2334 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2335 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2336 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2341 * Load the header for the file.
2343 hr
= StorageImpl_LoadFileHeader(This
);
2352 * There is no block depot cached yet.
2354 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2357 * Start searching for free blocks with block 0.
2359 This
->prevFreeBlock
= 0;
2362 * Create the block chain abstractions.
2364 if(!(This
->rootBlockChain
=
2365 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2367 hr
= STG_E_READFAULT
;
2371 if(!(This
->smallBlockDepotChain
=
2372 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2375 hr
= STG_E_READFAULT
;
2380 * Write the root storage entry (memory only)
2386 * Initialize the directory table
2388 memset(&rootEntry
, 0, sizeof(rootEntry
));
2389 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2390 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2391 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2392 rootEntry
.stgType
= STGTY_ROOT
;
2393 rootEntry
.leftChild
= DIRENTRY_NULL
;
2394 rootEntry
.rightChild
= DIRENTRY_NULL
;
2395 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2396 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2397 rootEntry
.size
.u
.HighPart
= 0;
2398 rootEntry
.size
.u
.LowPart
= 0;
2400 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2404 * Find the ID of the root storage.
2406 currentEntryRef
= 0;
2410 readSuccessful
= StorageImpl_ReadDirEntry(
2417 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2418 (currentEntry
.stgType
== STGTY_ROOT
) )
2420 This
->base
.storageDirEntry
= currentEntryRef
;
2426 } while (readSuccessful
&& (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2428 if (!readSuccessful
)
2430 hr
= STG_E_READFAULT
;
2435 * Create the block chain abstraction for the small block root chain.
2437 if(!(This
->smallBlockRootChain
=
2438 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2440 hr
= STG_E_READFAULT
;
2446 IStorage_Release((IStorage
*)This
);
2455 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2457 StorageImpl
*This
= (StorageImpl
*) iface
;
2458 TRACE("(%p)\n", This
);
2460 StorageBaseImpl_DeleteAll(&This
->base
);
2462 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2464 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2465 BlockChainStream_Destroy(This
->rootBlockChain
);
2466 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2468 if (This
->bigBlockFile
)
2469 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2470 HeapFree(GetProcessHeap(), 0, This
);
2473 /******************************************************************************
2474 * Storage32Impl_GetNextFreeBigBlock
2476 * Returns the index of the next free big block.
2477 * If the big block depot is filled, this method will enlarge it.
2480 static ULONG
StorageImpl_GetNextFreeBigBlock(
2483 ULONG depotBlockIndexPos
;
2484 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2486 ULONG depotBlockOffset
;
2487 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2488 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2490 ULONG freeBlock
= BLOCK_UNUSED
;
2492 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2493 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2496 * Scan the entire big block depot until we find a block marked free
2498 while (nextBlockIndex
!= BLOCK_UNUSED
)
2500 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2502 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2505 * Grow the primary depot.
2507 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2509 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2512 * Add a block depot.
2514 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2515 This
->bigBlockDepotCount
++;
2516 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2519 * Flag it as a block depot.
2521 StorageImpl_SetNextBlockInChain(This
,
2525 /* Save new header information.
2527 StorageImpl_SaveFileHeader(This
);
2532 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2534 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2537 * Grow the extended depot.
2539 ULONG extIndex
= BLOCK_UNUSED
;
2540 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2541 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2543 if (extBlockOffset
== 0)
2545 /* We need an extended block.
2547 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2548 This
->extBigBlockDepotCount
++;
2549 depotBlockIndexPos
= extIndex
+ 1;
2552 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2555 * Add a block depot and mark it in the extended block.
2557 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2558 This
->bigBlockDepotCount
++;
2559 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2561 /* Flag the block depot.
2563 StorageImpl_SetNextBlockInChain(This
,
2567 /* If necessary, flag the extended depot block.
2569 if (extIndex
!= BLOCK_UNUSED
)
2570 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2572 /* Save header information.
2574 StorageImpl_SaveFileHeader(This
);
2578 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2582 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2583 ( nextBlockIndex
!= BLOCK_UNUSED
))
2585 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2587 if (nextBlockIndex
== BLOCK_UNUSED
)
2589 freeBlock
= (depotIndex
* blocksPerDepot
) +
2590 (depotBlockOffset
/sizeof(ULONG
));
2593 depotBlockOffset
+= sizeof(ULONG
);
2598 depotBlockOffset
= 0;
2602 * make sure that the block physically exists before using it
2604 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2606 This
->prevFreeBlock
= freeBlock
;
2611 /******************************************************************************
2612 * Storage32Impl_AddBlockDepot
2614 * This will create a depot block, essentially it is a block initialized
2617 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2619 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2622 * Initialize blocks as free
2624 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2625 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2628 /******************************************************************************
2629 * Storage32Impl_GetExtDepotBlock
2631 * Returns the index of the block that corresponds to the specified depot
2632 * index. This method is only for depot indexes equal or greater than
2633 * COUNT_BBDEPOTINHEADER.
2635 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2637 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2638 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2639 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2640 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2641 ULONG blockIndex
= BLOCK_UNUSED
;
2642 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2644 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2646 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2647 return BLOCK_UNUSED
;
2649 while (extBlockCount
> 0)
2651 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2655 if (extBlockIndex
!= BLOCK_UNUSED
)
2656 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2657 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2662 /******************************************************************************
2663 * Storage32Impl_SetExtDepotBlock
2665 * Associates the specified block index to the specified depot index.
2666 * This method is only for depot indexes equal or greater than
2667 * COUNT_BBDEPOTINHEADER.
2669 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2671 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2672 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2673 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2674 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2675 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2677 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2679 while (extBlockCount
> 0)
2681 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2685 if (extBlockIndex
!= BLOCK_UNUSED
)
2687 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2688 extBlockOffset
* sizeof(ULONG
),
2693 /******************************************************************************
2694 * Storage32Impl_AddExtBlockDepot
2696 * Creates an extended depot block.
2698 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2700 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2701 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2702 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2703 ULONG index
= BLOCK_UNUSED
;
2704 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2705 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2706 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2708 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2709 blocksPerDepotBlock
;
2711 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2714 * The first extended block.
2716 This
->extBigBlockDepotStart
= index
;
2722 * Follow the chain to the last one.
2724 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2726 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2730 * Add the new extended block to the chain.
2732 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2737 * Initialize this block.
2739 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2740 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2745 /******************************************************************************
2746 * Storage32Impl_FreeBigBlock
2748 * This method will flag the specified block as free in the big block depot.
2750 static void StorageImpl_FreeBigBlock(
2754 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2756 if (blockIndex
< This
->prevFreeBlock
)
2757 This
->prevFreeBlock
= blockIndex
;
2760 /************************************************************************
2761 * Storage32Impl_GetNextBlockInChain
2763 * This method will retrieve the block index of the next big block in
2766 * Params: This - Pointer to the Storage object.
2767 * blockIndex - Index of the block to retrieve the chain
2769 * nextBlockIndex - receives the return value.
2771 * Returns: This method returns the index of the next block in the chain.
2772 * It will return the constants:
2773 * BLOCK_SPECIAL - If the block given was not part of a
2775 * BLOCK_END_OF_CHAIN - If the block given was the last in
2777 * BLOCK_UNUSED - If the block given was not past of a chain
2779 * BLOCK_EXTBBDEPOT - This block is part of the extended
2782 * See Windows documentation for more details on IStorage methods.
2784 static HRESULT
StorageImpl_GetNextBlockInChain(
2787 ULONG
* nextBlockIndex
)
2789 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2790 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2791 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2792 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2794 ULONG depotBlockIndexPos
;
2797 *nextBlockIndex
= BLOCK_SPECIAL
;
2799 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2801 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2802 This
->bigBlockDepotCount
);
2803 return STG_E_READFAULT
;
2807 * Cache the currently accessed depot block.
2809 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2811 This
->indexBlockDepotCached
= depotBlockCount
;
2813 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2815 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2820 * We have to look in the extended depot.
2822 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2825 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2828 return STG_E_READFAULT
;
2830 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2832 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2833 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2837 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2842 /******************************************************************************
2843 * Storage32Impl_GetNextExtendedBlock
2845 * Given an extended block this method will return the next extended block.
2848 * The last ULONG of an extended block is the block index of the next
2849 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2853 * - The index of the next extended block
2854 * - BLOCK_UNUSED: there is no next extended block.
2855 * - Any other return values denotes failure.
2857 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2859 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2860 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2862 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
2865 return nextBlockIndex
;
2868 /******************************************************************************
2869 * Storage32Impl_SetNextBlockInChain
2871 * This method will write the index of the specified block's next block
2872 * in the big block depot.
2874 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2877 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2878 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2879 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2882 static void StorageImpl_SetNextBlockInChain(
2887 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2888 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2889 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2890 ULONG depotBlockIndexPos
;
2892 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2893 assert(blockIndex
!= nextBlock
);
2895 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2897 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2902 * We have to look in the extended depot.
2904 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2907 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
2910 * Update the cached block depot, if necessary.
2912 if (depotBlockCount
== This
->indexBlockDepotCached
)
2914 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2918 /******************************************************************************
2919 * Storage32Impl_LoadFileHeader
2921 * This method will read in the file header, i.e. big block index -1.
2923 static HRESULT
StorageImpl_LoadFileHeader(
2926 HRESULT hr
= STG_E_FILENOTFOUND
;
2927 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2933 * Get a pointer to the big block of data containing the header.
2935 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2938 * Extract the information from the header.
2943 * Check for the "magic number" signature and return an error if it is not
2946 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2948 return STG_E_OLDFORMAT
;
2951 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2953 return STG_E_INVALIDHEADER
;
2956 StorageUtl_ReadWord(
2958 OFFSET_BIGBLOCKSIZEBITS
,
2959 &This
->bigBlockSizeBits
);
2961 StorageUtl_ReadWord(
2963 OFFSET_SMALLBLOCKSIZEBITS
,
2964 &This
->smallBlockSizeBits
);
2966 StorageUtl_ReadDWord(
2968 OFFSET_BBDEPOTCOUNT
,
2969 &This
->bigBlockDepotCount
);
2971 StorageUtl_ReadDWord(
2973 OFFSET_ROOTSTARTBLOCK
,
2974 &This
->rootStartBlock
);
2976 StorageUtl_ReadDWord(
2978 OFFSET_SBDEPOTSTART
,
2979 &This
->smallBlockDepotStart
);
2981 StorageUtl_ReadDWord(
2983 OFFSET_EXTBBDEPOTSTART
,
2984 &This
->extBigBlockDepotStart
);
2986 StorageUtl_ReadDWord(
2988 OFFSET_EXTBBDEPOTCOUNT
,
2989 &This
->extBigBlockDepotCount
);
2991 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2993 StorageUtl_ReadDWord(
2995 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2996 &(This
->bigBlockDepotStart
[index
]));
3000 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3002 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3003 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3006 * Right now, the code is making some assumptions about the size of the
3007 * blocks, just make sure they are what we're expecting.
3009 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3010 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3012 WARN("Broken OLE storage file\n");
3013 hr
= STG_E_INVALIDHEADER
;
3022 /******************************************************************************
3023 * Storage32Impl_SaveFileHeader
3025 * This method will save to the file the header, i.e. big block -1.
3027 static void StorageImpl_SaveFileHeader(
3030 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3035 * Get a pointer to the big block of data containing the header.
3037 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3040 * If the block read failed, the file is probably new.
3045 * Initialize for all unknown fields.
3047 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3050 * Initialize the magic number.
3052 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3055 * And a bunch of things we don't know what they mean
3057 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3058 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3059 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3060 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3064 * Write the information to the header.
3066 StorageUtl_WriteWord(
3068 OFFSET_BIGBLOCKSIZEBITS
,
3069 This
->bigBlockSizeBits
);
3071 StorageUtl_WriteWord(
3073 OFFSET_SMALLBLOCKSIZEBITS
,
3074 This
->smallBlockSizeBits
);
3076 StorageUtl_WriteDWord(
3078 OFFSET_BBDEPOTCOUNT
,
3079 This
->bigBlockDepotCount
);
3081 StorageUtl_WriteDWord(
3083 OFFSET_ROOTSTARTBLOCK
,
3084 This
->rootStartBlock
);
3086 StorageUtl_WriteDWord(
3088 OFFSET_SBDEPOTSTART
,
3089 This
->smallBlockDepotStart
);
3091 StorageUtl_WriteDWord(
3093 OFFSET_SBDEPOTCOUNT
,
3094 This
->smallBlockDepotChain
?
3095 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3097 StorageUtl_WriteDWord(
3099 OFFSET_EXTBBDEPOTSTART
,
3100 This
->extBigBlockDepotStart
);
3102 StorageUtl_WriteDWord(
3104 OFFSET_EXTBBDEPOTCOUNT
,
3105 This
->extBigBlockDepotCount
);
3107 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3109 StorageUtl_WriteDWord(
3111 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3112 (This
->bigBlockDepotStart
[index
]));
3116 * Write the big block back to the file.
3118 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3121 /******************************************************************************
3122 * StorageImpl_ReadRawDirEntry
3124 * This method will read the raw data from a directory entry in the file.
3126 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3128 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3130 ULARGE_INTEGER offset
;
3134 offset
.u
.HighPart
= 0;
3135 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3137 hr
= BlockChainStream_ReadAt(
3138 This
->rootBlockChain
,
3147 /******************************************************************************
3148 * StorageImpl_WriteRawDirEntry
3150 * This method will write the raw data from a directory entry in the file.
3152 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3154 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3156 ULARGE_INTEGER offset
;
3160 offset
.u
.HighPart
= 0;
3161 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3163 hr
= BlockChainStream_WriteAt(
3164 This
->rootBlockChain
,
3173 /******************************************************************************
3176 * Update raw directory entry data from the fields in newData.
3178 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3180 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3182 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3185 buffer
+ OFFSET_PS_NAME
,
3187 DIRENTRY_NAME_BUFFER_LEN
);
3189 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3191 StorageUtl_WriteWord(
3193 OFFSET_PS_NAMELENGTH
,
3194 newData
->sizeOfNameString
);
3196 StorageUtl_WriteDWord(
3198 OFFSET_PS_LEFTCHILD
,
3199 newData
->leftChild
);
3201 StorageUtl_WriteDWord(
3203 OFFSET_PS_RIGHTCHILD
,
3204 newData
->rightChild
);
3206 StorageUtl_WriteDWord(
3209 newData
->dirRootEntry
);
3211 StorageUtl_WriteGUID(
3216 StorageUtl_WriteDWord(
3219 newData
->ctime
.dwLowDateTime
);
3221 StorageUtl_WriteDWord(
3223 OFFSET_PS_CTIMEHIGH
,
3224 newData
->ctime
.dwHighDateTime
);
3226 StorageUtl_WriteDWord(
3229 newData
->mtime
.dwLowDateTime
);
3231 StorageUtl_WriteDWord(
3233 OFFSET_PS_MTIMEHIGH
,
3234 newData
->ctime
.dwHighDateTime
);
3236 StorageUtl_WriteDWord(
3238 OFFSET_PS_STARTBLOCK
,
3239 newData
->startingBlock
);
3241 StorageUtl_WriteDWord(
3244 newData
->size
.u
.LowPart
);
3247 /******************************************************************************
3248 * Storage32Impl_ReadDirEntry
3250 * This method will read the specified directory entry.
3252 BOOL
StorageImpl_ReadDirEntry(
3257 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3260 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3262 if (SUCCEEDED(readRes
))
3264 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3267 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3268 DIRENTRY_NAME_BUFFER_LEN
);
3269 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3271 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3273 StorageUtl_ReadWord(
3275 OFFSET_PS_NAMELENGTH
,
3276 &buffer
->sizeOfNameString
);
3278 StorageUtl_ReadDWord(
3280 OFFSET_PS_LEFTCHILD
,
3281 &buffer
->leftChild
);
3283 StorageUtl_ReadDWord(
3285 OFFSET_PS_RIGHTCHILD
,
3286 &buffer
->rightChild
);
3288 StorageUtl_ReadDWord(
3291 &buffer
->dirRootEntry
);
3293 StorageUtl_ReadGUID(
3298 StorageUtl_ReadDWord(
3301 &buffer
->ctime
.dwLowDateTime
);
3303 StorageUtl_ReadDWord(
3305 OFFSET_PS_CTIMEHIGH
,
3306 &buffer
->ctime
.dwHighDateTime
);
3308 StorageUtl_ReadDWord(
3311 &buffer
->mtime
.dwLowDateTime
);
3313 StorageUtl_ReadDWord(
3315 OFFSET_PS_MTIMEHIGH
,
3316 &buffer
->mtime
.dwHighDateTime
);
3318 StorageUtl_ReadDWord(
3320 OFFSET_PS_STARTBLOCK
,
3321 &buffer
->startingBlock
);
3323 StorageUtl_ReadDWord(
3326 &buffer
->size
.u
.LowPart
);
3328 buffer
->size
.u
.HighPart
= 0;
3331 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3334 /*********************************************************************
3335 * Write the specified directory entry to the file
3337 BOOL
StorageImpl_WriteDirEntry(
3340 const DirEntry
* buffer
)
3342 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3345 UpdateRawDirEntry(currentEntry
, buffer
);
3347 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3348 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3351 static BOOL
StorageImpl_ReadBigBlock(
3356 ULARGE_INTEGER ulOffset
;
3359 ulOffset
.u
.HighPart
= 0;
3360 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3362 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3363 return (read
== This
->bigBlockSize
);
3366 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3372 ULARGE_INTEGER ulOffset
;
3376 ulOffset
.u
.HighPart
= 0;
3377 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3378 ulOffset
.u
.LowPart
+= offset
;
3380 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3381 *value
= lendian32toh(tmp
);
3382 return (read
== sizeof(DWORD
));
3385 static BOOL
StorageImpl_WriteBigBlock(
3390 ULARGE_INTEGER ulOffset
;
3393 ulOffset
.u
.HighPart
= 0;
3394 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3396 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3397 return (wrote
== This
->bigBlockSize
);
3400 static BOOL
StorageImpl_WriteDWordToBigBlock(
3406 ULARGE_INTEGER ulOffset
;
3409 ulOffset
.u
.HighPart
= 0;
3410 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3411 ulOffset
.u
.LowPart
+= offset
;
3413 value
= htole32(value
);
3414 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3415 return (wrote
== sizeof(DWORD
));
3418 /******************************************************************************
3419 * Storage32Impl_SmallBlocksToBigBlocks
3421 * This method will convert a small block chain to a big block chain.
3422 * The small block chain will be destroyed.
3424 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3426 SmallBlockChainStream
** ppsbChain
)
3428 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3429 ULARGE_INTEGER size
, offset
;
3430 ULONG cbRead
, cbWritten
;
3431 ULARGE_INTEGER cbTotalRead
;
3432 DirRef streamEntryRef
;
3433 HRESULT resWrite
= S_OK
;
3435 DirEntry streamEntry
;
3437 BlockChainStream
*bbTempChain
= NULL
;
3438 BlockChainStream
*bigBlockChain
= NULL
;
3441 * Create a temporary big block chain that doesn't have
3442 * an associated directory entry. This temporary chain will be
3443 * used to copy data from small blocks to big blocks.
3445 bbTempChain
= BlockChainStream_Construct(This
,
3448 if(!bbTempChain
) return NULL
;
3450 * Grow the big block chain.
3452 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3453 BlockChainStream_SetSize(bbTempChain
, size
);
3456 * Copy the contents of the small block chain to the big block chain
3457 * by small block size increments.
3459 offset
.u
.LowPart
= 0;
3460 offset
.u
.HighPart
= 0;
3461 cbTotalRead
.QuadPart
= 0;
3463 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3466 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3468 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3471 if (FAILED(resRead
))
3476 cbTotalRead
.QuadPart
+= cbRead
;
3478 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3484 if (FAILED(resWrite
))
3487 offset
.u
.LowPart
+= cbRead
;
3489 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3490 HeapFree(GetProcessHeap(),0,buffer
);
3492 size
.u
.HighPart
= 0;
3495 if (FAILED(resRead
) || FAILED(resWrite
))
3497 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3498 BlockChainStream_SetSize(bbTempChain
, size
);
3499 BlockChainStream_Destroy(bbTempChain
);
3504 * Destroy the small block chain.
3506 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3507 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3508 SmallBlockChainStream_Destroy(*ppsbChain
);
3512 * Change the directory entry. This chain is now a big block chain
3513 * and it doesn't reside in the small blocks chain anymore.
3515 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3517 streamEntry
.startingBlock
= bbHeadOfChain
;
3519 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3522 * Destroy the temporary entryless big block chain.
3523 * Create a new big block chain associated with this entry.
3525 BlockChainStream_Destroy(bbTempChain
);
3526 bigBlockChain
= BlockChainStream_Construct(This
,
3530 return bigBlockChain
;
3533 /******************************************************************************
3534 * Storage32Impl_BigBlocksToSmallBlocks
3536 * This method will convert a big block chain to a small block chain.
3537 * The big block chain will be destroyed on success.
3539 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3541 BlockChainStream
** ppbbChain
)
3543 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3544 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3545 DirRef streamEntryRef
;
3546 HRESULT resWrite
= S_OK
, resRead
;
3547 DirEntry streamEntry
;
3549 SmallBlockChainStream
* sbTempChain
;
3551 TRACE("%p %p\n", This
, ppbbChain
);
3553 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3559 size
= BlockChainStream_GetSize(*ppbbChain
);
3560 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3562 offset
.u
.HighPart
= 0;
3563 offset
.u
.LowPart
= 0;
3564 cbTotalRead
.QuadPart
= 0;
3565 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3568 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3569 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3577 cbTotalRead
.QuadPart
+= cbRead
;
3579 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3580 cbRead
, buffer
, &cbWritten
);
3582 if(FAILED(resWrite
))
3585 offset
.u
.LowPart
+= cbRead
;
3587 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3588 HeapFree(GetProcessHeap(), 0, buffer
);
3590 size
.u
.HighPart
= 0;
3593 if(FAILED(resRead
) || FAILED(resWrite
))
3595 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3596 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3597 SmallBlockChainStream_Destroy(sbTempChain
);
3601 /* destroy the original big block chain */
3602 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3603 BlockChainStream_SetSize(*ppbbChain
, size
);
3604 BlockChainStream_Destroy(*ppbbChain
);
3607 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3608 streamEntry
.startingBlock
= sbHeadOfChain
;
3609 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3611 SmallBlockChainStream_Destroy(sbTempChain
);
3612 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3615 static void StorageInternalImpl_Invalidate( StorageInternalImpl
*This
)
3617 if (This
->base
.ancestorStorage
)
3619 TRACE("Storage invalidated (stg=%p)\n", This
);
3621 This
->base
.ancestorStorage
= NULL
;
3623 StorageBaseImpl_DeleteAll(&This
->base
);
3625 list_remove(&This
->ParentListEntry
);
3629 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3631 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3633 StorageInternalImpl_Invalidate(This
);
3635 HeapFree(GetProcessHeap(), 0, This
);
3638 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
3639 const DirEntry
*newData
, DirRef
*index
)
3641 return StorageBaseImpl_CreateDirEntry(&base
->ancestorStorage
->base
,
3645 /******************************************************************************
3647 ** Storage32InternalImpl_Commit
3650 static HRESULT WINAPI
StorageInternalImpl_Commit(
3652 DWORD grfCommitFlags
) /* [in] */
3654 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
3658 /******************************************************************************
3660 ** Storage32InternalImpl_Revert
3663 static HRESULT WINAPI
StorageInternalImpl_Revert(
3666 FIXME("(%p): stub\n", iface
);
3670 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3672 IStorage_Release((IStorage
*)This
->parentStorage
);
3673 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3674 HeapFree(GetProcessHeap(), 0, This
);
3677 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3678 IEnumSTATSTG
* iface
,
3682 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3685 return E_INVALIDARG
;
3689 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3690 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3693 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3697 return E_NOINTERFACE
;
3700 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3701 IEnumSTATSTG
* iface
)
3703 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3704 return InterlockedIncrement(&This
->ref
);
3707 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3708 IEnumSTATSTG
* iface
)
3710 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3714 newRef
= InterlockedDecrement(&This
->ref
);
3718 IEnumSTATSTGImpl_Destroy(This
);
3724 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3725 IEnumSTATSTG
* iface
,
3728 ULONG
* pceltFetched
)
3730 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3732 DirEntry currentEntry
;
3733 STATSTG
* currentReturnStruct
= rgelt
;
3734 ULONG objectFetched
= 0;
3735 DirRef currentSearchNode
;
3737 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3738 return E_INVALIDARG
;
3741 * To avoid the special case, get another pointer to a ULONG value if
3742 * the caller didn't supply one.
3744 if (pceltFetched
==0)
3745 pceltFetched
= &objectFetched
;
3748 * Start the iteration, we will iterate until we hit the end of the
3749 * linked list or until we hit the number of items to iterate through
3754 * Start with the node at the top of the stack.
3756 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3758 while ( ( *pceltFetched
< celt
) &&
3759 ( currentSearchNode
!=DIRENTRY_NULL
) )
3762 * Remove the top node from the stack
3764 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3767 * Read the entry from the storage.
3769 StorageImpl_ReadDirEntry(This
->parentStorage
,
3774 * Copy the information to the return buffer.
3776 StorageUtl_CopyDirEntryToSTATSTG(&This
->parentStorage
->base
,
3777 currentReturnStruct
,
3782 * Step to the next item in the iteration
3785 currentReturnStruct
++;
3788 * Push the next search node in the search stack.
3790 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3793 * continue the iteration.
3795 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3798 if (*pceltFetched
== celt
)
3805 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3806 IEnumSTATSTG
* iface
,
3809 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3811 DirEntry currentEntry
;
3812 ULONG objectFetched
= 0;
3813 DirRef currentSearchNode
;
3816 * Start with the node at the top of the stack.
3818 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3820 while ( (objectFetched
< celt
) &&
3821 (currentSearchNode
!=DIRENTRY_NULL
) )
3824 * Remove the top node from the stack
3826 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3829 * Read the entry from the storage.
3831 StorageImpl_ReadDirEntry(This
->parentStorage
,
3836 * Step to the next item in the iteration
3841 * Push the next search node in the search stack.
3843 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3846 * continue the iteration.
3848 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3851 if (objectFetched
== celt
)
3857 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3858 IEnumSTATSTG
* iface
)
3860 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3862 DirEntry storageEntry
;
3863 BOOL readSuccessful
;
3866 * Re-initialize the search stack to an empty stack
3868 This
->stackSize
= 0;
3871 * Read the storage entry from the top-level storage.
3873 readSuccessful
= StorageImpl_ReadDirEntry(
3874 This
->parentStorage
,
3875 This
->storageDirEntry
,
3880 assert(storageEntry
.sizeOfNameString
!=0);
3883 * Push the search node in the search stack.
3885 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.dirRootEntry
);
3891 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3892 IEnumSTATSTG
* iface
,
3893 IEnumSTATSTG
** ppenum
)
3895 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3897 IEnumSTATSTGImpl
* newClone
;
3900 * Perform a sanity check on the parameters.
3903 return E_INVALIDARG
;
3905 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3906 This
->storageDirEntry
);
3910 * The new clone enumeration must point to the same current node as
3913 newClone
->stackSize
= This
->stackSize
;
3914 newClone
->stackMaxSize
= This
->stackMaxSize
;
3915 newClone
->stackToVisit
=
3916 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3919 newClone
->stackToVisit
,
3921 sizeof(DirRef
) * newClone
->stackSize
);
3923 *ppenum
= (IEnumSTATSTG
*)newClone
;
3926 * Don't forget to nail down a reference to the clone before
3929 IEnumSTATSTGImpl_AddRef(*ppenum
);
3934 static void IEnumSTATSTGImpl_PushSearchNode(
3935 IEnumSTATSTGImpl
* This
,
3938 DirEntry storageEntry
;
3939 BOOL readSuccessful
;
3942 * First, make sure we're not trying to push an unexisting node.
3944 if (nodeToPush
==DIRENTRY_NULL
)
3948 * First push the node to the stack
3950 if (This
->stackSize
== This
->stackMaxSize
)
3952 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3954 This
->stackToVisit
= HeapReAlloc(
3958 sizeof(DirRef
) * This
->stackMaxSize
);
3961 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3965 * Read the storage entry from the top-level storage.
3967 readSuccessful
= StorageImpl_ReadDirEntry(
3968 This
->parentStorage
,
3974 assert(storageEntry
.sizeOfNameString
!=0);
3977 * Push the previous search node in the search stack.
3979 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.leftChild
);
3983 static DirRef
IEnumSTATSTGImpl_PopSearchNode(
3984 IEnumSTATSTGImpl
* This
,
3989 if (This
->stackSize
== 0)
3990 return DIRENTRY_NULL
;
3992 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4001 * Virtual function table for the IEnumSTATSTGImpl class.
4003 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4005 IEnumSTATSTGImpl_QueryInterface
,
4006 IEnumSTATSTGImpl_AddRef
,
4007 IEnumSTATSTGImpl_Release
,
4008 IEnumSTATSTGImpl_Next
,
4009 IEnumSTATSTGImpl_Skip
,
4010 IEnumSTATSTGImpl_Reset
,
4011 IEnumSTATSTGImpl_Clone
4014 /******************************************************************************
4015 ** IEnumSTATSTGImpl implementation
4018 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4019 StorageImpl
* parentStorage
,
4020 DirRef storageDirEntry
)
4022 IEnumSTATSTGImpl
* newEnumeration
;
4024 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4026 if (newEnumeration
!=0)
4029 * Set-up the virtual function table and reference count.
4031 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4032 newEnumeration
->ref
= 0;
4035 * We want to nail-down the reference to the storage in case the
4036 * enumeration out-lives the storage in the client application.
4038 newEnumeration
->parentStorage
= parentStorage
;
4039 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4041 newEnumeration
->storageDirEntry
= storageDirEntry
;
4044 * Initialize the search stack
4046 newEnumeration
->stackSize
= 0;
4047 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4048 newEnumeration
->stackToVisit
=
4049 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef
)*ENUMSTATSGT_SIZE_INCREMENT
);
4052 * Make sure the current node of the iterator is the first one.
4054 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4057 return newEnumeration
;
4061 * Virtual function table for the Storage32InternalImpl class.
4063 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4065 StorageBaseImpl_QueryInterface
,
4066 StorageBaseImpl_AddRef
,
4067 StorageBaseImpl_Release
,
4068 StorageBaseImpl_CreateStream
,
4069 StorageBaseImpl_OpenStream
,
4070 StorageBaseImpl_CreateStorage
,
4071 StorageBaseImpl_OpenStorage
,
4072 StorageBaseImpl_CopyTo
,
4073 StorageBaseImpl_MoveElementTo
,
4074 StorageInternalImpl_Commit
,
4075 StorageInternalImpl_Revert
,
4076 StorageBaseImpl_EnumElements
,
4077 StorageBaseImpl_DestroyElement
,
4078 StorageBaseImpl_RenameElement
,
4079 StorageBaseImpl_SetElementTimes
,
4080 StorageBaseImpl_SetClass
,
4081 StorageBaseImpl_SetStateBits
,
4082 StorageBaseImpl_Stat
4085 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4087 StorageInternalImpl_Destroy
,
4088 StorageInternalImpl_CreateDirEntry
4091 /******************************************************************************
4092 ** Storage32InternalImpl implementation
4095 static StorageInternalImpl
* StorageInternalImpl_Construct(
4096 StorageImpl
* ancestorStorage
,
4098 DirRef storageDirEntry
)
4100 StorageInternalImpl
* newStorage
;
4102 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4106 list_init(&newStorage
->base
.strmHead
);
4108 list_init(&newStorage
->base
.storageHead
);
4111 * Initialize the virtual function table.
4113 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4114 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4115 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4118 * Keep the ancestor storage pointer but do not nail a reference to it.
4120 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4123 * Keep a reference to the directory entry of this storage
4125 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4127 newStorage
->base
.create
= 0;
4135 /******************************************************************************
4136 ** StorageUtl implementation
4139 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4143 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4144 *value
= lendian16toh(tmp
);
4147 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4149 value
= htole16(value
);
4150 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4153 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4157 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4158 *value
= lendian32toh(tmp
);
4161 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4163 value
= htole32(value
);
4164 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4167 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4168 ULARGE_INTEGER
* value
)
4170 #ifdef WORDS_BIGENDIAN
4173 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4174 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4175 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4177 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4181 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4182 const ULARGE_INTEGER
*value
)
4184 #ifdef WORDS_BIGENDIAN
4187 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4188 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4189 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4191 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4195 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4197 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4198 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4199 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4201 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4204 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4206 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4207 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4208 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4210 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4213 void StorageUtl_CopyDirEntryToSTATSTG(
4214 StorageBaseImpl
* storage
,
4215 STATSTG
* destination
,
4216 const DirEntry
* source
,
4221 if (source
->stgType
== STGTY_ROOT
)
4223 /* replace the name of root entry (often "Root Entry") by the file name */
4224 entryName
= storage
->filename
;
4228 entryName
= source
->name
;
4232 * The copy of the string occurs only when the flag is not set
4234 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4235 (entryName
== NULL
) ||
4236 (entryName
[0] == 0) )
4238 destination
->pwcsName
= 0;
4242 destination
->pwcsName
=
4243 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4245 strcpyW(destination
->pwcsName
, entryName
);
4248 switch (source
->stgType
)
4252 destination
->type
= STGTY_STORAGE
;
4255 destination
->type
= STGTY_STREAM
;
4258 destination
->type
= STGTY_STREAM
;
4262 destination
->cbSize
= source
->size
;
4264 currentReturnStruct->mtime = {0}; TODO
4265 currentReturnStruct->ctime = {0};
4266 currentReturnStruct->atime = {0};
4268 destination
->grfMode
= 0;
4269 destination
->grfLocksSupported
= 0;
4270 destination
->clsid
= source
->clsid
;
4271 destination
->grfStateBits
= 0;
4272 destination
->reserved
= 0;
4275 /******************************************************************************
4276 ** BlockChainStream implementation
4279 BlockChainStream
* BlockChainStream_Construct(
4280 StorageImpl
* parentStorage
,
4281 ULONG
* headOfStreamPlaceHolder
,
4284 BlockChainStream
* newStream
;
4287 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4289 newStream
->parentStorage
= parentStorage
;
4290 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4291 newStream
->ownerDirEntry
= dirEntry
;
4292 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4293 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4294 newStream
->numBlocks
= 0;
4296 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4298 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4300 newStream
->numBlocks
++;
4301 newStream
->tailIndex
= blockIndex
;
4303 if(FAILED(StorageImpl_GetNextBlockInChain(
4308 HeapFree(GetProcessHeap(), 0, newStream
);
4316 void BlockChainStream_Destroy(BlockChainStream
* This
)
4318 HeapFree(GetProcessHeap(), 0, This
);
4321 /******************************************************************************
4322 * BlockChainStream_GetHeadOfChain
4324 * Returns the head of this stream chain.
4325 * Some special chains don't have directory entries, their heads are kept in
4326 * This->headOfStreamPlaceHolder.
4329 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4331 DirEntry chainEntry
;
4332 BOOL readSuccessful
;
4334 if (This
->headOfStreamPlaceHolder
!= 0)
4335 return *(This
->headOfStreamPlaceHolder
);
4337 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
4339 readSuccessful
= StorageImpl_ReadDirEntry(
4340 This
->parentStorage
,
4341 This
->ownerDirEntry
,
4346 return chainEntry
.startingBlock
;
4350 return BLOCK_END_OF_CHAIN
;
4353 /******************************************************************************
4354 * BlockChainStream_GetCount
4356 * Returns the number of blocks that comprises this chain.
4357 * This is not the size of the stream as the last block may not be full!
4360 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4365 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4367 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4371 if(FAILED(StorageImpl_GetNextBlockInChain(
4372 This
->parentStorage
,
4381 /******************************************************************************
4382 * BlockChainStream_ReadAt
4384 * Reads a specified number of bytes from this chain at the specified offset.
4385 * bytesRead may be NULL.
4386 * Failure will be returned if the specified number of bytes has not been read.
4388 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4389 ULARGE_INTEGER offset
,
4394 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4395 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4396 ULONG bytesToReadInBuffer
;
4400 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4403 * Find the first block in the stream that contains part of the buffer.
4405 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4406 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4407 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4409 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4410 This
->lastBlockNoInSequence
= blockNoInSequence
;
4414 ULONG temp
= blockNoInSequence
;
4416 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4417 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4418 This
->lastBlockNoInSequence
= temp
;
4421 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4423 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4424 return STG_E_DOCFILECORRUPT
;
4425 blockNoInSequence
--;
4428 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4429 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4431 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4434 * Start reading the buffer.
4437 bufferWalker
= buffer
;
4439 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4441 ULARGE_INTEGER ulOffset
;
4444 * Calculate how many bytes we can copy from this big block.
4446 bytesToReadInBuffer
=
4447 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4449 TRACE("block %i\n",blockIndex
);
4450 ulOffset
.u
.HighPart
= 0;
4451 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4454 StorageImpl_ReadAt(This
->parentStorage
,
4457 bytesToReadInBuffer
,
4460 * Step to the next big block.
4462 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4463 return STG_E_DOCFILECORRUPT
;
4465 bufferWalker
+= bytesReadAt
;
4466 size
-= bytesReadAt
;
4467 *bytesRead
+= bytesReadAt
;
4468 offsetInBlock
= 0; /* There is no offset on the next block */
4470 if (bytesToReadInBuffer
!= bytesReadAt
)
4474 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4477 /******************************************************************************
4478 * BlockChainStream_WriteAt
4480 * Writes the specified number of bytes to this chain at the specified offset.
4481 * Will fail if not all specified number of bytes have been written.
4483 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4484 ULARGE_INTEGER offset
,
4487 ULONG
* bytesWritten
)
4489 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4490 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4493 const BYTE
* bufferWalker
;
4496 * Find the first block in the stream that contains part of the buffer.
4498 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4499 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4500 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4502 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4503 This
->lastBlockNoInSequence
= blockNoInSequence
;
4507 ULONG temp
= blockNoInSequence
;
4509 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4510 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4511 This
->lastBlockNoInSequence
= temp
;
4514 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4516 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4518 return STG_E_DOCFILECORRUPT
;
4519 blockNoInSequence
--;
4522 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4524 /* BlockChainStream_SetSize should have already been called to ensure we have
4525 * enough blocks in the chain to write into */
4526 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4528 ERR("not enough blocks in chain to write data\n");
4529 return STG_E_DOCFILECORRUPT
;
4533 bufferWalker
= buffer
;
4535 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4537 ULARGE_INTEGER ulOffset
;
4538 DWORD bytesWrittenAt
;
4540 * Calculate how many bytes we can copy from this big block.
4543 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4545 TRACE("block %i\n",blockIndex
);
4546 ulOffset
.u
.HighPart
= 0;
4547 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4550 StorageImpl_WriteAt(This
->parentStorage
,
4557 * Step to the next big block.
4559 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4561 return STG_E_DOCFILECORRUPT
;
4563 bufferWalker
+= bytesWrittenAt
;
4564 size
-= bytesWrittenAt
;
4565 *bytesWritten
+= bytesWrittenAt
;
4566 offsetInBlock
= 0; /* There is no offset on the next block */
4568 if (bytesWrittenAt
!= bytesToWrite
)
4572 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4575 /******************************************************************************
4576 * BlockChainStream_Shrink
4578 * Shrinks this chain in the big block depot.
4580 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4581 ULARGE_INTEGER newSize
)
4583 ULONG blockIndex
, extraBlock
;
4588 * Reset the last accessed block cache.
4590 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4591 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4594 * Figure out how many blocks are needed to contain the new size
4596 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4598 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4601 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4604 * Go to the new end of chain
4606 while (count
< numBlocks
)
4608 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4614 /* Get the next block before marking the new end */
4615 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4619 /* Mark the new end of chain */
4620 StorageImpl_SetNextBlockInChain(
4621 This
->parentStorage
,
4623 BLOCK_END_OF_CHAIN
);
4625 This
->tailIndex
= blockIndex
;
4626 This
->numBlocks
= numBlocks
;
4629 * Mark the extra blocks as free
4631 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4633 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4636 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4637 extraBlock
= blockIndex
;
4643 /******************************************************************************
4644 * BlockChainStream_Enlarge
4646 * Grows this chain in the big block depot.
4648 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4649 ULARGE_INTEGER newSize
)
4651 ULONG blockIndex
, currentBlock
;
4653 ULONG oldNumBlocks
= 0;
4655 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4658 * Empty chain. Create the head.
4660 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4662 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4663 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4665 BLOCK_END_OF_CHAIN
);
4667 if (This
->headOfStreamPlaceHolder
!= 0)
4669 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4673 DirEntry chainEntry
;
4674 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
4676 StorageImpl_ReadDirEntry(
4677 This
->parentStorage
,
4678 This
->ownerDirEntry
,
4681 chainEntry
.startingBlock
= blockIndex
;
4683 StorageImpl_WriteDirEntry(
4684 This
->parentStorage
,
4685 This
->ownerDirEntry
,
4689 This
->tailIndex
= blockIndex
;
4690 This
->numBlocks
= 1;
4694 * Figure out how many blocks are needed to contain this stream
4696 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4698 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4702 * Go to the current end of chain
4704 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4706 currentBlock
= blockIndex
;
4708 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4711 currentBlock
= blockIndex
;
4713 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4718 This
->tailIndex
= currentBlock
;
4721 currentBlock
= This
->tailIndex
;
4722 oldNumBlocks
= This
->numBlocks
;
4725 * Add new blocks to the chain
4727 if (oldNumBlocks
< newNumBlocks
)
4729 while (oldNumBlocks
< newNumBlocks
)
4731 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4733 StorageImpl_SetNextBlockInChain(
4734 This
->parentStorage
,
4738 StorageImpl_SetNextBlockInChain(
4739 This
->parentStorage
,
4741 BLOCK_END_OF_CHAIN
);
4743 currentBlock
= blockIndex
;
4747 This
->tailIndex
= blockIndex
;
4748 This
->numBlocks
= newNumBlocks
;
4754 /******************************************************************************
4755 * BlockChainStream_SetSize
4757 * Sets the size of this stream. The big block depot will be updated.
4758 * The file will grow if we grow the chain.
4760 * TODO: Free the actual blocks in the file when we shrink the chain.
4761 * Currently, the blocks are still in the file. So the file size
4762 * doesn't shrink even if we shrink streams.
4764 BOOL
BlockChainStream_SetSize(
4765 BlockChainStream
* This
,
4766 ULARGE_INTEGER newSize
)
4768 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4770 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4773 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4775 BlockChainStream_Shrink(This
, newSize
);
4779 BlockChainStream_Enlarge(This
, newSize
);
4785 /******************************************************************************
4786 * BlockChainStream_GetSize
4788 * Returns the size of this chain.
4789 * Will return the block count if this chain doesn't have a directory entry.
4791 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4793 DirEntry chainEntry
;
4795 if(This
->headOfStreamPlaceHolder
== NULL
)
4798 * This chain has a directory entry so use the size value from there.
4800 StorageImpl_ReadDirEntry(
4801 This
->parentStorage
,
4802 This
->ownerDirEntry
,
4805 return chainEntry
.size
;
4810 * this chain is a chain that does not have a directory entry, figure out the
4811 * size by making the product number of used blocks times the
4814 ULARGE_INTEGER result
;
4815 result
.u
.HighPart
= 0;
4818 BlockChainStream_GetCount(This
) *
4819 This
->parentStorage
->bigBlockSize
;
4825 /******************************************************************************
4826 ** SmallBlockChainStream implementation
4829 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4830 StorageImpl
* parentStorage
,
4831 ULONG
* headOfStreamPlaceHolder
,
4834 SmallBlockChainStream
* newStream
;
4836 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4838 newStream
->parentStorage
= parentStorage
;
4839 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4840 newStream
->ownerDirEntry
= dirEntry
;
4845 void SmallBlockChainStream_Destroy(
4846 SmallBlockChainStream
* This
)
4848 HeapFree(GetProcessHeap(), 0, This
);
4851 /******************************************************************************
4852 * SmallBlockChainStream_GetHeadOfChain
4854 * Returns the head of this chain of small blocks.
4856 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4857 SmallBlockChainStream
* This
)
4859 DirEntry chainEntry
;
4860 BOOL readSuccessful
;
4862 if (This
->headOfStreamPlaceHolder
!= NULL
)
4863 return *(This
->headOfStreamPlaceHolder
);
4865 if (This
->ownerDirEntry
)
4867 readSuccessful
= StorageImpl_ReadDirEntry(
4868 This
->parentStorage
,
4869 This
->ownerDirEntry
,
4874 return chainEntry
.startingBlock
;
4879 return BLOCK_END_OF_CHAIN
;
4882 /******************************************************************************
4883 * SmallBlockChainStream_GetNextBlockInChain
4885 * Returns the index of the next small block in this chain.
4888 * - BLOCK_END_OF_CHAIN: end of this chain
4889 * - BLOCK_UNUSED: small block 'blockIndex' is free
4891 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4892 SmallBlockChainStream
* This
,
4894 ULONG
* nextBlockInChain
)
4896 ULARGE_INTEGER offsetOfBlockInDepot
;
4901 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4903 offsetOfBlockInDepot
.u
.HighPart
= 0;
4904 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4907 * Read those bytes in the buffer from the small block file.
4909 res
= BlockChainStream_ReadAt(
4910 This
->parentStorage
->smallBlockDepotChain
,
4911 offsetOfBlockInDepot
,
4918 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4925 /******************************************************************************
4926 * SmallBlockChainStream_SetNextBlockInChain
4928 * Writes the index of the next block of the specified block in the small
4930 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4931 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4933 static void SmallBlockChainStream_SetNextBlockInChain(
4934 SmallBlockChainStream
* This
,
4938 ULARGE_INTEGER offsetOfBlockInDepot
;
4942 offsetOfBlockInDepot
.u
.HighPart
= 0;
4943 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4945 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4948 * Read those bytes in the buffer from the small block file.
4950 BlockChainStream_WriteAt(
4951 This
->parentStorage
->smallBlockDepotChain
,
4952 offsetOfBlockInDepot
,
4958 /******************************************************************************
4959 * SmallBlockChainStream_FreeBlock
4961 * Flag small block 'blockIndex' as free in the small block depot.
4963 static void SmallBlockChainStream_FreeBlock(
4964 SmallBlockChainStream
* This
,
4967 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4970 /******************************************************************************
4971 * SmallBlockChainStream_GetNextFreeBlock
4973 * Returns the index of a free small block. The small block depot will be
4974 * enlarged if necessary. The small block chain will also be enlarged if
4977 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
4978 SmallBlockChainStream
* This
)
4980 ULARGE_INTEGER offsetOfBlockInDepot
;
4983 ULONG blockIndex
= 0;
4984 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4986 ULONG smallBlocksPerBigBlock
;
4988 offsetOfBlockInDepot
.u
.HighPart
= 0;
4991 * Scan the small block depot for a free block
4993 while (nextBlockIndex
!= BLOCK_UNUSED
)
4995 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4997 res
= BlockChainStream_ReadAt(
4998 This
->parentStorage
->smallBlockDepotChain
,
4999 offsetOfBlockInDepot
,
5005 * If we run out of space for the small block depot, enlarge it
5009 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5011 if (nextBlockIndex
!= BLOCK_UNUSED
)
5017 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5019 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5020 ULONG nextBlock
, newsbdIndex
;
5021 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5023 nextBlock
= sbdIndex
;
5024 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5026 sbdIndex
= nextBlock
;
5027 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5030 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5031 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5032 StorageImpl_SetNextBlockInChain(
5033 This
->parentStorage
,
5037 StorageImpl_SetNextBlockInChain(
5038 This
->parentStorage
,
5040 BLOCK_END_OF_CHAIN
);
5043 * Initialize all the small blocks to free
5045 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5046 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5051 * We have just created the small block depot.
5057 * Save it in the header
5059 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5060 StorageImpl_SaveFileHeader(This
->parentStorage
);
5063 * And allocate the first big block that will contain small blocks
5066 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5068 StorageImpl_SetNextBlockInChain(
5069 This
->parentStorage
,
5071 BLOCK_END_OF_CHAIN
);
5073 StorageImpl_ReadDirEntry(
5074 This
->parentStorage
,
5075 This
->parentStorage
->base
.storageDirEntry
,
5078 rootEntry
.startingBlock
= sbStartIndex
;
5079 rootEntry
.size
.u
.HighPart
= 0;
5080 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5082 StorageImpl_WriteDirEntry(
5083 This
->parentStorage
,
5084 This
->parentStorage
->base
.storageDirEntry
,
5088 StorageImpl_SaveFileHeader(This
->parentStorage
);
5092 smallBlocksPerBigBlock
=
5093 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5096 * Verify if we have to allocate big blocks to contain small blocks
5098 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5101 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5103 StorageImpl_ReadDirEntry(
5104 This
->parentStorage
,
5105 This
->parentStorage
->base
.storageDirEntry
,
5108 if (rootEntry
.size
.u
.LowPart
<
5109 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5111 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5113 BlockChainStream_SetSize(
5114 This
->parentStorage
->smallBlockRootChain
,
5117 StorageImpl_WriteDirEntry(
5118 This
->parentStorage
,
5119 This
->parentStorage
->base
.storageDirEntry
,
5127 /******************************************************************************
5128 * SmallBlockChainStream_ReadAt
5130 * Reads a specified number of bytes from this chain at the specified offset.
5131 * bytesRead may be NULL.
5132 * Failure will be returned if the specified number of bytes has not been read.
5134 HRESULT
SmallBlockChainStream_ReadAt(
5135 SmallBlockChainStream
* This
,
5136 ULARGE_INTEGER offset
,
5142 ULARGE_INTEGER offsetInBigBlockFile
;
5143 ULONG blockNoInSequence
=
5144 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5146 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5147 ULONG bytesToReadInBuffer
;
5149 ULONG bytesReadFromBigBlockFile
;
5153 * This should never happen on a small block file.
5155 assert(offset
.u
.HighPart
==0);
5158 * Find the first block in the stream that contains part of the buffer.
5160 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5162 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5164 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5167 blockNoInSequence
--;
5171 * Start reading the buffer.
5174 bufferWalker
= buffer
;
5176 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5179 * Calculate how many bytes we can copy from this small block.
5181 bytesToReadInBuffer
=
5182 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5185 * Calculate the offset of the small block in the small block file.
5187 offsetInBigBlockFile
.u
.HighPart
= 0;
5188 offsetInBigBlockFile
.u
.LowPart
=
5189 blockIndex
* This
->parentStorage
->smallBlockSize
;
5191 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5194 * Read those bytes in the buffer from the small block file.
5195 * The small block has already been identified so it shouldn't fail
5196 * unless the file is corrupt.
5198 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5199 offsetInBigBlockFile
,
5200 bytesToReadInBuffer
,
5202 &bytesReadFromBigBlockFile
);
5208 * Step to the next big block.
5210 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5212 return STG_E_DOCFILECORRUPT
;
5214 bufferWalker
+= bytesReadFromBigBlockFile
;
5215 size
-= bytesReadFromBigBlockFile
;
5216 *bytesRead
+= bytesReadFromBigBlockFile
;
5217 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5220 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5223 /******************************************************************************
5224 * SmallBlockChainStream_WriteAt
5226 * Writes the specified number of bytes to this chain at the specified offset.
5227 * Will fail if not all specified number of bytes have been written.
5229 HRESULT
SmallBlockChainStream_WriteAt(
5230 SmallBlockChainStream
* This
,
5231 ULARGE_INTEGER offset
,
5234 ULONG
* bytesWritten
)
5236 ULARGE_INTEGER offsetInBigBlockFile
;
5237 ULONG blockNoInSequence
=
5238 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5240 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5241 ULONG bytesToWriteInBuffer
;
5243 ULONG bytesWrittenToBigBlockFile
;
5244 const BYTE
* bufferWalker
;
5248 * This should never happen on a small block file.
5250 assert(offset
.u
.HighPart
==0);
5253 * Find the first block in the stream that contains part of the buffer.
5255 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5257 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5259 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5260 return STG_E_DOCFILECORRUPT
;
5261 blockNoInSequence
--;
5265 * Start writing the buffer.
5268 bufferWalker
= buffer
;
5269 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5272 * Calculate how many bytes we can copy to this small block.
5274 bytesToWriteInBuffer
=
5275 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5278 * Calculate the offset of the small block in the small block file.
5280 offsetInBigBlockFile
.u
.HighPart
= 0;
5281 offsetInBigBlockFile
.u
.LowPart
=
5282 blockIndex
* This
->parentStorage
->smallBlockSize
;
5284 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5287 * Write those bytes in the buffer to the small block file.
5289 res
= BlockChainStream_WriteAt(
5290 This
->parentStorage
->smallBlockRootChain
,
5291 offsetInBigBlockFile
,
5292 bytesToWriteInBuffer
,
5294 &bytesWrittenToBigBlockFile
);
5299 * Step to the next big block.
5301 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5304 bufferWalker
+= bytesWrittenToBigBlockFile
;
5305 size
-= bytesWrittenToBigBlockFile
;
5306 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5307 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5310 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5313 /******************************************************************************
5314 * SmallBlockChainStream_Shrink
5316 * Shrinks this chain in the small block depot.
5318 static BOOL
SmallBlockChainStream_Shrink(
5319 SmallBlockChainStream
* This
,
5320 ULARGE_INTEGER newSize
)
5322 ULONG blockIndex
, extraBlock
;
5326 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5328 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5331 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5334 * Go to the new end of chain
5336 while (count
< numBlocks
)
5338 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5345 * If the count is 0, we have a special case, the head of the chain was
5350 DirEntry chainEntry
;
5352 StorageImpl_ReadDirEntry(This
->parentStorage
,
5353 This
->ownerDirEntry
,
5356 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
5358 StorageImpl_WriteDirEntry(This
->parentStorage
,
5359 This
->ownerDirEntry
,
5363 * We start freeing the chain at the head block.
5365 extraBlock
= blockIndex
;
5369 /* Get the next block before marking the new end */
5370 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5374 /* Mark the new end of chain */
5375 SmallBlockChainStream_SetNextBlockInChain(
5378 BLOCK_END_OF_CHAIN
);
5382 * Mark the extra blocks as free
5384 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5386 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5389 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5390 extraBlock
= blockIndex
;
5396 /******************************************************************************
5397 * SmallBlockChainStream_Enlarge
5399 * Grows this chain in the small block depot.
5401 static BOOL
SmallBlockChainStream_Enlarge(
5402 SmallBlockChainStream
* This
,
5403 ULARGE_INTEGER newSize
)
5405 ULONG blockIndex
, currentBlock
;
5407 ULONG oldNumBlocks
= 0;
5409 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5412 * Empty chain. Create the head.
5414 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5416 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5417 SmallBlockChainStream_SetNextBlockInChain(
5420 BLOCK_END_OF_CHAIN
);
5422 if (This
->headOfStreamPlaceHolder
!= NULL
)
5424 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5428 DirEntry chainEntry
;
5430 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5433 chainEntry
.startingBlock
= blockIndex
;
5435 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5440 currentBlock
= blockIndex
;
5443 * Figure out how many blocks are needed to contain this stream
5445 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5447 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5451 * Go to the current end of chain
5453 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5456 currentBlock
= blockIndex
;
5457 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5462 * Add new blocks to the chain
5464 while (oldNumBlocks
< newNumBlocks
)
5466 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5467 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5469 SmallBlockChainStream_SetNextBlockInChain(
5472 BLOCK_END_OF_CHAIN
);
5474 currentBlock
= blockIndex
;
5481 /******************************************************************************
5482 * SmallBlockChainStream_SetSize
5484 * Sets the size of this stream.
5485 * The file will grow if we grow the chain.
5487 * TODO: Free the actual blocks in the file when we shrink the chain.
5488 * Currently, the blocks are still in the file. So the file size
5489 * doesn't shrink even if we shrink streams.
5491 BOOL
SmallBlockChainStream_SetSize(
5492 SmallBlockChainStream
* This
,
5493 ULARGE_INTEGER newSize
)
5495 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5497 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5500 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5502 SmallBlockChainStream_Shrink(This
, newSize
);
5506 SmallBlockChainStream_Enlarge(This
, newSize
);
5512 /******************************************************************************
5513 * SmallBlockChainStream_GetCount
5515 * Returns the number of small blocks that comprises this chain.
5516 * This is not the size of the stream as the last block may not be full!
5519 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5524 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5526 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
5530 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
5531 blockIndex
, &blockIndex
)))
5538 /******************************************************************************
5539 * SmallBlockChainStream_GetSize
5541 * Returns the size of this chain.
5543 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5545 DirEntry chainEntry
;
5547 if(This
->headOfStreamPlaceHolder
!= NULL
)
5549 ULARGE_INTEGER result
;
5550 result
.u
.HighPart
= 0;
5552 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
5553 This
->parentStorage
->smallBlockSize
;
5558 StorageImpl_ReadDirEntry(
5559 This
->parentStorage
,
5560 This
->ownerDirEntry
,
5563 return chainEntry
.size
;
5566 /******************************************************************************
5567 * StgCreateDocfile [OLE32.@]
5568 * Creates a new compound file storage object
5571 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5572 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5573 * reserved [ ?] unused?, usually 0
5574 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5577 * S_OK if the file was successfully created
5578 * some STG_E_ value if error
5580 * if pwcsName is NULL, create file with new unique name
5581 * the function can returns
5582 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5585 HRESULT WINAPI
StgCreateDocfile(
5589 IStorage
**ppstgOpen
)
5591 StorageImpl
* newStorage
= 0;
5592 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5593 HRESULT hr
= STG_E_INVALIDFLAG
;
5597 DWORD fileAttributes
;
5598 WCHAR tempFileName
[MAX_PATH
];
5600 TRACE("(%s, %x, %d, %p)\n",
5601 debugstr_w(pwcsName
), grfMode
,
5602 reserved
, ppstgOpen
);
5605 return STG_E_INVALIDPOINTER
;
5607 return STG_E_INVALIDPARAMETER
;
5609 /* if no share mode given then DENY_NONE is the default */
5610 if (STGM_SHARE_MODE(grfMode
) == 0)
5611 grfMode
|= STGM_SHARE_DENY_NONE
;
5613 if ( FAILED( validateSTGM(grfMode
) ))
5616 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5617 switch(STGM_ACCESS_MODE(grfMode
))
5620 case STGM_READWRITE
:
5626 /* in direct mode, can only use SHARE_EXCLUSIVE */
5627 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5630 /* but in transacted mode, any share mode is valid */
5633 * Generate a unique name.
5637 WCHAR tempPath
[MAX_PATH
];
5638 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5640 memset(tempPath
, 0, sizeof(tempPath
));
5641 memset(tempFileName
, 0, sizeof(tempFileName
));
5643 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5646 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5647 pwcsName
= tempFileName
;
5650 hr
= STG_E_INSUFFICIENTMEMORY
;
5654 creationMode
= TRUNCATE_EXISTING
;
5658 creationMode
= GetCreationModeFromSTGM(grfMode
);
5662 * Interpret the STGM value grfMode
5664 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5665 accessMode
= GetAccessModeFromSTGM(grfMode
);
5667 if (grfMode
& STGM_DELETEONRELEASE
)
5668 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5670 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5672 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
5673 FIXME("Storage share mode not implemented.\n");
5675 if (grfMode
& STGM_TRANSACTED
)
5676 FIXME("Transacted mode not implemented.\n");
5680 hFile
= CreateFileW(pwcsName
,
5688 if (hFile
== INVALID_HANDLE_VALUE
)
5690 if(GetLastError() == ERROR_FILE_EXISTS
)
5691 hr
= STG_E_FILEALREADYEXISTS
;
5698 * Allocate and initialize the new IStorage32object.
5700 hr
= StorageImpl_Construct(
5715 * Get an "out" pointer for the caller.
5717 *ppstgOpen
= (IStorage
*)newStorage
;
5720 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5725 /******************************************************************************
5726 * StgCreateStorageEx [OLE32.@]
5728 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5730 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5731 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5733 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5735 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5736 return STG_E_INVALIDPARAMETER
;
5739 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5741 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5742 return STG_E_INVALIDPARAMETER
;
5745 if (stgfmt
== STGFMT_FILE
)
5747 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5748 return STG_E_INVALIDPARAMETER
;
5751 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5753 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5754 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5757 ERR("Invalid stgfmt argument\n");
5758 return STG_E_INVALIDPARAMETER
;
5761 /******************************************************************************
5762 * StgCreatePropSetStg [OLE32.@]
5764 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5765 IPropertySetStorage
**ppPropSetStg
)
5769 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5771 hr
= STG_E_INVALIDPARAMETER
;
5773 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5774 (void**)ppPropSetStg
);
5778 /******************************************************************************
5779 * StgOpenStorageEx [OLE32.@]
5781 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5783 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5784 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5786 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5788 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5789 return STG_E_INVALIDPARAMETER
;
5795 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5796 return STG_E_INVALIDPARAMETER
;
5798 case STGFMT_STORAGE
:
5801 case STGFMT_DOCFILE
:
5802 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5804 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5805 return STG_E_INVALIDPARAMETER
;
5807 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5811 WARN("STGFMT_ANY assuming storage\n");
5815 return STG_E_INVALIDPARAMETER
;
5818 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
5822 /******************************************************************************
5823 * StgOpenStorage [OLE32.@]
5825 HRESULT WINAPI
StgOpenStorage(
5826 const OLECHAR
*pwcsName
,
5827 IStorage
*pstgPriority
,
5831 IStorage
**ppstgOpen
)
5833 StorageImpl
* newStorage
= 0;
5838 WCHAR fullname
[MAX_PATH
];
5840 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5841 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5842 snbExclude
, reserved
, ppstgOpen
);
5846 hr
= STG_E_INVALIDNAME
;
5852 hr
= STG_E_INVALIDPOINTER
;
5858 hr
= STG_E_INVALIDPARAMETER
;
5862 if (grfMode
& STGM_PRIORITY
)
5864 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5865 return STG_E_INVALIDFLAG
;
5866 if (grfMode
& STGM_DELETEONRELEASE
)
5867 return STG_E_INVALIDFUNCTION
;
5868 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5869 return STG_E_INVALIDFLAG
;
5870 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5871 grfMode
|= STGM_SHARE_DENY_NONE
;
5873 /* STGM_PRIORITY stops other IStorage objects on the same file from
5874 * committing until the STGM_PRIORITY IStorage is closed. it also
5875 * stops non-transacted mode StgOpenStorage calls with write access from
5876 * succeeding. obviously, both of these cannot be achieved through just
5877 * file share flags */
5878 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5882 * Validate the sharing mode
5884 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5885 switch(STGM_SHARE_MODE(grfMode
))
5887 case STGM_SHARE_EXCLUSIVE
:
5888 case STGM_SHARE_DENY_WRITE
:
5891 hr
= STG_E_INVALIDFLAG
;
5895 if ( FAILED( validateSTGM(grfMode
) ) ||
5896 (grfMode
&STGM_CREATE
))
5898 hr
= STG_E_INVALIDFLAG
;
5902 /* shared reading requires transacted mode */
5903 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5904 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5905 !(grfMode
&STGM_TRANSACTED
) )
5907 hr
= STG_E_INVALIDFLAG
;
5912 * Interpret the STGM value grfMode
5914 shareMode
= GetShareModeFromSTGM(grfMode
);
5915 accessMode
= GetAccessModeFromSTGM(grfMode
);
5919 hFile
= CreateFileW( pwcsName
,
5924 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5927 if (hFile
==INVALID_HANDLE_VALUE
)
5929 DWORD last_error
= GetLastError();
5935 case ERROR_FILE_NOT_FOUND
:
5936 hr
= STG_E_FILENOTFOUND
;
5939 case ERROR_PATH_NOT_FOUND
:
5940 hr
= STG_E_PATHNOTFOUND
;
5943 case ERROR_ACCESS_DENIED
:
5944 case ERROR_WRITE_PROTECT
:
5945 hr
= STG_E_ACCESSDENIED
;
5948 case ERROR_SHARING_VIOLATION
:
5949 hr
= STG_E_SHAREVIOLATION
;
5960 * Refuse to open the file if it's too small to be a structured storage file
5961 * FIXME: verify the file when reading instead of here
5963 if (GetFileSize(hFile
, NULL
) < 0x100)
5966 hr
= STG_E_FILEALREADYEXISTS
;
5971 * Allocate and initialize the new IStorage32object.
5973 hr
= StorageImpl_Construct(
5985 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5987 if(hr
== STG_E_INVALIDHEADER
)
5988 hr
= STG_E_FILEALREADYEXISTS
;
5992 /* prepare the file name string given in lieu of the root property name */
5993 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5994 memcpy(newStorage
->base
.filename
, fullname
, DIRENTRY_NAME_BUFFER_LEN
);
5995 newStorage
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = '\0';
5998 * Get an "out" pointer for the caller.
6000 *ppstgOpen
= (IStorage
*)newStorage
;
6003 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6007 /******************************************************************************
6008 * StgCreateDocfileOnILockBytes [OLE32.@]
6010 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6014 IStorage
** ppstgOpen
)
6016 StorageImpl
* newStorage
= 0;
6019 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6020 return STG_E_INVALIDPOINTER
;
6023 * Allocate and initialize the new IStorage object.
6025 hr
= StorageImpl_Construct(
6040 * Get an "out" pointer for the caller.
6042 *ppstgOpen
= (IStorage
*)newStorage
;
6047 /******************************************************************************
6048 * StgOpenStorageOnILockBytes [OLE32.@]
6050 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6052 IStorage
*pstgPriority
,
6056 IStorage
**ppstgOpen
)
6058 StorageImpl
* newStorage
= 0;
6061 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6062 return STG_E_INVALIDPOINTER
;
6064 if ( FAILED( validateSTGM(grfMode
) ))
6065 return STG_E_INVALIDFLAG
;
6070 * Allocate and initialize the new IStorage object.
6072 hr
= StorageImpl_Construct(
6087 * Get an "out" pointer for the caller.
6089 *ppstgOpen
= (IStorage
*)newStorage
;
6094 /******************************************************************************
6095 * StgSetTimes [ole32.@]
6096 * StgSetTimes [OLE32.@]
6100 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6101 FILETIME
const *patime
, FILETIME
const *pmtime
)
6103 IStorage
*stg
= NULL
;
6106 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6108 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6112 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6113 IStorage_Release(stg
);
6119 /******************************************************************************
6120 * StgIsStorageILockBytes [OLE32.@]
6122 * Determines if the ILockBytes contains a storage object.
6124 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6127 ULARGE_INTEGER offset
;
6129 offset
.u
.HighPart
= 0;
6130 offset
.u
.LowPart
= 0;
6132 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6134 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6140 /******************************************************************************
6141 * WriteClassStg [OLE32.@]
6143 * This method will store the specified CLSID in the specified storage object
6145 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6150 return E_INVALIDARG
;
6153 return STG_E_INVALIDPOINTER
;
6155 hRes
= IStorage_SetClass(pStg
, rclsid
);
6160 /***********************************************************************
6161 * ReadClassStg (OLE32.@)
6163 * This method reads the CLSID previously written to a storage object with
6164 * the WriteClassStg.
6167 * pstg [I] IStorage pointer
6168 * pclsid [O] Pointer to where the CLSID is written
6172 * Failure: HRESULT code.
6174 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6179 TRACE("(%p, %p)\n", pstg
, pclsid
);
6181 if(!pstg
|| !pclsid
)
6182 return E_INVALIDARG
;
6185 * read a STATSTG structure (contains the clsid) from the storage
6187 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6190 *pclsid
=pstatstg
.clsid
;
6195 /***********************************************************************
6196 * OleLoadFromStream (OLE32.@)
6198 * This function loads an object from stream
6200 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6204 LPPERSISTSTREAM xstm
;
6206 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6208 res
=ReadClassStm(pStm
,&clsid
);
6211 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6214 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6216 IUnknown_Release((IUnknown
*)*ppvObj
);
6219 res
=IPersistStream_Load(xstm
,pStm
);
6220 IPersistStream_Release(xstm
);
6221 /* FIXME: all refcounts ok at this point? I think they should be:
6224 * xstm : 0 (released)
6229 /***********************************************************************
6230 * OleSaveToStream (OLE32.@)
6232 * This function saves an object with the IPersistStream interface on it
6233 * to the specified stream.
6235 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6241 TRACE("(%p,%p)\n",pPStm
,pStm
);
6243 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6245 if (SUCCEEDED(res
)){
6247 res
=WriteClassStm(pStm
,&clsid
);
6251 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6254 TRACE("Finished Save\n");
6258 /****************************************************************************
6259 * This method validate a STGM parameter that can contain the values below
6261 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6262 * The stgm values contained in 0xffff0000 are bitmasks.
6264 * STGM_DIRECT 0x00000000
6265 * STGM_TRANSACTED 0x00010000
6266 * STGM_SIMPLE 0x08000000
6268 * STGM_READ 0x00000000
6269 * STGM_WRITE 0x00000001
6270 * STGM_READWRITE 0x00000002
6272 * STGM_SHARE_DENY_NONE 0x00000040
6273 * STGM_SHARE_DENY_READ 0x00000030
6274 * STGM_SHARE_DENY_WRITE 0x00000020
6275 * STGM_SHARE_EXCLUSIVE 0x00000010
6277 * STGM_PRIORITY 0x00040000
6278 * STGM_DELETEONRELEASE 0x04000000
6280 * STGM_CREATE 0x00001000
6281 * STGM_CONVERT 0x00020000
6282 * STGM_FAILIFTHERE 0x00000000
6284 * STGM_NOSCRATCH 0x00100000
6285 * STGM_NOSNAPSHOT 0x00200000
6287 static HRESULT
validateSTGM(DWORD stgm
)
6289 DWORD access
= STGM_ACCESS_MODE(stgm
);
6290 DWORD share
= STGM_SHARE_MODE(stgm
);
6291 DWORD create
= STGM_CREATE_MODE(stgm
);
6293 if (stgm
&~STGM_KNOWN_FLAGS
)
6295 ERR("unknown flags %08x\n", stgm
);
6303 case STGM_READWRITE
:
6311 case STGM_SHARE_DENY_NONE
:
6312 case STGM_SHARE_DENY_READ
:
6313 case STGM_SHARE_DENY_WRITE
:
6314 case STGM_SHARE_EXCLUSIVE
:
6323 case STGM_FAILIFTHERE
:
6330 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6332 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6336 * STGM_CREATE | STGM_CONVERT
6337 * if both are false, STGM_FAILIFTHERE is set to TRUE
6339 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6343 * STGM_NOSCRATCH requires STGM_TRANSACTED
6345 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6349 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6350 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6352 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6353 (!(stgm
& STGM_TRANSACTED
) ||
6354 share
== STGM_SHARE_EXCLUSIVE
||
6355 share
== STGM_SHARE_DENY_WRITE
) )
6361 /****************************************************************************
6362 * GetShareModeFromSTGM
6364 * This method will return a share mode flag from a STGM value.
6365 * The STGM value is assumed valid.
6367 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6369 switch (STGM_SHARE_MODE(stgm
))
6371 case STGM_SHARE_DENY_NONE
:
6372 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6373 case STGM_SHARE_DENY_READ
:
6374 return FILE_SHARE_WRITE
;
6375 case STGM_SHARE_DENY_WRITE
:
6376 return FILE_SHARE_READ
;
6377 case STGM_SHARE_EXCLUSIVE
:
6380 ERR("Invalid share mode!\n");
6385 /****************************************************************************
6386 * GetAccessModeFromSTGM
6388 * This method will return an access mode flag from a STGM value.
6389 * The STGM value is assumed valid.
6391 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6393 switch (STGM_ACCESS_MODE(stgm
))
6396 return GENERIC_READ
;
6398 case STGM_READWRITE
:
6399 return GENERIC_READ
| GENERIC_WRITE
;
6401 ERR("Invalid access mode!\n");
6406 /****************************************************************************
6407 * GetCreationModeFromSTGM
6409 * This method will return a creation mode flag from a STGM value.
6410 * The STGM value is assumed valid.
6412 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6414 switch(STGM_CREATE_MODE(stgm
))
6417 return CREATE_ALWAYS
;
6419 FIXME("STGM_CONVERT not implemented!\n");
6421 case STGM_FAILIFTHERE
:
6424 ERR("Invalid create mode!\n");
6430 /*************************************************************************
6431 * OLECONVERT_LoadOLE10 [Internal]
6433 * Loads the OLE10 STREAM to memory
6436 * pOleStream [I] The OLESTREAM
6437 * pData [I] Data Structure for the OLESTREAM Data
6441 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6442 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6445 * This function is used by OleConvertOLESTREAMToIStorage only.
6447 * Memory allocated for pData must be freed by the caller
6449 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6452 HRESULT hRes
= S_OK
;
6456 pData
->pData
= NULL
;
6457 pData
->pstrOleObjFileName
= NULL
;
6459 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6462 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6463 if(dwSize
!= sizeof(pData
->dwOleID
))
6465 hRes
= CONVERT10_E_OLESTREAM_GET
;
6467 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6469 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6480 /* Get the TypeID... more info needed for this field */
6481 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6482 if(dwSize
!= sizeof(pData
->dwTypeID
))
6484 hRes
= CONVERT10_E_OLESTREAM_GET
;
6489 if(pData
->dwTypeID
!= 0)
6491 /* Get the length of the OleTypeName */
6492 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6493 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6495 hRes
= CONVERT10_E_OLESTREAM_GET
;
6500 if(pData
->dwOleTypeNameLength
> 0)
6502 /* Get the OleTypeName */
6503 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6504 if(dwSize
!= pData
->dwOleTypeNameLength
)
6506 hRes
= CONVERT10_E_OLESTREAM_GET
;
6512 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6513 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6515 hRes
= CONVERT10_E_OLESTREAM_GET
;
6519 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6520 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6521 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6522 if(pData
->pstrOleObjFileName
)
6524 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
6525 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6527 hRes
= CONVERT10_E_OLESTREAM_GET
;
6531 hRes
= CONVERT10_E_OLESTREAM_GET
;
6536 /* Get the Width of the Metafile */
6537 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6538 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6540 hRes
= CONVERT10_E_OLESTREAM_GET
;
6544 /* Get the Height of the Metafile */
6545 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6546 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6548 hRes
= CONVERT10_E_OLESTREAM_GET
;
6554 /* Get the Length of the Data */
6555 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6556 if(dwSize
!= sizeof(pData
->dwDataLength
))
6558 hRes
= CONVERT10_E_OLESTREAM_GET
;
6562 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
6564 if(!bStrem1
) /* if it is a second OLE stream data */
6566 pData
->dwDataLength
-= 8;
6567 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
6568 if(dwSize
!= sizeof(pData
->strUnknown
))
6570 hRes
= CONVERT10_E_OLESTREAM_GET
;
6576 if(pData
->dwDataLength
> 0)
6578 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6580 /* Get Data (ex. IStorage, Metafile, or BMP) */
6583 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6584 if(dwSize
!= pData
->dwDataLength
)
6586 hRes
= CONVERT10_E_OLESTREAM_GET
;
6591 hRes
= CONVERT10_E_OLESTREAM_GET
;
6600 /*************************************************************************
6601 * OLECONVERT_SaveOLE10 [Internal]
6603 * Saves the OLE10 STREAM From memory
6606 * pData [I] Data Structure for the OLESTREAM Data
6607 * pOleStream [I] The OLESTREAM to save
6611 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6614 * This function is used by OleConvertIStorageToOLESTREAM only.
6617 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6620 HRESULT hRes
= S_OK
;
6624 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6625 if(dwSize
!= sizeof(pData
->dwOleID
))
6627 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6632 /* Set the TypeID */
6633 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6634 if(dwSize
!= sizeof(pData
->dwTypeID
))
6636 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6640 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6642 /* Set the Length of the OleTypeName */
6643 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6644 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6646 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6651 if(pData
->dwOleTypeNameLength
> 0)
6653 /* Set the OleTypeName */
6654 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6655 if(dwSize
!= pData
->dwOleTypeNameLength
)
6657 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6664 /* Set the width of the Metafile */
6665 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6666 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6668 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6674 /* Set the height of the Metafile */
6675 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6676 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6678 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6684 /* Set the length of the Data */
6685 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6686 if(dwSize
!= sizeof(pData
->dwDataLength
))
6688 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6694 if(pData
->dwDataLength
> 0)
6696 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6697 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6698 if(dwSize
!= pData
->dwDataLength
)
6700 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6708 /*************************************************************************
6709 * OLECONVERT_GetOLE20FromOLE10[Internal]
6711 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6712 * opens it, and copies the content to the dest IStorage for
6713 * OleConvertOLESTREAMToIStorage
6717 * pDestStorage [I] The IStorage to copy the data to
6718 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6719 * nBufferLength [I] The size of the buffer
6728 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
6732 IStorage
*pTempStorage
;
6733 DWORD dwNumOfBytesWritten
;
6734 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6735 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6737 /* Create a temp File */
6738 GetTempPathW(MAX_PATH
, wstrTempDir
);
6739 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6740 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6742 if(hFile
!= INVALID_HANDLE_VALUE
)
6744 /* Write IStorage Data to File */
6745 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6748 /* Open and copy temp storage to the Dest Storage */
6749 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6752 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6753 IStorage_Release(pTempStorage
);
6755 DeleteFileW(wstrTempFile
);
6760 /*************************************************************************
6761 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6763 * Saves the OLE10 STREAM From memory
6766 * pStorage [I] The Src IStorage to copy
6767 * pData [I] The Dest Memory to write to.
6770 * The size in bytes allocated for pData
6773 * Memory allocated for pData must be freed by the caller
6775 * Used by OleConvertIStorageToOLESTREAM only.
6778 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6782 DWORD nDataLength
= 0;
6783 IStorage
*pTempStorage
;
6784 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6785 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6789 /* Create temp Storage */
6790 GetTempPathW(MAX_PATH
, wstrTempDir
);
6791 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6792 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6796 /* Copy Src Storage to the Temp Storage */
6797 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6798 IStorage_Release(pTempStorage
);
6800 /* Open Temp Storage as a file and copy to memory */
6801 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6802 if(hFile
!= INVALID_HANDLE_VALUE
)
6804 nDataLength
= GetFileSize(hFile
, NULL
);
6805 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6806 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6809 DeleteFileW(wstrTempFile
);
6814 /*************************************************************************
6815 * OLECONVERT_CreateOleStream [Internal]
6817 * Creates the "\001OLE" stream in the IStorage if necessary.
6820 * pStorage [I] Dest storage to create the stream in
6826 * This function is used by OleConvertOLESTREAMToIStorage only.
6828 * This stream is still unknown, MS Word seems to have extra data
6829 * but since the data is stored in the OLESTREAM there should be
6830 * no need to recreate the stream. If the stream is manually
6831 * deleted it will create it with this default data.
6834 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6838 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6839 BYTE pOleStreamHeader
[] =
6841 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6842 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6843 0x00, 0x00, 0x00, 0x00
6846 /* Create stream if not present */
6847 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6848 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6852 /* Write default Data */
6853 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6854 IStream_Release(pStream
);
6858 /* write a string to a stream, preceded by its length */
6859 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6866 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6867 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6872 str
= CoTaskMemAlloc( len
);
6873 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6874 r
= IStream_Write( stm
, str
, len
, NULL
);
6875 CoTaskMemFree( str
);
6879 /* read a string preceded by its length from a stream */
6880 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6883 DWORD len
, count
= 0;
6887 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6890 if( count
!= sizeof(len
) )
6891 return E_OUTOFMEMORY
;
6893 TRACE("%d bytes\n",len
);
6895 str
= CoTaskMemAlloc( len
);
6897 return E_OUTOFMEMORY
;
6899 r
= IStream_Read( stm
, str
, len
, &count
);
6904 CoTaskMemFree( str
);
6905 return E_OUTOFMEMORY
;
6908 TRACE("Read string %s\n",debugstr_an(str
,len
));
6910 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6911 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6913 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6914 CoTaskMemFree( str
);
6922 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6923 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6927 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6929 static const BYTE unknown1
[12] =
6930 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6931 0xFF, 0xFF, 0xFF, 0xFF};
6932 static const BYTE unknown2
[16] =
6933 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6936 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6937 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6938 debugstr_w(szProgIDName
));
6940 /* Create a CompObj stream */
6941 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6942 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6946 /* Write CompObj Structure to stream */
6947 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6949 if( SUCCEEDED( r
) )
6950 r
= WriteClassStm( pstm
, clsid
);
6952 if( SUCCEEDED( r
) )
6953 r
= STREAM_WriteString( pstm
, lpszUserType
);
6954 if( SUCCEEDED( r
) )
6955 r
= STREAM_WriteString( pstm
, szClipName
);
6956 if( SUCCEEDED( r
) )
6957 r
= STREAM_WriteString( pstm
, szProgIDName
);
6958 if( SUCCEEDED( r
) )
6959 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6961 IStream_Release( pstm
);
6966 /***********************************************************************
6967 * WriteFmtUserTypeStg (OLE32.@)
6969 HRESULT WINAPI
WriteFmtUserTypeStg(
6970 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6973 WCHAR szwClipName
[0x40];
6974 CLSID clsid
= CLSID_NULL
;
6975 LPWSTR wstrProgID
= NULL
;
6978 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6980 /* get the clipboard format name */
6981 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
6984 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6986 /* FIXME: There's room to save a CLSID and its ProgID, but
6987 the CLSID is not looked up in the registry and in all the
6988 tests I wrote it was CLSID_NULL. Where does it come from?
6991 /* get the real program ID. This may fail, but that's fine */
6992 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6994 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6996 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6997 lpszUserType
, szwClipName
, wstrProgID
);
6999 CoTaskMemFree(wstrProgID
);
7005 /******************************************************************************
7006 * ReadFmtUserTypeStg [OLE32.@]
7008 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7012 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7013 unsigned char unknown1
[12];
7014 unsigned char unknown2
[16];
7016 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7019 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7021 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7022 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7025 WARN("Failed to open stream r = %08x\n", r
);
7029 /* read the various parts of the structure */
7030 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7031 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7033 r
= ReadClassStm( stm
, &clsid
);
7037 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7041 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7045 r
= STREAM_ReadString( stm
, &szProgIDName
);
7049 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7050 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7053 /* ok, success... now we just need to store what we found */
7055 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7056 CoTaskMemFree( szOleTypeName
);
7058 if( lplpszUserType
)
7059 *lplpszUserType
= szCLSIDName
;
7060 CoTaskMemFree( szProgIDName
);
7063 IStream_Release( stm
);
7069 /*************************************************************************
7070 * OLECONVERT_CreateCompObjStream [Internal]
7072 * Creates a "\001CompObj" is the destination IStorage if necessary.
7075 * pStorage [I] The dest IStorage to create the CompObj Stream
7077 * strOleTypeName [I] The ProgID
7081 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7084 * This function is used by OleConvertOLESTREAMToIStorage only.
7086 * The stream data is stored in the OLESTREAM and there should be
7087 * no need to recreate the stream. If the stream is manually
7088 * deleted it will attempt to create it by querying the registry.
7092 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7095 HRESULT hStorageRes
, hRes
= S_OK
;
7096 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7097 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7098 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7100 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7101 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7103 /* Initialize the CompObj structure */
7104 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7105 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7106 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7109 /* Create a CompObj stream if it doesn't exist */
7110 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7111 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7112 if(hStorageRes
== S_OK
)
7114 /* copy the OleTypeName to the compobj struct */
7115 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7116 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7118 /* copy the OleTypeName to the compobj struct */
7119 /* Note: in the test made, these were Identical */
7120 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7121 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7124 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7125 bufferW
, OLESTREAM_MAX_STR_LEN
);
7126 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7132 /* Get the CLSID Default Name from the Registry */
7133 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7134 if(hErr
== ERROR_SUCCESS
)
7136 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7137 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7138 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7139 if(hErr
== ERROR_SUCCESS
)
7141 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7147 /* Write CompObj Structure to stream */
7148 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7150 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7152 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7153 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7155 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7157 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7158 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7160 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7162 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7163 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7165 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7167 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7168 IStream_Release(pStream
);
7174 /*************************************************************************
7175 * OLECONVERT_CreateOlePresStream[Internal]
7177 * Creates the "\002OlePres000" Stream with the Metafile data
7180 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7181 * dwExtentX [I] Width of the Metafile
7182 * dwExtentY [I] Height of the Metafile
7183 * pData [I] Metafile data
7184 * dwDataLength [I] Size of the Metafile data
7188 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7191 * This function is used by OleConvertOLESTREAMToIStorage only.
7194 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7198 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7199 BYTE pOlePresStreamHeader
[] =
7201 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7202 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7203 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7204 0x00, 0x00, 0x00, 0x00
7207 BYTE pOlePresStreamHeaderEmpty
[] =
7209 0x00, 0x00, 0x00, 0x00,
7210 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7211 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7212 0x00, 0x00, 0x00, 0x00
7215 /* Create the OlePres000 Stream */
7216 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7217 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7222 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7224 memset(&OlePres
, 0, sizeof(OlePres
));
7225 /* Do we have any metafile data to save */
7226 if(dwDataLength
> 0)
7228 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7229 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7233 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7234 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7236 /* Set width and height of the metafile */
7237 OlePres
.dwExtentX
= dwExtentX
;
7238 OlePres
.dwExtentY
= -dwExtentY
;
7240 /* Set Data and Length */
7241 if(dwDataLength
> sizeof(METAFILEPICT16
))
7243 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7244 OlePres
.pData
= &(pData
[8]);
7246 /* Save OlePres000 Data to Stream */
7247 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7248 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7249 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7250 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7251 if(OlePres
.dwSize
> 0)
7253 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7255 IStream_Release(pStream
);
7259 /*************************************************************************
7260 * OLECONVERT_CreateOle10NativeStream [Internal]
7262 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7265 * pStorage [I] Dest storage to create the stream in
7266 * pData [I] Ole10 Native Data (ex. bmp)
7267 * dwDataLength [I] Size of the Ole10 Native Data
7273 * This function is used by OleConvertOLESTREAMToIStorage only.
7275 * Might need to verify the data and return appropriate error message
7278 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7282 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7284 /* Create the Ole10Native Stream */
7285 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7286 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7290 /* Write info to stream */
7291 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7292 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7293 IStream_Release(pStream
);
7298 /*************************************************************************
7299 * OLECONVERT_GetOLE10ProgID [Internal]
7301 * Finds the ProgID (or OleTypeID) from the IStorage
7304 * pStorage [I] The Src IStorage to get the ProgID
7305 * strProgID [I] the ProgID string to get
7306 * dwSize [I] the size of the string
7310 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7313 * This function is used by OleConvertIStorageToOLESTREAM only.
7317 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7321 LARGE_INTEGER iSeekPos
;
7322 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7323 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7325 /* Open the CompObj Stream */
7326 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7327 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7331 /*Get the OleType from the CompObj Stream */
7332 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7333 iSeekPos
.u
.HighPart
= 0;
7335 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7336 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7337 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7338 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7339 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7340 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7341 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7343 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7346 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7348 IStream_Release(pStream
);
7353 LPOLESTR wstrProgID
;
7355 /* Get the OleType from the registry */
7356 REFCLSID clsid
= &(stat
.clsid
);
7357 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7358 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7361 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7368 /*************************************************************************
7369 * OLECONVERT_GetOle10PresData [Internal]
7371 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7374 * pStorage [I] Src IStroage
7375 * pOleStream [I] Dest OleStream Mem Struct
7381 * This function is used by OleConvertIStorageToOLESTREAM only.
7383 * Memory allocated for pData must be freed by the caller
7387 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7392 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7394 /* Initialize Default data for OLESTREAM */
7395 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7396 pOleStreamData
[0].dwTypeID
= 2;
7397 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7398 pOleStreamData
[1].dwTypeID
= 0;
7399 pOleStreamData
[0].dwMetaFileWidth
= 0;
7400 pOleStreamData
[0].dwMetaFileHeight
= 0;
7401 pOleStreamData
[0].pData
= NULL
;
7402 pOleStreamData
[1].pData
= NULL
;
7404 /* Open Ole10Native Stream */
7405 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7406 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7410 /* Read Size and Data */
7411 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7412 if(pOleStreamData
->dwDataLength
> 0)
7414 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7415 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7417 IStream_Release(pStream
);
7423 /*************************************************************************
7424 * OLECONVERT_GetOle20PresData[Internal]
7426 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7429 * pStorage [I] Src IStroage
7430 * pOleStreamData [I] Dest OleStream Mem Struct
7436 * This function is used by OleConvertIStorageToOLESTREAM only.
7438 * Memory allocated for pData must be freed by the caller
7440 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7444 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7445 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7447 /* Initialize Default data for OLESTREAM */
7448 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7449 pOleStreamData
[0].dwTypeID
= 2;
7450 pOleStreamData
[0].dwMetaFileWidth
= 0;
7451 pOleStreamData
[0].dwMetaFileHeight
= 0;
7452 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7453 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7454 pOleStreamData
[1].dwTypeID
= 0;
7455 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7456 pOleStreamData
[1].strOleTypeName
[0] = 0;
7457 pOleStreamData
[1].dwMetaFileWidth
= 0;
7458 pOleStreamData
[1].dwMetaFileHeight
= 0;
7459 pOleStreamData
[1].pData
= NULL
;
7460 pOleStreamData
[1].dwDataLength
= 0;
7463 /* Open OlePress000 stream */
7464 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7465 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7468 LARGE_INTEGER iSeekPos
;
7469 METAFILEPICT16 MetaFilePict
;
7470 static const char strMetafilePictName
[] = "METAFILEPICT";
7472 /* Set the TypeID for a Metafile */
7473 pOleStreamData
[1].dwTypeID
= 5;
7475 /* Set the OleTypeName to Metafile */
7476 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7477 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7479 iSeekPos
.u
.HighPart
= 0;
7480 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7482 /* Get Presentation Data */
7483 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7484 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7485 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7486 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7488 /*Set width and Height */
7489 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7490 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7491 if(olePress
.dwSize
> 0)
7494 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7496 /* Set MetaFilePict struct */
7497 MetaFilePict
.mm
= 8;
7498 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7499 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7500 MetaFilePict
.hMF
= 0;
7502 /* Get Metafile Data */
7503 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7504 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7505 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7507 IStream_Release(pStream
);
7511 /*************************************************************************
7512 * OleConvertOLESTREAMToIStorage [OLE32.@]
7517 * DVTARGETDEVICE parameter is not handled
7518 * Still unsure of some mem fields for OLE 10 Stream
7519 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7520 * and "\001OLE" streams
7523 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7524 LPOLESTREAM pOleStream
,
7526 const DVTARGETDEVICE
* ptd
)
7530 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7532 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7534 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7538 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7541 if(pstg
== NULL
|| pOleStream
== NULL
)
7543 hRes
= E_INVALIDARG
;
7548 /* Load the OLESTREAM to Memory */
7549 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7554 /* Load the OLESTREAM to Memory (part 2)*/
7555 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7561 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7563 /* Do we have the IStorage Data in the OLESTREAM */
7564 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7566 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7567 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7571 /* It must be an original OLE 1.0 source */
7572 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7577 /* It must be an original OLE 1.0 source */
7578 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7581 /* Create CompObj Stream if necessary */
7582 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7585 /*Create the Ole Stream if necessary */
7586 OLECONVERT_CreateOleStream(pstg
);
7591 /* Free allocated memory */
7592 for(i
=0; i
< 2; i
++)
7594 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7595 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7596 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7601 /*************************************************************************
7602 * OleConvertIStorageToOLESTREAM [OLE32.@]
7609 * Still unsure of some mem fields for OLE 10 Stream
7610 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7611 * and "\001OLE" streams.
7614 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7616 LPOLESTREAM pOleStream
)
7619 HRESULT hRes
= S_OK
;
7621 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7622 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7624 TRACE("%p %p\n", pstg
, pOleStream
);
7626 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7628 if(pstg
== NULL
|| pOleStream
== NULL
)
7630 hRes
= E_INVALIDARG
;
7634 /* Get the ProgID */
7635 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7636 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7640 /* Was it originally Ole10 */
7641 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7644 IStream_Release(pStream
);
7645 /* Get Presentation Data for Ole10Native */
7646 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7650 /* Get Presentation Data (OLE20) */
7651 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7654 /* Save OLESTREAM */
7655 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7658 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7663 /* Free allocated memory */
7664 for(i
=0; i
< 2; i
++)
7666 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7672 /***********************************************************************
7673 * GetConvertStg (OLE32.@)
7675 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7676 FIXME("unimplemented stub!\n");
7680 /******************************************************************************
7681 * StgIsStorageFile [OLE32.@]
7682 * Verify if the file contains a storage object
7688 * S_OK if file has magic bytes as a storage object
7689 * S_FALSE if file is not storage
7692 StgIsStorageFile(LPCOLESTR fn
)
7698 TRACE("%s\n", debugstr_w(fn
));
7699 hf
= CreateFileW(fn
, GENERIC_READ
,
7700 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7701 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7703 if (hf
== INVALID_HANDLE_VALUE
)
7704 return STG_E_FILENOTFOUND
;
7706 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7708 WARN(" unable to read file\n");
7715 if (bytes_read
!= 8) {
7716 WARN(" too short\n");
7720 if (!memcmp(magic
,STORAGE_magic
,8)) {
7725 WARN(" -> Invalid header.\n");
7729 /***********************************************************************
7730 * WriteClassStm (OLE32.@)
7732 * Writes a CLSID to a stream.
7735 * pStm [I] Stream to write to.
7736 * rclsid [I] CLSID to write.
7740 * Failure: HRESULT code.
7742 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7744 TRACE("(%p,%p)\n",pStm
,rclsid
);
7746 if (!pStm
|| !rclsid
)
7747 return E_INVALIDARG
;
7749 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7752 /***********************************************************************
7753 * ReadClassStm (OLE32.@)
7755 * Reads a CLSID from a stream.
7758 * pStm [I] Stream to read from.
7759 * rclsid [O] CLSID to read.
7763 * Failure: HRESULT code.
7765 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7770 TRACE("(%p,%p)\n",pStm
,pclsid
);
7772 if (!pStm
|| !pclsid
)
7773 return E_INVALIDARG
;
7775 /* clear the output args */
7776 *pclsid
= CLSID_NULL
;
7778 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7783 if (nbByte
!= sizeof(CLSID
))
7784 return STG_E_READFAULT
;