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
createDirEntry(
188 StorageImpl
*storage
,
189 const DirEntry
*newData
,
192 static HRESULT
destroyDirEntry(
193 StorageImpl
*storage
,
196 static HRESULT
insertIntoTree(
198 DirRef parentStorageIndex
,
199 DirRef newEntryIndex
);
201 static LONG
entryNameCmp(
202 const OLECHAR
*name1
,
203 const OLECHAR
*name2
);
205 static DirRef
findElement(
206 StorageImpl
*storage
,
211 static HRESULT
findTreeParent(
212 StorageImpl
*storage
,
214 const OLECHAR
*childName
,
215 DirEntry
*parentData
,
219 /***********************************************************************
220 * Declaration of miscellaneous functions...
222 static HRESULT
validateSTGM(DWORD stgmValue
);
224 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
225 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
226 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
228 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
231 /****************************************************************************
232 * IEnumSTATSTGImpl definitions.
234 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
235 * This class allows iterating through the content of a storage and to find
236 * specific items inside it.
238 struct IEnumSTATSTGImpl
240 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
241 * since we want to cast this in an IEnumSTATSTG pointer */
243 LONG ref
; /* Reference count */
244 StorageImpl
* parentStorage
; /* Reference to the parent storage */
245 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
248 * The current implementation of the IEnumSTATSTGImpl class uses a stack
249 * to walk the directory entries to get the content of a storage. This stack
250 * is implemented by the following 3 data members
254 DirRef
* stackToVisit
;
256 #define ENUMSTATSGT_SIZE_INCREMENT 10
260 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageImpl
* This
, DirRef storageDirEntry
);
261 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
262 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, DirRef nodeToPush
);
263 static DirRef
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
265 /************************************************************************
269 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
271 if (index
== 0xffffffff)
276 return index
* BIG_BLOCK_SIZE
;
279 /************************************************************************
280 ** Storage32BaseImpl implementation
282 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
283 ULARGE_INTEGER offset
,
288 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
291 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
292 ULARGE_INTEGER offset
,
297 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
300 /************************************************************************
301 * Storage32BaseImpl_QueryInterface (IUnknown)
303 * This method implements the common QueryInterface for all IStorage32
304 * implementations contained in this file.
306 * See Windows documentation for more details on IUnknown methods.
308 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
313 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
315 if ( (This
==0) || (ppvObject
==0) )
320 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
321 IsEqualGUID(&IID_IStorage
, riid
))
325 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
327 *ppvObject
= &This
->pssVtbl
;
331 return E_NOINTERFACE
;
333 IStorage_AddRef(iface
);
338 /************************************************************************
339 * Storage32BaseImpl_AddRef (IUnknown)
341 * This method implements the common AddRef for all IStorage32
342 * implementations contained in this file.
344 * See Windows documentation for more details on IUnknown methods.
346 static ULONG WINAPI
StorageBaseImpl_AddRef(
349 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
350 ULONG ref
= InterlockedIncrement(&This
->ref
);
352 TRACE("(%p) AddRef to %d\n", This
, ref
);
357 /************************************************************************
358 * Storage32BaseImpl_Release (IUnknown)
360 * This method implements the common Release for all IStorage32
361 * implementations contained in this file.
363 * See Windows documentation for more details on IUnknown methods.
365 static ULONG WINAPI
StorageBaseImpl_Release(
368 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
370 ULONG ref
= InterlockedDecrement(&This
->ref
);
372 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
377 * Since we are using a system of base-classes, we want to call the
378 * destructor of the appropriate derived class. To do this, we are
379 * using virtual functions to implement the destructor.
381 StorageBaseImpl_Destroy(This
);
387 /************************************************************************
388 * Storage32BaseImpl_OpenStream (IStorage)
390 * This method will open the specified stream object from the current storage.
392 * See Windows documentation for more details on IStorage methods.
394 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
396 const OLECHAR
* pwcsName
, /* [string][in] */
397 void* reserved1
, /* [unique][in] */
398 DWORD grfMode
, /* [in] */
399 DWORD reserved2
, /* [in] */
400 IStream
** ppstm
) /* [out] */
402 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
403 StgStreamImpl
* newStream
;
404 DirEntry currentEntry
;
405 DirRef streamEntryRef
;
406 HRESULT res
= STG_E_UNKNOWN
;
408 TRACE("(%p, %s, %p, %x, %d, %p)\n",
409 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
411 if ( (pwcsName
==NULL
) || (ppstm
==0) )
419 if ( FAILED( validateSTGM(grfMode
) ) ||
420 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
422 res
= STG_E_INVALIDFLAG
;
429 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
431 res
= STG_E_INVALIDFUNCTION
;
435 if (!This
->ancestorStorage
)
437 res
= STG_E_REVERTED
;
442 * Check that we're compatible with the parent's storage mode, but
443 * only if we are not in transacted mode
445 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
446 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
448 res
= STG_E_ACCESSDENIED
;
454 * Search for the element with the given name
456 streamEntryRef
= findElement(
457 This
->ancestorStorage
,
458 This
->storageDirEntry
,
463 * If it was found, construct the stream object and return a pointer to it.
465 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
466 (currentEntry
.stgType
==STGTY_STREAM
) )
468 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
470 /* A single stream cannot be opened a second time. */
471 res
= STG_E_ACCESSDENIED
;
475 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
479 newStream
->grfMode
= grfMode
;
480 *ppstm
= (IStream
*)newStream
;
482 IStream_AddRef(*ppstm
);
492 res
= STG_E_FILENOTFOUND
;
496 TRACE("<-- IStream %p\n", *ppstm
);
497 TRACE("<-- %08x\n", res
);
501 /************************************************************************
502 * Storage32BaseImpl_OpenStorage (IStorage)
504 * This method will open a new storage object from the current storage.
506 * See Windows documentation for more details on IStorage methods.
508 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
510 const OLECHAR
* pwcsName
, /* [string][unique][in] */
511 IStorage
* pstgPriority
, /* [unique][in] */
512 DWORD grfMode
, /* [in] */
513 SNB snbExclude
, /* [unique][in] */
514 DWORD reserved
, /* [in] */
515 IStorage
** ppstg
) /* [out] */
517 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
518 StorageInternalImpl
* newStorage
;
519 DirEntry currentEntry
;
520 DirRef storageEntryRef
;
521 HRESULT res
= STG_E_UNKNOWN
;
523 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
524 iface
, debugstr_w(pwcsName
), pstgPriority
,
525 grfMode
, snbExclude
, reserved
, ppstg
);
527 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
533 if (This
->openFlags
& STGM_SIMPLE
)
535 res
= STG_E_INVALIDFUNCTION
;
540 if (snbExclude
!= NULL
)
542 res
= STG_E_INVALIDPARAMETER
;
546 if ( FAILED( validateSTGM(grfMode
) ))
548 res
= STG_E_INVALIDFLAG
;
555 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
556 (grfMode
& STGM_DELETEONRELEASE
) ||
557 (grfMode
& STGM_PRIORITY
) )
559 res
= STG_E_INVALIDFUNCTION
;
563 if (!This
->ancestorStorage
)
564 return STG_E_REVERTED
;
567 * Check that we're compatible with the parent's storage mode,
568 * but only if we are not transacted
570 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
571 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
573 res
= STG_E_ACCESSDENIED
;
580 storageEntryRef
= findElement(
581 This
->ancestorStorage
,
582 This
->storageDirEntry
,
586 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
587 (currentEntry
.stgType
==STGTY_STORAGE
) )
589 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
591 /* A single storage cannot be opened a second time. */
592 res
= STG_E_ACCESSDENIED
;
596 newStorage
= StorageInternalImpl_Construct(
597 This
->ancestorStorage
,
603 *ppstg
= (IStorage
*)newStorage
;
605 StorageBaseImpl_AddRef(*ppstg
);
607 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
613 res
= STG_E_INSUFFICIENTMEMORY
;
617 res
= STG_E_FILENOTFOUND
;
620 TRACE("<-- %08x\n", res
);
624 /************************************************************************
625 * Storage32BaseImpl_EnumElements (IStorage)
627 * This method will create an enumerator object that can be used to
628 * retrieve information about all the elements in the storage object.
630 * See Windows documentation for more details on IStorage methods.
632 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
634 DWORD reserved1
, /* [in] */
635 void* reserved2
, /* [size_is][unique][in] */
636 DWORD reserved3
, /* [in] */
637 IEnumSTATSTG
** ppenum
) /* [out] */
639 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
640 IEnumSTATSTGImpl
* newEnum
;
642 TRACE("(%p, %d, %p, %d, %p)\n",
643 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
645 if ( (This
==0) || (ppenum
==0))
648 if (!This
->ancestorStorage
)
649 return STG_E_REVERTED
;
651 newEnum
= IEnumSTATSTGImpl_Construct(
652 This
->ancestorStorage
,
653 This
->storageDirEntry
);
657 *ppenum
= (IEnumSTATSTG
*)newEnum
;
659 IEnumSTATSTG_AddRef(*ppenum
);
664 return E_OUTOFMEMORY
;
667 /************************************************************************
668 * Storage32BaseImpl_Stat (IStorage)
670 * This method will retrieve information about this storage object.
672 * See Windows documentation for more details on IStorage methods.
674 static HRESULT WINAPI
StorageBaseImpl_Stat(
676 STATSTG
* pstatstg
, /* [out] */
677 DWORD grfStatFlag
) /* [in] */
679 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
680 DirEntry currentEntry
;
682 HRESULT res
= STG_E_UNKNOWN
;
684 TRACE("(%p, %p, %x)\n",
685 iface
, pstatstg
, grfStatFlag
);
687 if ( (This
==0) || (pstatstg
==0))
693 if (!This
->ancestorStorage
)
695 res
= STG_E_REVERTED
;
699 readSuccessful
= StorageImpl_ReadDirEntry(
700 This
->ancestorStorage
,
701 This
->storageDirEntry
,
706 StorageUtl_CopyDirEntryToSTATSTG(
712 pstatstg
->grfMode
= This
->openFlags
;
713 pstatstg
->grfStateBits
= This
->stateBits
;
724 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
);
726 TRACE("<-- %08x\n", res
);
730 /************************************************************************
731 * Storage32BaseImpl_RenameElement (IStorage)
733 * This method will rename the specified element.
735 * See Windows documentation for more details on IStorage methods.
737 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
739 const OLECHAR
* pwcsOldName
, /* [in] */
740 const OLECHAR
* pwcsNewName
) /* [in] */
742 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
743 DirEntry currentEntry
;
744 DirRef currentEntryRef
;
746 TRACE("(%p, %s, %s)\n",
747 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
749 if (!This
->ancestorStorage
)
750 return STG_E_REVERTED
;
752 currentEntryRef
= findElement(This
->ancestorStorage
,
753 This
->storageDirEntry
,
757 if (currentEntryRef
!= DIRENTRY_NULL
)
760 * There is already an element with the new name
762 return STG_E_FILEALREADYEXISTS
;
766 * Search for the old element name
768 currentEntryRef
= findElement(This
->ancestorStorage
,
769 This
->storageDirEntry
,
773 if (currentEntryRef
!= DIRENTRY_NULL
)
775 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
776 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
778 WARN("Element is already open; cannot rename.\n");
779 return STG_E_ACCESSDENIED
;
782 /* Remove the element from its current position in the tree */
783 removeFromTree(This
->ancestorStorage
, This
->storageDirEntry
,
786 /* Change the name of the element */
787 strcpyW(currentEntry
.name
, pwcsNewName
);
789 StorageImpl_WriteDirEntry(This
->ancestorStorage
, currentEntryRef
,
792 /* Insert the element in a new position in the tree */
793 insertIntoTree(This
->ancestorStorage
, This
->storageDirEntry
,
799 * There is no element with the old name
801 return STG_E_FILENOTFOUND
;
807 /************************************************************************
808 * Storage32BaseImpl_CreateStream (IStorage)
810 * This method will create a stream object within this storage
812 * See Windows documentation for more details on IStorage methods.
814 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
816 const OLECHAR
* pwcsName
, /* [string][in] */
817 DWORD grfMode
, /* [in] */
818 DWORD reserved1
, /* [in] */
819 DWORD reserved2
, /* [in] */
820 IStream
** ppstm
) /* [out] */
822 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
823 StgStreamImpl
* newStream
;
824 DirEntry currentEntry
, newStreamEntry
;
825 DirRef currentEntryRef
, newStreamEntryRef
;
827 TRACE("(%p, %s, %x, %d, %d, %p)\n",
828 iface
, debugstr_w(pwcsName
), grfMode
,
829 reserved1
, reserved2
, ppstm
);
832 return STG_E_INVALIDPOINTER
;
835 return STG_E_INVALIDNAME
;
837 if (reserved1
|| reserved2
)
838 return STG_E_INVALIDPARAMETER
;
840 if ( FAILED( validateSTGM(grfMode
) ))
841 return STG_E_INVALIDFLAG
;
843 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
844 return STG_E_INVALIDFLAG
;
846 if (!This
->ancestorStorage
)
847 return STG_E_REVERTED
;
852 if ((grfMode
& STGM_DELETEONRELEASE
) ||
853 (grfMode
& STGM_TRANSACTED
))
854 return STG_E_INVALIDFUNCTION
;
856 /* Can't create a stream on read-only storage */
857 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
858 return STG_E_ACCESSDENIED
;
861 * Check that we're compatible with the parent's storage mode
862 * if not in transacted mode
864 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
865 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
866 return STG_E_ACCESSDENIED
;
869 if(This
->openFlags
& STGM_SIMPLE
)
870 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
874 currentEntryRef
= findElement(This
->ancestorStorage
,
875 This
->storageDirEntry
,
879 if (currentEntryRef
!= DIRENTRY_NULL
)
882 * An element with this name already exists
884 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
886 IStorage_DestroyElement(iface
, pwcsName
);
889 return STG_E_FILEALREADYEXISTS
;
891 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
893 WARN("read-only storage\n");
894 return STG_E_ACCESSDENIED
;
898 * memset the empty entry
900 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
902 newStreamEntry
.sizeOfNameString
=
903 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
905 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
906 return STG_E_INVALIDNAME
;
908 strcpyW(newStreamEntry
.name
, pwcsName
);
910 newStreamEntry
.stgType
= STGTY_STREAM
;
911 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
912 newStreamEntry
.size
.u
.LowPart
= 0;
913 newStreamEntry
.size
.u
.HighPart
= 0;
915 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
916 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
917 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
919 /* call CoFileTime to get the current time
924 /* newStreamEntry.clsid */
927 * Create an entry with the new data
929 createDirEntry(This
->ancestorStorage
, &newStreamEntry
, &newStreamEntryRef
);
932 * Insert the new entry in the parent storage's tree.
935 This
->ancestorStorage
,
936 This
->storageDirEntry
,
940 * Open the stream to return it.
942 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
946 *ppstm
= (IStream
*)newStream
;
948 IStream_AddRef(*ppstm
);
952 return STG_E_INSUFFICIENTMEMORY
;
958 /************************************************************************
959 * Storage32BaseImpl_SetClass (IStorage)
961 * This method will write the specified CLSID in the directory entry of this
964 * See Windows documentation for more details on IStorage methods.
966 static HRESULT WINAPI
StorageBaseImpl_SetClass(
968 REFCLSID clsid
) /* [in] */
970 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
971 HRESULT hRes
= E_FAIL
;
972 DirEntry currentEntry
;
975 TRACE("(%p, %p)\n", iface
, clsid
);
977 if (!This
->ancestorStorage
)
978 return STG_E_REVERTED
;
980 success
= StorageImpl_ReadDirEntry(This
->ancestorStorage
,
981 This
->storageDirEntry
,
985 currentEntry
.clsid
= *clsid
;
987 success
= StorageImpl_WriteDirEntry(This
->ancestorStorage
,
988 This
->storageDirEntry
,
997 /************************************************************************
998 ** Storage32Impl implementation
1001 /************************************************************************
1002 * Storage32BaseImpl_CreateStorage (IStorage)
1004 * This method will create the storage object within the provided storage.
1006 * See Windows documentation for more details on IStorage methods.
1008 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1010 const OLECHAR
*pwcsName
, /* [string][in] */
1011 DWORD grfMode
, /* [in] */
1012 DWORD reserved1
, /* [in] */
1013 DWORD reserved2
, /* [in] */
1014 IStorage
**ppstg
) /* [out] */
1016 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1018 DirEntry currentEntry
;
1020 DirRef currentEntryRef
;
1024 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1025 iface
, debugstr_w(pwcsName
), grfMode
,
1026 reserved1
, reserved2
, ppstg
);
1029 return STG_E_INVALIDPOINTER
;
1031 if (This
->openFlags
& STGM_SIMPLE
)
1033 return STG_E_INVALIDFUNCTION
;
1037 return STG_E_INVALIDNAME
;
1041 if ( FAILED( validateSTGM(grfMode
) ) ||
1042 (grfMode
& STGM_DELETEONRELEASE
) )
1044 WARN("bad grfMode: 0x%x\n", grfMode
);
1045 return STG_E_INVALIDFLAG
;
1048 if (!This
->ancestorStorage
)
1049 return STG_E_REVERTED
;
1052 * Check that we're compatible with the parent's storage mode
1054 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1056 WARN("access denied\n");
1057 return STG_E_ACCESSDENIED
;
1060 currentEntryRef
= findElement(This
->ancestorStorage
,
1061 This
->storageDirEntry
,
1065 if (currentEntryRef
!= DIRENTRY_NULL
)
1068 * An element with this name already exists
1070 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1071 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
)
1073 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1079 WARN("file already exists\n");
1080 return STG_E_FILEALREADYEXISTS
;
1083 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1085 WARN("read-only storage\n");
1086 return STG_E_ACCESSDENIED
;
1089 memset(&newEntry
, 0, sizeof(DirEntry
));
1091 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1093 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1095 FIXME("name too long\n");
1096 return STG_E_INVALIDNAME
;
1099 strcpyW(newEntry
.name
, pwcsName
);
1101 newEntry
.stgType
= STGTY_STORAGE
;
1102 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1103 newEntry
.size
.u
.LowPart
= 0;
1104 newEntry
.size
.u
.HighPart
= 0;
1106 newEntry
.leftChild
= DIRENTRY_NULL
;
1107 newEntry
.rightChild
= DIRENTRY_NULL
;
1108 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1110 /* call CoFileTime to get the current time
1115 /* newEntry.clsid */
1118 * Create a new directory entry for the storage
1120 createDirEntry(This
->ancestorStorage
, &newEntry
, &newEntryRef
);
1123 * Insert the new directory entry into the parent storage's tree
1126 This
->ancestorStorage
,
1127 This
->storageDirEntry
,
1131 * Open it to get a pointer to return.
1133 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1135 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1145 /***************************************************************************
1149 * Reserve a directory entry in the file and initialize it.
1151 static HRESULT
createDirEntry(
1152 StorageImpl
*storage
,
1153 const DirEntry
*newData
,
1156 ULONG currentEntryIndex
= 0;
1157 ULONG newEntryIndex
= DIRENTRY_NULL
;
1159 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1160 WORD sizeOfNameString
;
1164 hr
= StorageImpl_ReadRawDirEntry(storage
,
1170 StorageUtl_ReadWord(
1172 OFFSET_PS_NAMELENGTH
,
1175 if (sizeOfNameString
== 0)
1178 * The entry exists and is available, we found it.
1180 newEntryIndex
= currentEntryIndex
;
1186 * We exhausted the directory entries, we will create more space below
1188 newEntryIndex
= currentEntryIndex
;
1190 currentEntryIndex
++;
1192 } while (newEntryIndex
== DIRENTRY_NULL
);
1195 * grow the directory stream
1199 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1200 ULARGE_INTEGER newSize
;
1202 ULONG lastEntry
= 0;
1203 ULONG blockCount
= 0;
1206 * obtain the new count of blocks in the directory stream
1208 blockCount
= BlockChainStream_GetCount(
1209 storage
->rootBlockChain
)+1;
1212 * initialize the size used by the directory stream
1214 newSize
.u
.HighPart
= 0;
1215 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1218 * add a block to the directory stream
1220 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1223 * memset the empty entry in order to initialize the unused newly
1226 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1231 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1234 entryIndex
= newEntryIndex
+ 1;
1235 entryIndex
< lastEntry
;
1238 StorageImpl_WriteRawDirEntry(
1245 UpdateRawDirEntry(currentData
, newData
);
1247 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1250 *index
= newEntryIndex
;
1255 /***************************************************************************
1259 * Mark a directory entry in the file as free.
1261 static HRESULT
destroyDirEntry(
1262 StorageImpl
*storage
,
1266 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1268 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1270 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1276 /****************************************************************************
1280 * Case insensitive comparison of DirEntry.name by first considering
1283 * Returns <0 when name1 < name2
1284 * >0 when name1 > name2
1285 * 0 when name1 == name2
1287 static LONG
entryNameCmp(
1288 const OLECHAR
*name1
,
1289 const OLECHAR
*name2
)
1291 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1296 * We compare the string themselves only when they are of the same length
1298 diff
= lstrcmpiW( name1
, name2
);
1304 /****************************************************************************
1308 * Add a directory entry to a storage
1310 static HRESULT
insertIntoTree(
1312 DirRef parentStorageIndex
,
1313 DirRef newEntryIndex
)
1315 DirEntry currentEntry
;
1319 * Read the inserted entry
1321 StorageImpl_ReadDirEntry(This
,
1326 * Read the storage entry
1328 StorageImpl_ReadDirEntry(This
,
1332 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1335 * The root storage contains some element, therefore, start the research
1336 * for the appropriate location.
1339 DirRef current
, next
, previous
, currentEntryId
;
1342 * Keep a reference to the root of the storage's element tree
1344 currentEntryId
= currentEntry
.dirRootEntry
;
1349 StorageImpl_ReadDirEntry(This
,
1350 currentEntry
.dirRootEntry
,
1353 previous
= currentEntry
.leftChild
;
1354 next
= currentEntry
.rightChild
;
1355 current
= currentEntryId
;
1359 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1363 if (previous
!= DIRENTRY_NULL
)
1365 StorageImpl_ReadDirEntry(This
,
1372 currentEntry
.leftChild
= newEntryIndex
;
1373 StorageImpl_WriteDirEntry(This
,
1381 if (next
!= DIRENTRY_NULL
)
1383 StorageImpl_ReadDirEntry(This
,
1390 currentEntry
.rightChild
= newEntryIndex
;
1391 StorageImpl_WriteDirEntry(This
,
1400 * Trying to insert an item with the same name in the
1401 * subtree structure.
1403 return STG_E_FILEALREADYEXISTS
;
1406 previous
= currentEntry
.leftChild
;
1407 next
= currentEntry
.rightChild
;
1413 * The storage is empty, make the new entry the root of its element tree
1415 currentEntry
.dirRootEntry
= newEntryIndex
;
1416 StorageImpl_WriteDirEntry(This
,
1424 /****************************************************************************
1428 * Find and read the element of a storage with the given name.
1430 static DirRef
findElement(StorageImpl
*storage
, DirRef storageEntry
,
1431 const OLECHAR
*name
, DirEntry
*data
)
1433 DirRef currentEntry
;
1435 /* Read the storage entry to find the root of the tree. */
1436 StorageImpl_ReadDirEntry(storage
, storageEntry
, data
);
1438 currentEntry
= data
->dirRootEntry
;
1440 while (currentEntry
!= DIRENTRY_NULL
)
1444 StorageImpl_ReadDirEntry(storage
, currentEntry
, data
);
1446 cmp
= entryNameCmp(name
, data
->name
);
1453 currentEntry
= data
->leftChild
;
1456 currentEntry
= data
->rightChild
;
1459 return currentEntry
;
1462 /****************************************************************************
1466 * Find and read the binary tree parent of the element with the given name.
1468 * If there is no such element, find a place where it could be inserted and
1469 * return STG_E_FILENOTFOUND.
1471 static HRESULT
findTreeParent(StorageImpl
*storage
, DirRef storageEntry
,
1472 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1478 /* Read the storage entry to find the root of the tree. */
1479 StorageImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1481 *parentEntry
= storageEntry
;
1482 *relation
= DIRENTRY_RELATION_DIR
;
1484 childEntry
= parentData
->dirRootEntry
;
1486 while (childEntry
!= DIRENTRY_NULL
)
1490 StorageImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1492 cmp
= entryNameCmp(childName
, childData
.name
);
1500 *parentData
= childData
;
1501 *parentEntry
= childEntry
;
1502 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1504 childEntry
= parentData
->leftChild
;
1509 *parentData
= childData
;
1510 *parentEntry
= childEntry
;
1511 *relation
= DIRENTRY_RELATION_NEXT
;
1513 childEntry
= parentData
->rightChild
;
1517 if (childEntry
== DIRENTRY_NULL
)
1518 return STG_E_FILENOTFOUND
;
1524 /*************************************************************************
1527 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1529 DWORD ciidExclude
, /* [in] */
1530 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1531 SNB snbExclude
, /* [unique][in] */
1532 IStorage
* pstgDest
) /* [unique][in] */
1534 IEnumSTATSTG
*elements
= 0;
1535 STATSTG curElement
, strStat
;
1537 IStorage
*pstgTmp
, *pstgChild
;
1538 IStream
*pstrTmp
, *pstrChild
;
1539 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1542 TRACE("(%p, %d, %p, %p, %p)\n",
1543 iface
, ciidExclude
, rgiidExclude
,
1544 snbExclude
, pstgDest
);
1546 if ( pstgDest
== 0 )
1547 return STG_E_INVALIDPOINTER
;
1550 * Enumerate the elements
1552 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1560 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1561 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1563 for(i
= 0; i
< ciidExclude
; ++i
)
1565 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1566 skip_storage
= TRUE
;
1567 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1570 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1576 * Obtain the next element
1578 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1580 if ( hr
== S_FALSE
)
1582 hr
= S_OK
; /* done, every element has been copied */
1588 WCHAR
**snb
= snbExclude
;
1590 while ( *snb
!= NULL
&& !skip
)
1592 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1601 if (curElement
.type
== STGTY_STORAGE
)
1607 * open child source storage
1609 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1610 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1611 NULL
, 0, &pstgChild
);
1617 * Check if destination storage is not a child of the source
1618 * storage, which will cause an infinite loop
1620 if (pstgChild
== pstgDest
)
1622 IEnumSTATSTG_Release(elements
);
1624 return STG_E_ACCESSDENIED
;
1628 * create a new storage in destination storage
1630 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1631 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1635 * if it already exist, don't create a new one use this one
1637 if (hr
== STG_E_FILEALREADYEXISTS
)
1639 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1640 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1641 NULL
, 0, &pstgTmp
);
1649 * do the copy recursively
1651 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1654 IStorage_Release( pstgTmp
);
1655 IStorage_Release( pstgChild
);
1657 else if (curElement
.type
== STGTY_STREAM
)
1663 * create a new stream in destination storage. If the stream already
1664 * exist, it will be deleted and a new one will be created.
1666 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1667 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1674 * open child stream storage
1676 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1677 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1684 * Get the size of the source stream
1686 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1689 * Set the size of the destination stream.
1691 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1696 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1699 IStream_Release( pstrTmp
);
1700 IStream_Release( pstrChild
);
1704 WARN("unknown element type: %d\n", curElement
.type
);
1707 } while (hr
== S_OK
);
1712 IEnumSTATSTG_Release(elements
);
1717 /*************************************************************************
1718 * MoveElementTo (IStorage)
1720 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1722 const OLECHAR
*pwcsName
, /* [string][in] */
1723 IStorage
*pstgDest
, /* [unique][in] */
1724 const OLECHAR
*pwcsNewName
,/* [string][in] */
1725 DWORD grfFlags
) /* [in] */
1727 FIXME("(%p %s %p %s %u): stub\n", iface
,
1728 debugstr_w(pwcsName
), pstgDest
,
1729 debugstr_w(pwcsNewName
), grfFlags
);
1733 /*************************************************************************
1736 * Ensures that any changes made to a storage object open in transacted mode
1737 * are reflected in the parent storage
1740 * Wine doesn't implement transacted mode, which seems to be a basic
1741 * optimization, so we can ignore this stub for now.
1743 static HRESULT WINAPI
StorageImpl_Commit(
1745 DWORD grfCommitFlags
)/* [in] */
1747 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1751 /*************************************************************************
1754 * Discard all changes that have been made since the last commit operation
1756 static HRESULT WINAPI
StorageImpl_Revert(
1759 FIXME("(%p): stub\n", iface
);
1763 /*************************************************************************
1764 * DestroyElement (IStorage)
1766 * Strategy: This implementation is built this way for simplicity not for speed.
1767 * I always delete the topmost element of the enumeration and adjust
1768 * the deleted element pointer all the time. This takes longer to
1769 * do but allow to reinvoke DestroyElement whenever we encounter a
1770 * storage object. The optimisation resides in the usage of another
1771 * enumeration strategy that would give all the leaves of a storage
1772 * first. (postfix order)
1774 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1776 const OLECHAR
*pwcsName
)/* [string][in] */
1778 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1781 DirEntry entryToDelete
;
1782 DirRef entryToDeleteRef
;
1785 iface
, debugstr_w(pwcsName
));
1788 return STG_E_INVALIDPOINTER
;
1790 if (!This
->ancestorStorage
)
1791 return STG_E_REVERTED
;
1793 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1794 return STG_E_ACCESSDENIED
;
1796 entryToDeleteRef
= findElement(
1797 This
->ancestorStorage
,
1798 This
->storageDirEntry
,
1802 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1804 return STG_E_FILENOTFOUND
;
1807 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1809 hr
= deleteStorageContents(
1814 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1816 hr
= deleteStreamContents(
1826 * Remove the entry from its parent storage
1828 hr
= removeFromTree(
1829 This
->ancestorStorage
,
1830 This
->storageDirEntry
,
1834 * Invalidate the entry
1837 destroyDirEntry(This
->ancestorStorage
,
1844 /******************************************************************************
1845 * Internal stream list handlers
1848 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1850 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1851 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1854 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1856 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1857 list_remove(&(strm
->StrmListEntry
));
1860 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1862 StgStreamImpl
*strm
;
1864 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1866 if (strm
->dirEntry
== streamEntry
)
1875 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1877 StorageInternalImpl
*childstg
;
1879 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1881 if (childstg
->base
.storageDirEntry
== storageEntry
)
1890 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1892 struct list
*cur
, *cur2
;
1893 StgStreamImpl
*strm
=NULL
;
1894 StorageInternalImpl
*childstg
=NULL
;
1896 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1897 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1898 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1899 strm
->parentStorage
= NULL
;
1903 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1904 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1905 StorageInternalImpl_Invalidate( childstg
);
1910 /*********************************************************************
1914 * Delete the contents of a storage entry.
1917 static HRESULT
deleteStorageContents(
1918 StorageBaseImpl
*parentStorage
,
1919 DirRef indexToDelete
,
1920 DirEntry entryDataToDelete
)
1922 IEnumSTATSTG
*elements
= 0;
1923 IStorage
*childStorage
= 0;
1924 STATSTG currentElement
;
1926 HRESULT destroyHr
= S_OK
;
1927 StorageInternalImpl
*stg
, *stg2
;
1929 /* Invalidate any open storage objects. */
1930 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1932 if (stg
->base
.storageDirEntry
== indexToDelete
)
1934 StorageInternalImpl_Invalidate(stg
);
1939 * Open the storage and enumerate it
1941 hr
= StorageBaseImpl_OpenStorage(
1942 (IStorage
*)parentStorage
,
1943 entryDataToDelete
.name
,
1945 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1956 * Enumerate the elements
1958 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1963 * Obtain the next element
1965 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1968 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
1970 CoTaskMemFree(currentElement
.pwcsName
);
1974 * We need to Reset the enumeration every time because we delete elements
1975 * and the enumeration could be invalid
1977 IEnumSTATSTG_Reset(elements
);
1979 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1981 IStorage_Release(childStorage
);
1982 IEnumSTATSTG_Release(elements
);
1987 /*********************************************************************
1991 * Perform the deletion of a stream's data
1994 static HRESULT
deleteStreamContents(
1995 StorageBaseImpl
*parentStorage
,
1996 DirRef indexToDelete
,
1997 DirEntry entryDataToDelete
)
2001 ULARGE_INTEGER size
;
2002 StgStreamImpl
*strm
, *strm2
;
2004 /* Invalidate any open stream objects. */
2005 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2007 if (strm
->dirEntry
== indexToDelete
)
2009 TRACE("Stream deleted %p\n", strm
);
2010 strm
->parentStorage
= NULL
;
2011 list_remove(&strm
->StrmListEntry
);
2015 size
.u
.HighPart
= 0;
2018 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2019 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2029 hr
= IStream_SetSize(pis
, size
);
2037 * Release the stream object.
2039 IStream_Release(pis
);
2044 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2048 case DIRENTRY_RELATION_PREVIOUS
:
2049 entry
->leftChild
= new_target
;
2051 case DIRENTRY_RELATION_NEXT
:
2052 entry
->rightChild
= new_target
;
2054 case DIRENTRY_RELATION_DIR
:
2055 entry
->dirRootEntry
= new_target
;
2062 /*************************************************************************
2066 * This method removes a directory entry from its parent storage tree without
2067 * freeing any resources attached to it.
2069 static HRESULT
removeFromTree(
2071 DirRef parentStorageIndex
,
2072 DirRef deletedIndex
)
2076 DirEntry entryToDelete
;
2077 DirEntry parentEntry
;
2078 DirRef parentEntryRef
;
2079 ULONG typeOfRelation
;
2081 res
= StorageImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2084 * Find the element that links to the one we want to delete.
2086 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2087 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2092 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2095 * Replace the deleted entry with its left child
2097 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2099 res
= StorageImpl_WriteDirEntry(
2108 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2111 * We need to reinsert the right child somewhere. We already know it and
2112 * its children are greater than everything in the left tree, so we
2113 * insert it at the rightmost point in the left tree.
2115 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2116 DirEntry newRightChildParentEntry
;
2120 res
= StorageImpl_ReadDirEntry(
2122 newRightChildParent
,
2123 &newRightChildParentEntry
);
2129 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2130 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2131 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2133 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2135 res
= StorageImpl_WriteDirEntry(
2137 newRightChildParent
,
2138 &newRightChildParentEntry
);
2148 * Replace the deleted entry with its right child
2150 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2152 res
= StorageImpl_WriteDirEntry(
2166 /******************************************************************************
2167 * SetElementTimes (IStorage)
2169 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2171 const OLECHAR
*pwcsName
,/* [string][in] */
2172 const FILETIME
*pctime
, /* [in] */
2173 const FILETIME
*patime
, /* [in] */
2174 const FILETIME
*pmtime
) /* [in] */
2176 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2180 /******************************************************************************
2181 * SetStateBits (IStorage)
2183 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2185 DWORD grfStateBits
,/* [in] */
2186 DWORD grfMask
) /* [in] */
2188 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2190 if (!This
->ancestorStorage
)
2191 return STG_E_REVERTED
;
2193 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2198 * Virtual function table for the IStorage32Impl class.
2200 static const IStorageVtbl Storage32Impl_Vtbl
=
2202 StorageBaseImpl_QueryInterface
,
2203 StorageBaseImpl_AddRef
,
2204 StorageBaseImpl_Release
,
2205 StorageBaseImpl_CreateStream
,
2206 StorageBaseImpl_OpenStream
,
2207 StorageBaseImpl_CreateStorage
,
2208 StorageBaseImpl_OpenStorage
,
2209 StorageBaseImpl_CopyTo
,
2210 StorageBaseImpl_MoveElementTo
,
2213 StorageBaseImpl_EnumElements
,
2214 StorageBaseImpl_DestroyElement
,
2215 StorageBaseImpl_RenameElement
,
2216 StorageBaseImpl_SetElementTimes
,
2217 StorageBaseImpl_SetClass
,
2218 StorageBaseImpl_SetStateBits
,
2219 StorageBaseImpl_Stat
2222 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2227 static HRESULT
StorageImpl_Construct(
2234 StorageImpl
** result
)
2238 DirEntry currentEntry
;
2239 BOOL readSuccessful
;
2240 DirRef currentEntryRef
;
2242 if ( FAILED( validateSTGM(openFlags
) ))
2243 return STG_E_INVALIDFLAG
;
2245 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2247 return E_OUTOFMEMORY
;
2249 memset(This
, 0, sizeof(StorageImpl
));
2251 list_init(&This
->base
.strmHead
);
2253 list_init(&This
->base
.storageHead
);
2255 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2256 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2257 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2258 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2260 This
->base
.create
= create
;
2263 * This is the top-level storage so initialize the ancestor pointer
2266 This
->base
.ancestorStorage
= This
;
2268 This
->hFile
= hFile
;
2271 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2272 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2273 if (!This
->pwcsName
)
2275 hr
= STG_E_INSUFFICIENTMEMORY
;
2278 strcpyW(This
->pwcsName
, pwcsName
);
2280 memcpy(This
->base
.filename
, pwcsName
, DIRENTRY_NAME_BUFFER_LEN
-1);
2281 This
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = 0;
2285 * Initialize the big block cache.
2287 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2288 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2289 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2295 if (This
->bigBlockFile
== 0)
2303 ULARGE_INTEGER size
;
2304 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2307 * Initialize all header variables:
2308 * - The big block depot consists of one block and it is at block 0
2309 * - The directory table starts at block 1
2310 * - There is no small block depot
2312 memset( This
->bigBlockDepotStart
,
2314 sizeof(This
->bigBlockDepotStart
));
2316 This
->bigBlockDepotCount
= 1;
2317 This
->bigBlockDepotStart
[0] = 0;
2318 This
->rootStartBlock
= 1;
2319 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2320 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2321 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2322 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2323 This
->extBigBlockDepotCount
= 0;
2325 StorageImpl_SaveFileHeader(This
);
2328 * Add one block for the big block depot and one block for the directory table
2330 size
.u
.HighPart
= 0;
2331 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2332 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2335 * Initialize the big block depot
2337 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2338 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2339 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2340 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2345 * Load the header for the file.
2347 hr
= StorageImpl_LoadFileHeader(This
);
2356 * There is no block depot cached yet.
2358 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2361 * Start searching for free blocks with block 0.
2363 This
->prevFreeBlock
= 0;
2366 * Create the block chain abstractions.
2368 if(!(This
->rootBlockChain
=
2369 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2371 hr
= STG_E_READFAULT
;
2375 if(!(This
->smallBlockDepotChain
=
2376 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2379 hr
= STG_E_READFAULT
;
2384 * Write the root storage entry (memory only)
2390 * Initialize the directory table
2392 memset(&rootEntry
, 0, sizeof(rootEntry
));
2393 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2394 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2395 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2396 rootEntry
.stgType
= STGTY_ROOT
;
2397 rootEntry
.leftChild
= DIRENTRY_NULL
;
2398 rootEntry
.rightChild
= DIRENTRY_NULL
;
2399 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2400 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2401 rootEntry
.size
.u
.HighPart
= 0;
2402 rootEntry
.size
.u
.LowPart
= 0;
2404 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2408 * Find the ID of the root storage.
2410 currentEntryRef
= 0;
2414 readSuccessful
= StorageImpl_ReadDirEntry(
2421 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2422 (currentEntry
.stgType
== STGTY_ROOT
) )
2424 This
->base
.storageDirEntry
= currentEntryRef
;
2430 } while (readSuccessful
&& (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2432 if (!readSuccessful
)
2434 hr
= STG_E_READFAULT
;
2439 * Create the block chain abstraction for the small block root chain.
2441 if(!(This
->smallBlockRootChain
=
2442 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2444 hr
= STG_E_READFAULT
;
2450 IStorage_Release((IStorage
*)This
);
2459 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2461 StorageImpl
*This
= (StorageImpl
*) iface
;
2462 TRACE("(%p)\n", This
);
2464 StorageBaseImpl_DeleteAll(&This
->base
);
2466 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2468 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2469 BlockChainStream_Destroy(This
->rootBlockChain
);
2470 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2472 if (This
->bigBlockFile
)
2473 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2474 HeapFree(GetProcessHeap(), 0, This
);
2477 /******************************************************************************
2478 * Storage32Impl_GetNextFreeBigBlock
2480 * Returns the index of the next free big block.
2481 * If the big block depot is filled, this method will enlarge it.
2484 static ULONG
StorageImpl_GetNextFreeBigBlock(
2487 ULONG depotBlockIndexPos
;
2488 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2490 ULONG depotBlockOffset
;
2491 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2492 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2494 ULONG freeBlock
= BLOCK_UNUSED
;
2496 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2497 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2500 * Scan the entire big block depot until we find a block marked free
2502 while (nextBlockIndex
!= BLOCK_UNUSED
)
2504 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2506 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2509 * Grow the primary depot.
2511 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2513 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2516 * Add a block depot.
2518 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2519 This
->bigBlockDepotCount
++;
2520 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2523 * Flag it as a block depot.
2525 StorageImpl_SetNextBlockInChain(This
,
2529 /* Save new header information.
2531 StorageImpl_SaveFileHeader(This
);
2536 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2538 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2541 * Grow the extended depot.
2543 ULONG extIndex
= BLOCK_UNUSED
;
2544 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2545 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2547 if (extBlockOffset
== 0)
2549 /* We need an extended block.
2551 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2552 This
->extBigBlockDepotCount
++;
2553 depotBlockIndexPos
= extIndex
+ 1;
2556 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2559 * Add a block depot and mark it in the extended block.
2561 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2562 This
->bigBlockDepotCount
++;
2563 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2565 /* Flag the block depot.
2567 StorageImpl_SetNextBlockInChain(This
,
2571 /* If necessary, flag the extended depot block.
2573 if (extIndex
!= BLOCK_UNUSED
)
2574 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2576 /* Save header information.
2578 StorageImpl_SaveFileHeader(This
);
2582 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2586 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2587 ( nextBlockIndex
!= BLOCK_UNUSED
))
2589 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2591 if (nextBlockIndex
== BLOCK_UNUSED
)
2593 freeBlock
= (depotIndex
* blocksPerDepot
) +
2594 (depotBlockOffset
/sizeof(ULONG
));
2597 depotBlockOffset
+= sizeof(ULONG
);
2602 depotBlockOffset
= 0;
2606 * make sure that the block physically exists before using it
2608 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2610 This
->prevFreeBlock
= freeBlock
;
2615 /******************************************************************************
2616 * Storage32Impl_AddBlockDepot
2618 * This will create a depot block, essentially it is a block initialized
2621 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2623 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2626 * Initialize blocks as free
2628 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2629 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2632 /******************************************************************************
2633 * Storage32Impl_GetExtDepotBlock
2635 * Returns the index of the block that corresponds to the specified depot
2636 * index. This method is only for depot indexes equal or greater than
2637 * COUNT_BBDEPOTINHEADER.
2639 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2641 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2642 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2643 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2644 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2645 ULONG blockIndex
= BLOCK_UNUSED
;
2646 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2648 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2650 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2651 return BLOCK_UNUSED
;
2653 while (extBlockCount
> 0)
2655 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2659 if (extBlockIndex
!= BLOCK_UNUSED
)
2660 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2661 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2666 /******************************************************************************
2667 * Storage32Impl_SetExtDepotBlock
2669 * Associates the specified block index to the specified depot index.
2670 * This method is only for depot indexes equal or greater than
2671 * COUNT_BBDEPOTINHEADER.
2673 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2675 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2676 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2677 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2678 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2679 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2681 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2683 while (extBlockCount
> 0)
2685 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2689 if (extBlockIndex
!= BLOCK_UNUSED
)
2691 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2692 extBlockOffset
* sizeof(ULONG
),
2697 /******************************************************************************
2698 * Storage32Impl_AddExtBlockDepot
2700 * Creates an extended depot block.
2702 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2704 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2705 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2706 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2707 ULONG index
= BLOCK_UNUSED
;
2708 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2709 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2710 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2712 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2713 blocksPerDepotBlock
;
2715 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2718 * The first extended block.
2720 This
->extBigBlockDepotStart
= index
;
2726 * Follow the chain to the last one.
2728 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2730 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2734 * Add the new extended block to the chain.
2736 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2741 * Initialize this block.
2743 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2744 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2749 /******************************************************************************
2750 * Storage32Impl_FreeBigBlock
2752 * This method will flag the specified block as free in the big block depot.
2754 static void StorageImpl_FreeBigBlock(
2758 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2760 if (blockIndex
< This
->prevFreeBlock
)
2761 This
->prevFreeBlock
= blockIndex
;
2764 /************************************************************************
2765 * Storage32Impl_GetNextBlockInChain
2767 * This method will retrieve the block index of the next big block in
2770 * Params: This - Pointer to the Storage object.
2771 * blockIndex - Index of the block to retrieve the chain
2773 * nextBlockIndex - receives the return value.
2775 * Returns: This method returns the index of the next block in the chain.
2776 * It will return the constants:
2777 * BLOCK_SPECIAL - If the block given was not part of a
2779 * BLOCK_END_OF_CHAIN - If the block given was the last in
2781 * BLOCK_UNUSED - If the block given was not past of a chain
2783 * BLOCK_EXTBBDEPOT - This block is part of the extended
2786 * See Windows documentation for more details on IStorage methods.
2788 static HRESULT
StorageImpl_GetNextBlockInChain(
2791 ULONG
* nextBlockIndex
)
2793 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2794 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2795 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2796 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2798 ULONG depotBlockIndexPos
;
2801 *nextBlockIndex
= BLOCK_SPECIAL
;
2803 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2805 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2806 This
->bigBlockDepotCount
);
2807 return STG_E_READFAULT
;
2811 * Cache the currently accessed depot block.
2813 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2815 This
->indexBlockDepotCached
= depotBlockCount
;
2817 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2819 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2824 * We have to look in the extended depot.
2826 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2829 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2832 return STG_E_READFAULT
;
2834 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2836 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2837 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2841 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2846 /******************************************************************************
2847 * Storage32Impl_GetNextExtendedBlock
2849 * Given an extended block this method will return the next extended block.
2852 * The last ULONG of an extended block is the block index of the next
2853 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2857 * - The index of the next extended block
2858 * - BLOCK_UNUSED: there is no next extended block.
2859 * - Any other return values denotes failure.
2861 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2863 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2864 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2866 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
2869 return nextBlockIndex
;
2872 /******************************************************************************
2873 * Storage32Impl_SetNextBlockInChain
2875 * This method will write the index of the specified block's next block
2876 * in the big block depot.
2878 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2881 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2882 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2883 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2886 static void StorageImpl_SetNextBlockInChain(
2891 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2892 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2893 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2894 ULONG depotBlockIndexPos
;
2896 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2897 assert(blockIndex
!= nextBlock
);
2899 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2901 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2906 * We have to look in the extended depot.
2908 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2911 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
2914 * Update the cached block depot, if necessary.
2916 if (depotBlockCount
== This
->indexBlockDepotCached
)
2918 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2922 /******************************************************************************
2923 * Storage32Impl_LoadFileHeader
2925 * This method will read in the file header, i.e. big block index -1.
2927 static HRESULT
StorageImpl_LoadFileHeader(
2930 HRESULT hr
= STG_E_FILENOTFOUND
;
2931 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2937 * Get a pointer to the big block of data containing the header.
2939 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2942 * Extract the information from the header.
2947 * Check for the "magic number" signature and return an error if it is not
2950 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2952 return STG_E_OLDFORMAT
;
2955 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2957 return STG_E_INVALIDHEADER
;
2960 StorageUtl_ReadWord(
2962 OFFSET_BIGBLOCKSIZEBITS
,
2963 &This
->bigBlockSizeBits
);
2965 StorageUtl_ReadWord(
2967 OFFSET_SMALLBLOCKSIZEBITS
,
2968 &This
->smallBlockSizeBits
);
2970 StorageUtl_ReadDWord(
2972 OFFSET_BBDEPOTCOUNT
,
2973 &This
->bigBlockDepotCount
);
2975 StorageUtl_ReadDWord(
2977 OFFSET_ROOTSTARTBLOCK
,
2978 &This
->rootStartBlock
);
2980 StorageUtl_ReadDWord(
2982 OFFSET_SBDEPOTSTART
,
2983 &This
->smallBlockDepotStart
);
2985 StorageUtl_ReadDWord(
2987 OFFSET_EXTBBDEPOTSTART
,
2988 &This
->extBigBlockDepotStart
);
2990 StorageUtl_ReadDWord(
2992 OFFSET_EXTBBDEPOTCOUNT
,
2993 &This
->extBigBlockDepotCount
);
2995 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2997 StorageUtl_ReadDWord(
2999 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3000 &(This
->bigBlockDepotStart
[index
]));
3004 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3006 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3007 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3010 * Right now, the code is making some assumptions about the size of the
3011 * blocks, just make sure they are what we're expecting.
3013 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3014 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3016 WARN("Broken OLE storage file\n");
3017 hr
= STG_E_INVALIDHEADER
;
3026 /******************************************************************************
3027 * Storage32Impl_SaveFileHeader
3029 * This method will save to the file the header, i.e. big block -1.
3031 static void StorageImpl_SaveFileHeader(
3034 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3039 * Get a pointer to the big block of data containing the header.
3041 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3044 * If the block read failed, the file is probably new.
3049 * Initialize for all unknown fields.
3051 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3054 * Initialize the magic number.
3056 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3059 * And a bunch of things we don't know what they mean
3061 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3062 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3063 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3064 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3068 * Write the information to the header.
3070 StorageUtl_WriteWord(
3072 OFFSET_BIGBLOCKSIZEBITS
,
3073 This
->bigBlockSizeBits
);
3075 StorageUtl_WriteWord(
3077 OFFSET_SMALLBLOCKSIZEBITS
,
3078 This
->smallBlockSizeBits
);
3080 StorageUtl_WriteDWord(
3082 OFFSET_BBDEPOTCOUNT
,
3083 This
->bigBlockDepotCount
);
3085 StorageUtl_WriteDWord(
3087 OFFSET_ROOTSTARTBLOCK
,
3088 This
->rootStartBlock
);
3090 StorageUtl_WriteDWord(
3092 OFFSET_SBDEPOTSTART
,
3093 This
->smallBlockDepotStart
);
3095 StorageUtl_WriteDWord(
3097 OFFSET_SBDEPOTCOUNT
,
3098 This
->smallBlockDepotChain
?
3099 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3101 StorageUtl_WriteDWord(
3103 OFFSET_EXTBBDEPOTSTART
,
3104 This
->extBigBlockDepotStart
);
3106 StorageUtl_WriteDWord(
3108 OFFSET_EXTBBDEPOTCOUNT
,
3109 This
->extBigBlockDepotCount
);
3111 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3113 StorageUtl_WriteDWord(
3115 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3116 (This
->bigBlockDepotStart
[index
]));
3120 * Write the big block back to the file.
3122 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3125 /******************************************************************************
3126 * StorageImpl_ReadRawDirEntry
3128 * This method will read the raw data from a directory entry in the file.
3130 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3132 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3134 ULARGE_INTEGER offset
;
3138 offset
.u
.HighPart
= 0;
3139 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3141 hr
= BlockChainStream_ReadAt(
3142 This
->rootBlockChain
,
3151 /******************************************************************************
3152 * StorageImpl_WriteRawDirEntry
3154 * This method will write the raw data from a directory entry in the file.
3156 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3158 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3160 ULARGE_INTEGER offset
;
3164 offset
.u
.HighPart
= 0;
3165 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3167 hr
= BlockChainStream_WriteAt(
3168 This
->rootBlockChain
,
3177 /******************************************************************************
3180 * Update raw directory entry data from the fields in newData.
3182 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3184 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3186 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3189 buffer
+ OFFSET_PS_NAME
,
3191 DIRENTRY_NAME_BUFFER_LEN
);
3193 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3195 StorageUtl_WriteWord(
3197 OFFSET_PS_NAMELENGTH
,
3198 newData
->sizeOfNameString
);
3200 StorageUtl_WriteDWord(
3202 OFFSET_PS_LEFTCHILD
,
3203 newData
->leftChild
);
3205 StorageUtl_WriteDWord(
3207 OFFSET_PS_RIGHTCHILD
,
3208 newData
->rightChild
);
3210 StorageUtl_WriteDWord(
3213 newData
->dirRootEntry
);
3215 StorageUtl_WriteGUID(
3220 StorageUtl_WriteDWord(
3223 newData
->ctime
.dwLowDateTime
);
3225 StorageUtl_WriteDWord(
3227 OFFSET_PS_CTIMEHIGH
,
3228 newData
->ctime
.dwHighDateTime
);
3230 StorageUtl_WriteDWord(
3233 newData
->mtime
.dwLowDateTime
);
3235 StorageUtl_WriteDWord(
3237 OFFSET_PS_MTIMEHIGH
,
3238 newData
->ctime
.dwHighDateTime
);
3240 StorageUtl_WriteDWord(
3242 OFFSET_PS_STARTBLOCK
,
3243 newData
->startingBlock
);
3245 StorageUtl_WriteDWord(
3248 newData
->size
.u
.LowPart
);
3251 /******************************************************************************
3252 * Storage32Impl_ReadDirEntry
3254 * This method will read the specified directory entry.
3256 BOOL
StorageImpl_ReadDirEntry(
3261 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3264 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3266 if (SUCCEEDED(readRes
))
3268 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3271 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3272 DIRENTRY_NAME_BUFFER_LEN
);
3273 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3275 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3277 StorageUtl_ReadWord(
3279 OFFSET_PS_NAMELENGTH
,
3280 &buffer
->sizeOfNameString
);
3282 StorageUtl_ReadDWord(
3284 OFFSET_PS_LEFTCHILD
,
3285 &buffer
->leftChild
);
3287 StorageUtl_ReadDWord(
3289 OFFSET_PS_RIGHTCHILD
,
3290 &buffer
->rightChild
);
3292 StorageUtl_ReadDWord(
3295 &buffer
->dirRootEntry
);
3297 StorageUtl_ReadGUID(
3302 StorageUtl_ReadDWord(
3305 &buffer
->ctime
.dwLowDateTime
);
3307 StorageUtl_ReadDWord(
3309 OFFSET_PS_CTIMEHIGH
,
3310 &buffer
->ctime
.dwHighDateTime
);
3312 StorageUtl_ReadDWord(
3315 &buffer
->mtime
.dwLowDateTime
);
3317 StorageUtl_ReadDWord(
3319 OFFSET_PS_MTIMEHIGH
,
3320 &buffer
->mtime
.dwHighDateTime
);
3322 StorageUtl_ReadDWord(
3324 OFFSET_PS_STARTBLOCK
,
3325 &buffer
->startingBlock
);
3327 StorageUtl_ReadDWord(
3330 &buffer
->size
.u
.LowPart
);
3332 buffer
->size
.u
.HighPart
= 0;
3335 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3338 /*********************************************************************
3339 * Write the specified directory entry to the file
3341 BOOL
StorageImpl_WriteDirEntry(
3344 const DirEntry
* buffer
)
3346 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3349 UpdateRawDirEntry(currentEntry
, buffer
);
3351 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3352 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3355 static BOOL
StorageImpl_ReadBigBlock(
3360 ULARGE_INTEGER ulOffset
;
3363 ulOffset
.u
.HighPart
= 0;
3364 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3366 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3367 return (read
== This
->bigBlockSize
);
3370 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3376 ULARGE_INTEGER ulOffset
;
3380 ulOffset
.u
.HighPart
= 0;
3381 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3382 ulOffset
.u
.LowPart
+= offset
;
3384 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3385 *value
= lendian32toh(tmp
);
3386 return (read
== sizeof(DWORD
));
3389 static BOOL
StorageImpl_WriteBigBlock(
3394 ULARGE_INTEGER ulOffset
;
3397 ulOffset
.u
.HighPart
= 0;
3398 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3400 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3401 return (wrote
== This
->bigBlockSize
);
3404 static BOOL
StorageImpl_WriteDWordToBigBlock(
3410 ULARGE_INTEGER ulOffset
;
3413 ulOffset
.u
.HighPart
= 0;
3414 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3415 ulOffset
.u
.LowPart
+= offset
;
3417 value
= htole32(value
);
3418 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3419 return (wrote
== sizeof(DWORD
));
3422 /******************************************************************************
3423 * Storage32Impl_SmallBlocksToBigBlocks
3425 * This method will convert a small block chain to a big block chain.
3426 * The small block chain will be destroyed.
3428 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3430 SmallBlockChainStream
** ppsbChain
)
3432 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3433 ULARGE_INTEGER size
, offset
;
3434 ULONG cbRead
, cbWritten
;
3435 ULARGE_INTEGER cbTotalRead
;
3436 DirRef streamEntryRef
;
3437 HRESULT resWrite
= S_OK
;
3439 DirEntry streamEntry
;
3441 BlockChainStream
*bbTempChain
= NULL
;
3442 BlockChainStream
*bigBlockChain
= NULL
;
3445 * Create a temporary big block chain that doesn't have
3446 * an associated directory entry. This temporary chain will be
3447 * used to copy data from small blocks to big blocks.
3449 bbTempChain
= BlockChainStream_Construct(This
,
3452 if(!bbTempChain
) return NULL
;
3454 * Grow the big block chain.
3456 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3457 BlockChainStream_SetSize(bbTempChain
, size
);
3460 * Copy the contents of the small block chain to the big block chain
3461 * by small block size increments.
3463 offset
.u
.LowPart
= 0;
3464 offset
.u
.HighPart
= 0;
3465 cbTotalRead
.QuadPart
= 0;
3467 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3470 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3472 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3475 if (FAILED(resRead
))
3480 cbTotalRead
.QuadPart
+= cbRead
;
3482 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3488 if (FAILED(resWrite
))
3491 offset
.u
.LowPart
+= cbRead
;
3493 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3494 HeapFree(GetProcessHeap(),0,buffer
);
3496 size
.u
.HighPart
= 0;
3499 if (FAILED(resRead
) || FAILED(resWrite
))
3501 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3502 BlockChainStream_SetSize(bbTempChain
, size
);
3503 BlockChainStream_Destroy(bbTempChain
);
3508 * Destroy the small block chain.
3510 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3511 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3512 SmallBlockChainStream_Destroy(*ppsbChain
);
3516 * Change the directory entry. This chain is now a big block chain
3517 * and it doesn't reside in the small blocks chain anymore.
3519 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3521 streamEntry
.startingBlock
= bbHeadOfChain
;
3523 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3526 * Destroy the temporary entryless big block chain.
3527 * Create a new big block chain associated with this entry.
3529 BlockChainStream_Destroy(bbTempChain
);
3530 bigBlockChain
= BlockChainStream_Construct(This
,
3534 return bigBlockChain
;
3537 /******************************************************************************
3538 * Storage32Impl_BigBlocksToSmallBlocks
3540 * This method will convert a big block chain to a small block chain.
3541 * The big block chain will be destroyed on success.
3543 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3545 BlockChainStream
** ppbbChain
)
3547 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3548 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3549 DirRef streamEntryRef
;
3550 HRESULT resWrite
= S_OK
, resRead
;
3551 DirEntry streamEntry
;
3553 SmallBlockChainStream
* sbTempChain
;
3555 TRACE("%p %p\n", This
, ppbbChain
);
3557 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3563 size
= BlockChainStream_GetSize(*ppbbChain
);
3564 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3566 offset
.u
.HighPart
= 0;
3567 offset
.u
.LowPart
= 0;
3568 cbTotalRead
.QuadPart
= 0;
3569 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3572 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3573 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3581 cbTotalRead
.QuadPart
+= cbRead
;
3583 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3584 cbRead
, buffer
, &cbWritten
);
3586 if(FAILED(resWrite
))
3589 offset
.u
.LowPart
+= cbRead
;
3591 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3592 HeapFree(GetProcessHeap(), 0, buffer
);
3594 size
.u
.HighPart
= 0;
3597 if(FAILED(resRead
) || FAILED(resWrite
))
3599 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3600 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3601 SmallBlockChainStream_Destroy(sbTempChain
);
3605 /* destroy the original big block chain */
3606 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3607 BlockChainStream_SetSize(*ppbbChain
, size
);
3608 BlockChainStream_Destroy(*ppbbChain
);
3611 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3612 streamEntry
.startingBlock
= sbHeadOfChain
;
3613 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3615 SmallBlockChainStream_Destroy(sbTempChain
);
3616 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3619 static void StorageInternalImpl_Invalidate( StorageInternalImpl
*This
)
3621 if (This
->base
.ancestorStorage
)
3623 TRACE("Storage invalidated (stg=%p)\n", This
);
3625 This
->base
.ancestorStorage
= NULL
;
3627 StorageBaseImpl_DeleteAll(&This
->base
);
3629 list_remove(&This
->ParentListEntry
);
3633 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3635 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3637 StorageInternalImpl_Invalidate(This
);
3639 HeapFree(GetProcessHeap(), 0, This
);
3642 /******************************************************************************
3644 ** Storage32InternalImpl_Commit
3647 static HRESULT WINAPI
StorageInternalImpl_Commit(
3649 DWORD grfCommitFlags
) /* [in] */
3651 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
3655 /******************************************************************************
3657 ** Storage32InternalImpl_Revert
3660 static HRESULT WINAPI
StorageInternalImpl_Revert(
3663 FIXME("(%p): stub\n", iface
);
3667 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3669 IStorage_Release((IStorage
*)This
->parentStorage
);
3670 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3671 HeapFree(GetProcessHeap(), 0, This
);
3674 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3675 IEnumSTATSTG
* iface
,
3679 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3682 return E_INVALIDARG
;
3686 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3687 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3690 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3694 return E_NOINTERFACE
;
3697 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3698 IEnumSTATSTG
* iface
)
3700 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3701 return InterlockedIncrement(&This
->ref
);
3704 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3705 IEnumSTATSTG
* iface
)
3707 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3711 newRef
= InterlockedDecrement(&This
->ref
);
3715 IEnumSTATSTGImpl_Destroy(This
);
3721 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3722 IEnumSTATSTG
* iface
,
3725 ULONG
* pceltFetched
)
3727 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3729 DirEntry currentEntry
;
3730 STATSTG
* currentReturnStruct
= rgelt
;
3731 ULONG objectFetched
= 0;
3732 DirRef currentSearchNode
;
3734 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3735 return E_INVALIDARG
;
3738 * To avoid the special case, get another pointer to a ULONG value if
3739 * the caller didn't supply one.
3741 if (pceltFetched
==0)
3742 pceltFetched
= &objectFetched
;
3745 * Start the iteration, we will iterate until we hit the end of the
3746 * linked list or until we hit the number of items to iterate through
3751 * Start with the node at the top of the stack.
3753 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3755 while ( ( *pceltFetched
< celt
) &&
3756 ( currentSearchNode
!=DIRENTRY_NULL
) )
3759 * Remove the top node from the stack
3761 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3764 * Read the entry from the storage.
3766 StorageImpl_ReadDirEntry(This
->parentStorage
,
3771 * Copy the information to the return buffer.
3773 StorageUtl_CopyDirEntryToSTATSTG(&This
->parentStorage
->base
,
3774 currentReturnStruct
,
3779 * Step to the next item in the iteration
3782 currentReturnStruct
++;
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3790 * continue the iteration.
3792 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3795 if (*pceltFetched
== celt
)
3802 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3803 IEnumSTATSTG
* iface
,
3806 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3808 DirEntry currentEntry
;
3809 ULONG objectFetched
= 0;
3810 DirRef currentSearchNode
;
3813 * Start with the node at the top of the stack.
3815 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3817 while ( (objectFetched
< celt
) &&
3818 (currentSearchNode
!=DIRENTRY_NULL
) )
3821 * Remove the top node from the stack
3823 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3826 * Read the entry from the storage.
3828 StorageImpl_ReadDirEntry(This
->parentStorage
,
3833 * Step to the next item in the iteration
3838 * Push the next search node in the search stack.
3840 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3843 * continue the iteration.
3845 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3848 if (objectFetched
== celt
)
3854 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3855 IEnumSTATSTG
* iface
)
3857 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3859 DirEntry storageEntry
;
3860 BOOL readSuccessful
;
3863 * Re-initialize the search stack to an empty stack
3865 This
->stackSize
= 0;
3868 * Read the storage entry from the top-level storage.
3870 readSuccessful
= StorageImpl_ReadDirEntry(
3871 This
->parentStorage
,
3872 This
->storageDirEntry
,
3877 assert(storageEntry
.sizeOfNameString
!=0);
3880 * Push the search node in the search stack.
3882 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.dirRootEntry
);
3888 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3889 IEnumSTATSTG
* iface
,
3890 IEnumSTATSTG
** ppenum
)
3892 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3894 IEnumSTATSTGImpl
* newClone
;
3897 * Perform a sanity check on the parameters.
3900 return E_INVALIDARG
;
3902 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3903 This
->storageDirEntry
);
3907 * The new clone enumeration must point to the same current node as
3910 newClone
->stackSize
= This
->stackSize
;
3911 newClone
->stackMaxSize
= This
->stackMaxSize
;
3912 newClone
->stackToVisit
=
3913 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3916 newClone
->stackToVisit
,
3918 sizeof(DirRef
) * newClone
->stackSize
);
3920 *ppenum
= (IEnumSTATSTG
*)newClone
;
3923 * Don't forget to nail down a reference to the clone before
3926 IEnumSTATSTGImpl_AddRef(*ppenum
);
3931 static void IEnumSTATSTGImpl_PushSearchNode(
3932 IEnumSTATSTGImpl
* This
,
3935 DirEntry storageEntry
;
3936 BOOL readSuccessful
;
3939 * First, make sure we're not trying to push an unexisting node.
3941 if (nodeToPush
==DIRENTRY_NULL
)
3945 * First push the node to the stack
3947 if (This
->stackSize
== This
->stackMaxSize
)
3949 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3951 This
->stackToVisit
= HeapReAlloc(
3955 sizeof(DirRef
) * This
->stackMaxSize
);
3958 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3962 * Read the storage entry from the top-level storage.
3964 readSuccessful
= StorageImpl_ReadDirEntry(
3965 This
->parentStorage
,
3971 assert(storageEntry
.sizeOfNameString
!=0);
3974 * Push the previous search node in the search stack.
3976 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.leftChild
);
3980 static DirRef
IEnumSTATSTGImpl_PopSearchNode(
3981 IEnumSTATSTGImpl
* This
,
3986 if (This
->stackSize
== 0)
3987 return DIRENTRY_NULL
;
3989 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3998 * Virtual function table for the IEnumSTATSTGImpl class.
4000 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4002 IEnumSTATSTGImpl_QueryInterface
,
4003 IEnumSTATSTGImpl_AddRef
,
4004 IEnumSTATSTGImpl_Release
,
4005 IEnumSTATSTGImpl_Next
,
4006 IEnumSTATSTGImpl_Skip
,
4007 IEnumSTATSTGImpl_Reset
,
4008 IEnumSTATSTGImpl_Clone
4011 /******************************************************************************
4012 ** IEnumSTATSTGImpl implementation
4015 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4016 StorageImpl
* parentStorage
,
4017 DirRef storageDirEntry
)
4019 IEnumSTATSTGImpl
* newEnumeration
;
4021 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4023 if (newEnumeration
!=0)
4026 * Set-up the virtual function table and reference count.
4028 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4029 newEnumeration
->ref
= 0;
4032 * We want to nail-down the reference to the storage in case the
4033 * enumeration out-lives the storage in the client application.
4035 newEnumeration
->parentStorage
= parentStorage
;
4036 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4038 newEnumeration
->storageDirEntry
= storageDirEntry
;
4041 * Initialize the search stack
4043 newEnumeration
->stackSize
= 0;
4044 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4045 newEnumeration
->stackToVisit
=
4046 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef
)*ENUMSTATSGT_SIZE_INCREMENT
);
4049 * Make sure the current node of the iterator is the first one.
4051 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4054 return newEnumeration
;
4058 * Virtual function table for the Storage32InternalImpl class.
4060 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4062 StorageBaseImpl_QueryInterface
,
4063 StorageBaseImpl_AddRef
,
4064 StorageBaseImpl_Release
,
4065 StorageBaseImpl_CreateStream
,
4066 StorageBaseImpl_OpenStream
,
4067 StorageBaseImpl_CreateStorage
,
4068 StorageBaseImpl_OpenStorage
,
4069 StorageBaseImpl_CopyTo
,
4070 StorageBaseImpl_MoveElementTo
,
4071 StorageInternalImpl_Commit
,
4072 StorageInternalImpl_Revert
,
4073 StorageBaseImpl_EnumElements
,
4074 StorageBaseImpl_DestroyElement
,
4075 StorageBaseImpl_RenameElement
,
4076 StorageBaseImpl_SetElementTimes
,
4077 StorageBaseImpl_SetClass
,
4078 StorageBaseImpl_SetStateBits
,
4079 StorageBaseImpl_Stat
4082 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4084 StorageInternalImpl_Destroy
4087 /******************************************************************************
4088 ** Storage32InternalImpl implementation
4091 static StorageInternalImpl
* StorageInternalImpl_Construct(
4092 StorageImpl
* ancestorStorage
,
4094 DirRef storageDirEntry
)
4096 StorageInternalImpl
* newStorage
;
4098 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4102 list_init(&newStorage
->base
.strmHead
);
4104 list_init(&newStorage
->base
.storageHead
);
4107 * Initialize the virtual function table.
4109 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4110 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4111 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4114 * Keep the ancestor storage pointer but do not nail a reference to it.
4116 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4119 * Keep a reference to the directory entry of this storage
4121 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4123 newStorage
->base
.create
= 0;
4131 /******************************************************************************
4132 ** StorageUtl implementation
4135 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4139 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4140 *value
= lendian16toh(tmp
);
4143 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4145 value
= htole16(value
);
4146 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4149 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4153 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4154 *value
= lendian32toh(tmp
);
4157 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4159 value
= htole32(value
);
4160 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4163 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4164 ULARGE_INTEGER
* value
)
4166 #ifdef WORDS_BIGENDIAN
4169 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4170 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4171 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4173 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4177 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4178 const ULARGE_INTEGER
*value
)
4180 #ifdef WORDS_BIGENDIAN
4183 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4184 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4185 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4187 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4191 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4193 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4194 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4195 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4197 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4200 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4202 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4203 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4204 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4206 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4209 void StorageUtl_CopyDirEntryToSTATSTG(
4210 StorageBaseImpl
* storage
,
4211 STATSTG
* destination
,
4212 const DirEntry
* source
,
4217 if (source
->stgType
== STGTY_ROOT
)
4219 /* replace the name of root entry (often "Root Entry") by the file name */
4220 entryName
= storage
->filename
;
4224 entryName
= source
->name
;
4228 * The copy of the string occurs only when the flag is not set
4230 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4231 (entryName
== NULL
) ||
4232 (entryName
[0] == 0) )
4234 destination
->pwcsName
= 0;
4238 destination
->pwcsName
=
4239 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4241 strcpyW(destination
->pwcsName
, entryName
);
4244 switch (source
->stgType
)
4248 destination
->type
= STGTY_STORAGE
;
4251 destination
->type
= STGTY_STREAM
;
4254 destination
->type
= STGTY_STREAM
;
4258 destination
->cbSize
= source
->size
;
4260 currentReturnStruct->mtime = {0}; TODO
4261 currentReturnStruct->ctime = {0};
4262 currentReturnStruct->atime = {0};
4264 destination
->grfMode
= 0;
4265 destination
->grfLocksSupported
= 0;
4266 destination
->clsid
= source
->clsid
;
4267 destination
->grfStateBits
= 0;
4268 destination
->reserved
= 0;
4271 /******************************************************************************
4272 ** BlockChainStream implementation
4275 BlockChainStream
* BlockChainStream_Construct(
4276 StorageImpl
* parentStorage
,
4277 ULONG
* headOfStreamPlaceHolder
,
4280 BlockChainStream
* newStream
;
4283 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4285 newStream
->parentStorage
= parentStorage
;
4286 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4287 newStream
->ownerDirEntry
= dirEntry
;
4288 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4289 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4290 newStream
->numBlocks
= 0;
4292 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4294 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4296 newStream
->numBlocks
++;
4297 newStream
->tailIndex
= blockIndex
;
4299 if(FAILED(StorageImpl_GetNextBlockInChain(
4304 HeapFree(GetProcessHeap(), 0, newStream
);
4312 void BlockChainStream_Destroy(BlockChainStream
* This
)
4314 HeapFree(GetProcessHeap(), 0, This
);
4317 /******************************************************************************
4318 * BlockChainStream_GetHeadOfChain
4320 * Returns the head of this stream chain.
4321 * Some special chains don't have directory entries, their heads are kept in
4322 * This->headOfStreamPlaceHolder.
4325 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4327 DirEntry chainEntry
;
4328 BOOL readSuccessful
;
4330 if (This
->headOfStreamPlaceHolder
!= 0)
4331 return *(This
->headOfStreamPlaceHolder
);
4333 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
4335 readSuccessful
= StorageImpl_ReadDirEntry(
4336 This
->parentStorage
,
4337 This
->ownerDirEntry
,
4342 return chainEntry
.startingBlock
;
4346 return BLOCK_END_OF_CHAIN
;
4349 /******************************************************************************
4350 * BlockChainStream_GetCount
4352 * Returns the number of blocks that comprises this chain.
4353 * This is not the size of the stream as the last block may not be full!
4356 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4361 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4363 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4367 if(FAILED(StorageImpl_GetNextBlockInChain(
4368 This
->parentStorage
,
4377 /******************************************************************************
4378 * BlockChainStream_ReadAt
4380 * Reads a specified number of bytes from this chain at the specified offset.
4381 * bytesRead may be NULL.
4382 * Failure will be returned if the specified number of bytes has not been read.
4384 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4385 ULARGE_INTEGER offset
,
4390 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4391 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4392 ULONG bytesToReadInBuffer
;
4396 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4399 * Find the first block in the stream that contains part of the buffer.
4401 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4402 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4403 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4405 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4406 This
->lastBlockNoInSequence
= blockNoInSequence
;
4410 ULONG temp
= blockNoInSequence
;
4412 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4413 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4414 This
->lastBlockNoInSequence
= temp
;
4417 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4419 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4420 return STG_E_DOCFILECORRUPT
;
4421 blockNoInSequence
--;
4424 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4425 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4427 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4430 * Start reading the buffer.
4433 bufferWalker
= buffer
;
4435 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4437 ULARGE_INTEGER ulOffset
;
4440 * Calculate how many bytes we can copy from this big block.
4442 bytesToReadInBuffer
=
4443 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4445 TRACE("block %i\n",blockIndex
);
4446 ulOffset
.u
.HighPart
= 0;
4447 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4450 StorageImpl_ReadAt(This
->parentStorage
,
4453 bytesToReadInBuffer
,
4456 * Step to the next big block.
4458 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4459 return STG_E_DOCFILECORRUPT
;
4461 bufferWalker
+= bytesReadAt
;
4462 size
-= bytesReadAt
;
4463 *bytesRead
+= bytesReadAt
;
4464 offsetInBlock
= 0; /* There is no offset on the next block */
4466 if (bytesToReadInBuffer
!= bytesReadAt
)
4470 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4473 /******************************************************************************
4474 * BlockChainStream_WriteAt
4476 * Writes the specified number of bytes to this chain at the specified offset.
4477 * Will fail if not all specified number of bytes have been written.
4479 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4480 ULARGE_INTEGER offset
,
4483 ULONG
* bytesWritten
)
4485 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4486 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4489 const BYTE
* bufferWalker
;
4492 * Find the first block in the stream that contains part of the buffer.
4494 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4495 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4496 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4498 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4499 This
->lastBlockNoInSequence
= blockNoInSequence
;
4503 ULONG temp
= blockNoInSequence
;
4505 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4506 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4507 This
->lastBlockNoInSequence
= temp
;
4510 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4512 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4514 return STG_E_DOCFILECORRUPT
;
4515 blockNoInSequence
--;
4518 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4520 /* BlockChainStream_SetSize should have already been called to ensure we have
4521 * enough blocks in the chain to write into */
4522 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4524 ERR("not enough blocks in chain to write data\n");
4525 return STG_E_DOCFILECORRUPT
;
4529 bufferWalker
= buffer
;
4531 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4533 ULARGE_INTEGER ulOffset
;
4534 DWORD bytesWrittenAt
;
4536 * Calculate how many bytes we can copy from this big block.
4539 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4541 TRACE("block %i\n",blockIndex
);
4542 ulOffset
.u
.HighPart
= 0;
4543 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4546 StorageImpl_WriteAt(This
->parentStorage
,
4553 * Step to the next big block.
4555 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4557 return STG_E_DOCFILECORRUPT
;
4559 bufferWalker
+= bytesWrittenAt
;
4560 size
-= bytesWrittenAt
;
4561 *bytesWritten
+= bytesWrittenAt
;
4562 offsetInBlock
= 0; /* There is no offset on the next block */
4564 if (bytesWrittenAt
!= bytesToWrite
)
4568 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4571 /******************************************************************************
4572 * BlockChainStream_Shrink
4574 * Shrinks this chain in the big block depot.
4576 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4577 ULARGE_INTEGER newSize
)
4579 ULONG blockIndex
, extraBlock
;
4584 * Reset the last accessed block cache.
4586 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4587 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4590 * Figure out how many blocks are needed to contain the new size
4592 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4594 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4597 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4600 * Go to the new end of chain
4602 while (count
< numBlocks
)
4604 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4610 /* Get the next block before marking the new end */
4611 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4615 /* Mark the new end of chain */
4616 StorageImpl_SetNextBlockInChain(
4617 This
->parentStorage
,
4619 BLOCK_END_OF_CHAIN
);
4621 This
->tailIndex
= blockIndex
;
4622 This
->numBlocks
= numBlocks
;
4625 * Mark the extra blocks as free
4627 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4629 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4632 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4633 extraBlock
= blockIndex
;
4639 /******************************************************************************
4640 * BlockChainStream_Enlarge
4642 * Grows this chain in the big block depot.
4644 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4645 ULARGE_INTEGER newSize
)
4647 ULONG blockIndex
, currentBlock
;
4649 ULONG oldNumBlocks
= 0;
4651 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4654 * Empty chain. Create the head.
4656 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4658 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4659 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4661 BLOCK_END_OF_CHAIN
);
4663 if (This
->headOfStreamPlaceHolder
!= 0)
4665 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4669 DirEntry chainEntry
;
4670 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
4672 StorageImpl_ReadDirEntry(
4673 This
->parentStorage
,
4674 This
->ownerDirEntry
,
4677 chainEntry
.startingBlock
= blockIndex
;
4679 StorageImpl_WriteDirEntry(
4680 This
->parentStorage
,
4681 This
->ownerDirEntry
,
4685 This
->tailIndex
= blockIndex
;
4686 This
->numBlocks
= 1;
4690 * Figure out how many blocks are needed to contain this stream
4692 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4694 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4698 * Go to the current end of chain
4700 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4702 currentBlock
= blockIndex
;
4704 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4707 currentBlock
= blockIndex
;
4709 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4714 This
->tailIndex
= currentBlock
;
4717 currentBlock
= This
->tailIndex
;
4718 oldNumBlocks
= This
->numBlocks
;
4721 * Add new blocks to the chain
4723 if (oldNumBlocks
< newNumBlocks
)
4725 while (oldNumBlocks
< newNumBlocks
)
4727 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4729 StorageImpl_SetNextBlockInChain(
4730 This
->parentStorage
,
4734 StorageImpl_SetNextBlockInChain(
4735 This
->parentStorage
,
4737 BLOCK_END_OF_CHAIN
);
4739 currentBlock
= blockIndex
;
4743 This
->tailIndex
= blockIndex
;
4744 This
->numBlocks
= newNumBlocks
;
4750 /******************************************************************************
4751 * BlockChainStream_SetSize
4753 * Sets the size of this stream. The big block depot will be updated.
4754 * The file will grow if we grow the chain.
4756 * TODO: Free the actual blocks in the file when we shrink the chain.
4757 * Currently, the blocks are still in the file. So the file size
4758 * doesn't shrink even if we shrink streams.
4760 BOOL
BlockChainStream_SetSize(
4761 BlockChainStream
* This
,
4762 ULARGE_INTEGER newSize
)
4764 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4766 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4769 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4771 BlockChainStream_Shrink(This
, newSize
);
4775 BlockChainStream_Enlarge(This
, newSize
);
4781 /******************************************************************************
4782 * BlockChainStream_GetSize
4784 * Returns the size of this chain.
4785 * Will return the block count if this chain doesn't have a directory entry.
4787 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4789 DirEntry chainEntry
;
4791 if(This
->headOfStreamPlaceHolder
== NULL
)
4794 * This chain has a directory entry so use the size value from there.
4796 StorageImpl_ReadDirEntry(
4797 This
->parentStorage
,
4798 This
->ownerDirEntry
,
4801 return chainEntry
.size
;
4806 * this chain is a chain that does not have a directory entry, figure out the
4807 * size by making the product number of used blocks times the
4810 ULARGE_INTEGER result
;
4811 result
.u
.HighPart
= 0;
4814 BlockChainStream_GetCount(This
) *
4815 This
->parentStorage
->bigBlockSize
;
4821 /******************************************************************************
4822 ** SmallBlockChainStream implementation
4825 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4826 StorageImpl
* parentStorage
,
4827 ULONG
* headOfStreamPlaceHolder
,
4830 SmallBlockChainStream
* newStream
;
4832 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4834 newStream
->parentStorage
= parentStorage
;
4835 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4836 newStream
->ownerDirEntry
= dirEntry
;
4841 void SmallBlockChainStream_Destroy(
4842 SmallBlockChainStream
* This
)
4844 HeapFree(GetProcessHeap(), 0, This
);
4847 /******************************************************************************
4848 * SmallBlockChainStream_GetHeadOfChain
4850 * Returns the head of this chain of small blocks.
4852 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4853 SmallBlockChainStream
* This
)
4855 DirEntry chainEntry
;
4856 BOOL readSuccessful
;
4858 if (This
->headOfStreamPlaceHolder
!= NULL
)
4859 return *(This
->headOfStreamPlaceHolder
);
4861 if (This
->ownerDirEntry
)
4863 readSuccessful
= StorageImpl_ReadDirEntry(
4864 This
->parentStorage
,
4865 This
->ownerDirEntry
,
4870 return chainEntry
.startingBlock
;
4875 return BLOCK_END_OF_CHAIN
;
4878 /******************************************************************************
4879 * SmallBlockChainStream_GetNextBlockInChain
4881 * Returns the index of the next small block in this chain.
4884 * - BLOCK_END_OF_CHAIN: end of this chain
4885 * - BLOCK_UNUSED: small block 'blockIndex' is free
4887 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4888 SmallBlockChainStream
* This
,
4890 ULONG
* nextBlockInChain
)
4892 ULARGE_INTEGER offsetOfBlockInDepot
;
4897 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4899 offsetOfBlockInDepot
.u
.HighPart
= 0;
4900 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4903 * Read those bytes in the buffer from the small block file.
4905 res
= BlockChainStream_ReadAt(
4906 This
->parentStorage
->smallBlockDepotChain
,
4907 offsetOfBlockInDepot
,
4914 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4921 /******************************************************************************
4922 * SmallBlockChainStream_SetNextBlockInChain
4924 * Writes the index of the next block of the specified block in the small
4926 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4927 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4929 static void SmallBlockChainStream_SetNextBlockInChain(
4930 SmallBlockChainStream
* This
,
4934 ULARGE_INTEGER offsetOfBlockInDepot
;
4938 offsetOfBlockInDepot
.u
.HighPart
= 0;
4939 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4941 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4944 * Read those bytes in the buffer from the small block file.
4946 BlockChainStream_WriteAt(
4947 This
->parentStorage
->smallBlockDepotChain
,
4948 offsetOfBlockInDepot
,
4954 /******************************************************************************
4955 * SmallBlockChainStream_FreeBlock
4957 * Flag small block 'blockIndex' as free in the small block depot.
4959 static void SmallBlockChainStream_FreeBlock(
4960 SmallBlockChainStream
* This
,
4963 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4966 /******************************************************************************
4967 * SmallBlockChainStream_GetNextFreeBlock
4969 * Returns the index of a free small block. The small block depot will be
4970 * enlarged if necessary. The small block chain will also be enlarged if
4973 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
4974 SmallBlockChainStream
* This
)
4976 ULARGE_INTEGER offsetOfBlockInDepot
;
4979 ULONG blockIndex
= 0;
4980 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4982 ULONG smallBlocksPerBigBlock
;
4984 offsetOfBlockInDepot
.u
.HighPart
= 0;
4987 * Scan the small block depot for a free block
4989 while (nextBlockIndex
!= BLOCK_UNUSED
)
4991 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4993 res
= BlockChainStream_ReadAt(
4994 This
->parentStorage
->smallBlockDepotChain
,
4995 offsetOfBlockInDepot
,
5001 * If we run out of space for the small block depot, enlarge it
5005 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5007 if (nextBlockIndex
!= BLOCK_UNUSED
)
5013 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5015 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5016 ULONG nextBlock
, newsbdIndex
;
5017 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5019 nextBlock
= sbdIndex
;
5020 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5022 sbdIndex
= nextBlock
;
5023 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5026 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5027 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5028 StorageImpl_SetNextBlockInChain(
5029 This
->parentStorage
,
5033 StorageImpl_SetNextBlockInChain(
5034 This
->parentStorage
,
5036 BLOCK_END_OF_CHAIN
);
5039 * Initialize all the small blocks to free
5041 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5042 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5047 * We have just created the small block depot.
5053 * Save it in the header
5055 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5056 StorageImpl_SaveFileHeader(This
->parentStorage
);
5059 * And allocate the first big block that will contain small blocks
5062 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5064 StorageImpl_SetNextBlockInChain(
5065 This
->parentStorage
,
5067 BLOCK_END_OF_CHAIN
);
5069 StorageImpl_ReadDirEntry(
5070 This
->parentStorage
,
5071 This
->parentStorage
->base
.storageDirEntry
,
5074 rootEntry
.startingBlock
= sbStartIndex
;
5075 rootEntry
.size
.u
.HighPart
= 0;
5076 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5078 StorageImpl_WriteDirEntry(
5079 This
->parentStorage
,
5080 This
->parentStorage
->base
.storageDirEntry
,
5084 StorageImpl_SaveFileHeader(This
->parentStorage
);
5088 smallBlocksPerBigBlock
=
5089 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5092 * Verify if we have to allocate big blocks to contain small blocks
5094 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5097 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5099 StorageImpl_ReadDirEntry(
5100 This
->parentStorage
,
5101 This
->parentStorage
->base
.storageDirEntry
,
5104 if (rootEntry
.size
.u
.LowPart
<
5105 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5107 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5109 BlockChainStream_SetSize(
5110 This
->parentStorage
->smallBlockRootChain
,
5113 StorageImpl_WriteDirEntry(
5114 This
->parentStorage
,
5115 This
->parentStorage
->base
.storageDirEntry
,
5123 /******************************************************************************
5124 * SmallBlockChainStream_ReadAt
5126 * Reads a specified number of bytes from this chain at the specified offset.
5127 * bytesRead may be NULL.
5128 * Failure will be returned if the specified number of bytes has not been read.
5130 HRESULT
SmallBlockChainStream_ReadAt(
5131 SmallBlockChainStream
* This
,
5132 ULARGE_INTEGER offset
,
5138 ULARGE_INTEGER offsetInBigBlockFile
;
5139 ULONG blockNoInSequence
=
5140 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5142 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5143 ULONG bytesToReadInBuffer
;
5145 ULONG bytesReadFromBigBlockFile
;
5149 * This should never happen on a small block file.
5151 assert(offset
.u
.HighPart
==0);
5154 * Find the first block in the stream that contains part of the buffer.
5156 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5158 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5160 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5163 blockNoInSequence
--;
5167 * Start reading the buffer.
5170 bufferWalker
= buffer
;
5172 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5175 * Calculate how many bytes we can copy from this small block.
5177 bytesToReadInBuffer
=
5178 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5181 * Calculate the offset of the small block in the small block file.
5183 offsetInBigBlockFile
.u
.HighPart
= 0;
5184 offsetInBigBlockFile
.u
.LowPart
=
5185 blockIndex
* This
->parentStorage
->smallBlockSize
;
5187 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5190 * Read those bytes in the buffer from the small block file.
5191 * The small block has already been identified so it shouldn't fail
5192 * unless the file is corrupt.
5194 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5195 offsetInBigBlockFile
,
5196 bytesToReadInBuffer
,
5198 &bytesReadFromBigBlockFile
);
5204 * Step to the next big block.
5206 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5208 return STG_E_DOCFILECORRUPT
;
5210 bufferWalker
+= bytesReadFromBigBlockFile
;
5211 size
-= bytesReadFromBigBlockFile
;
5212 *bytesRead
+= bytesReadFromBigBlockFile
;
5213 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5216 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5219 /******************************************************************************
5220 * SmallBlockChainStream_WriteAt
5222 * Writes the specified number of bytes to this chain at the specified offset.
5223 * Will fail if not all specified number of bytes have been written.
5225 HRESULT
SmallBlockChainStream_WriteAt(
5226 SmallBlockChainStream
* This
,
5227 ULARGE_INTEGER offset
,
5230 ULONG
* bytesWritten
)
5232 ULARGE_INTEGER offsetInBigBlockFile
;
5233 ULONG blockNoInSequence
=
5234 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5236 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5237 ULONG bytesToWriteInBuffer
;
5239 ULONG bytesWrittenToBigBlockFile
;
5240 const BYTE
* bufferWalker
;
5244 * This should never happen on a small block file.
5246 assert(offset
.u
.HighPart
==0);
5249 * Find the first block in the stream that contains part of the buffer.
5251 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5253 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5255 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5256 return STG_E_DOCFILECORRUPT
;
5257 blockNoInSequence
--;
5261 * Start writing the buffer.
5264 bufferWalker
= buffer
;
5265 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5268 * Calculate how many bytes we can copy to this small block.
5270 bytesToWriteInBuffer
=
5271 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5274 * Calculate the offset of the small block in the small block file.
5276 offsetInBigBlockFile
.u
.HighPart
= 0;
5277 offsetInBigBlockFile
.u
.LowPart
=
5278 blockIndex
* This
->parentStorage
->smallBlockSize
;
5280 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5283 * Write those bytes in the buffer to the small block file.
5285 res
= BlockChainStream_WriteAt(
5286 This
->parentStorage
->smallBlockRootChain
,
5287 offsetInBigBlockFile
,
5288 bytesToWriteInBuffer
,
5290 &bytesWrittenToBigBlockFile
);
5295 * Step to the next big block.
5297 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5300 bufferWalker
+= bytesWrittenToBigBlockFile
;
5301 size
-= bytesWrittenToBigBlockFile
;
5302 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5303 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5306 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5309 /******************************************************************************
5310 * SmallBlockChainStream_Shrink
5312 * Shrinks this chain in the small block depot.
5314 static BOOL
SmallBlockChainStream_Shrink(
5315 SmallBlockChainStream
* This
,
5316 ULARGE_INTEGER newSize
)
5318 ULONG blockIndex
, extraBlock
;
5322 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5324 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5327 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5330 * Go to the new end of chain
5332 while (count
< numBlocks
)
5334 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5341 * If the count is 0, we have a special case, the head of the chain was
5346 DirEntry chainEntry
;
5348 StorageImpl_ReadDirEntry(This
->parentStorage
,
5349 This
->ownerDirEntry
,
5352 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
5354 StorageImpl_WriteDirEntry(This
->parentStorage
,
5355 This
->ownerDirEntry
,
5359 * We start freeing the chain at the head block.
5361 extraBlock
= blockIndex
;
5365 /* Get the next block before marking the new end */
5366 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5370 /* Mark the new end of chain */
5371 SmallBlockChainStream_SetNextBlockInChain(
5374 BLOCK_END_OF_CHAIN
);
5378 * Mark the extra blocks as free
5380 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5382 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5385 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5386 extraBlock
= blockIndex
;
5392 /******************************************************************************
5393 * SmallBlockChainStream_Enlarge
5395 * Grows this chain in the small block depot.
5397 static BOOL
SmallBlockChainStream_Enlarge(
5398 SmallBlockChainStream
* This
,
5399 ULARGE_INTEGER newSize
)
5401 ULONG blockIndex
, currentBlock
;
5403 ULONG oldNumBlocks
= 0;
5405 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5408 * Empty chain. Create the head.
5410 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5412 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5413 SmallBlockChainStream_SetNextBlockInChain(
5416 BLOCK_END_OF_CHAIN
);
5418 if (This
->headOfStreamPlaceHolder
!= NULL
)
5420 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5424 DirEntry chainEntry
;
5426 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5429 chainEntry
.startingBlock
= blockIndex
;
5431 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5436 currentBlock
= blockIndex
;
5439 * Figure out how many blocks are needed to contain this stream
5441 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5443 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5447 * Go to the current end of chain
5449 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5452 currentBlock
= blockIndex
;
5453 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5458 * Add new blocks to the chain
5460 while (oldNumBlocks
< newNumBlocks
)
5462 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5463 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5465 SmallBlockChainStream_SetNextBlockInChain(
5468 BLOCK_END_OF_CHAIN
);
5470 currentBlock
= blockIndex
;
5477 /******************************************************************************
5478 * SmallBlockChainStream_SetSize
5480 * Sets the size of this stream.
5481 * The file will grow if we grow the chain.
5483 * TODO: Free the actual blocks in the file when we shrink the chain.
5484 * Currently, the blocks are still in the file. So the file size
5485 * doesn't shrink even if we shrink streams.
5487 BOOL
SmallBlockChainStream_SetSize(
5488 SmallBlockChainStream
* This
,
5489 ULARGE_INTEGER newSize
)
5491 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5493 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5496 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5498 SmallBlockChainStream_Shrink(This
, newSize
);
5502 SmallBlockChainStream_Enlarge(This
, newSize
);
5508 /******************************************************************************
5509 * SmallBlockChainStream_GetCount
5511 * Returns the number of small blocks that comprises this chain.
5512 * This is not the size of the stream as the last block may not be full!
5515 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5520 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5522 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
5526 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
5527 blockIndex
, &blockIndex
)))
5534 /******************************************************************************
5535 * SmallBlockChainStream_GetSize
5537 * Returns the size of this chain.
5539 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5541 DirEntry chainEntry
;
5543 if(This
->headOfStreamPlaceHolder
!= NULL
)
5545 ULARGE_INTEGER result
;
5546 result
.u
.HighPart
= 0;
5548 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
5549 This
->parentStorage
->smallBlockSize
;
5554 StorageImpl_ReadDirEntry(
5555 This
->parentStorage
,
5556 This
->ownerDirEntry
,
5559 return chainEntry
.size
;
5562 /******************************************************************************
5563 * StgCreateDocfile [OLE32.@]
5564 * Creates a new compound file storage object
5567 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5568 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5569 * reserved [ ?] unused?, usually 0
5570 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5573 * S_OK if the file was successfully created
5574 * some STG_E_ value if error
5576 * if pwcsName is NULL, create file with new unique name
5577 * the function can returns
5578 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5581 HRESULT WINAPI
StgCreateDocfile(
5585 IStorage
**ppstgOpen
)
5587 StorageImpl
* newStorage
= 0;
5588 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5589 HRESULT hr
= STG_E_INVALIDFLAG
;
5593 DWORD fileAttributes
;
5594 WCHAR tempFileName
[MAX_PATH
];
5596 TRACE("(%s, %x, %d, %p)\n",
5597 debugstr_w(pwcsName
), grfMode
,
5598 reserved
, ppstgOpen
);
5601 return STG_E_INVALIDPOINTER
;
5603 return STG_E_INVALIDPARAMETER
;
5605 /* if no share mode given then DENY_NONE is the default */
5606 if (STGM_SHARE_MODE(grfMode
) == 0)
5607 grfMode
|= STGM_SHARE_DENY_NONE
;
5609 if ( FAILED( validateSTGM(grfMode
) ))
5612 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5613 switch(STGM_ACCESS_MODE(grfMode
))
5616 case STGM_READWRITE
:
5622 /* in direct mode, can only use SHARE_EXCLUSIVE */
5623 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5626 /* but in transacted mode, any share mode is valid */
5629 * Generate a unique name.
5633 WCHAR tempPath
[MAX_PATH
];
5634 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5636 memset(tempPath
, 0, sizeof(tempPath
));
5637 memset(tempFileName
, 0, sizeof(tempFileName
));
5639 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5642 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5643 pwcsName
= tempFileName
;
5646 hr
= STG_E_INSUFFICIENTMEMORY
;
5650 creationMode
= TRUNCATE_EXISTING
;
5654 creationMode
= GetCreationModeFromSTGM(grfMode
);
5658 * Interpret the STGM value grfMode
5660 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5661 accessMode
= GetAccessModeFromSTGM(grfMode
);
5663 if (grfMode
& STGM_DELETEONRELEASE
)
5664 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5666 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5668 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
5669 FIXME("Storage share mode not implemented.\n");
5671 if (grfMode
& STGM_TRANSACTED
)
5672 FIXME("Transacted mode not implemented.\n");
5676 hFile
= CreateFileW(pwcsName
,
5684 if (hFile
== INVALID_HANDLE_VALUE
)
5686 if(GetLastError() == ERROR_FILE_EXISTS
)
5687 hr
= STG_E_FILEALREADYEXISTS
;
5694 * Allocate and initialize the new IStorage32object.
5696 hr
= StorageImpl_Construct(
5711 * Get an "out" pointer for the caller.
5713 *ppstgOpen
= (IStorage
*)newStorage
;
5716 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5721 /******************************************************************************
5722 * StgCreateStorageEx [OLE32.@]
5724 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5726 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5727 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5729 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5731 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5732 return STG_E_INVALIDPARAMETER
;
5735 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5737 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5738 return STG_E_INVALIDPARAMETER
;
5741 if (stgfmt
== STGFMT_FILE
)
5743 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5744 return STG_E_INVALIDPARAMETER
;
5747 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5749 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5750 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5753 ERR("Invalid stgfmt argument\n");
5754 return STG_E_INVALIDPARAMETER
;
5757 /******************************************************************************
5758 * StgCreatePropSetStg [OLE32.@]
5760 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5761 IPropertySetStorage
**ppPropSetStg
)
5765 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5767 hr
= STG_E_INVALIDPARAMETER
;
5769 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5770 (void**)ppPropSetStg
);
5774 /******************************************************************************
5775 * StgOpenStorageEx [OLE32.@]
5777 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5779 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5780 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5782 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5784 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5785 return STG_E_INVALIDPARAMETER
;
5791 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5792 return STG_E_INVALIDPARAMETER
;
5794 case STGFMT_STORAGE
:
5797 case STGFMT_DOCFILE
:
5798 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5800 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5801 return STG_E_INVALIDPARAMETER
;
5803 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5807 WARN("STGFMT_ANY assuming storage\n");
5811 return STG_E_INVALIDPARAMETER
;
5814 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
5818 /******************************************************************************
5819 * StgOpenStorage [OLE32.@]
5821 HRESULT WINAPI
StgOpenStorage(
5822 const OLECHAR
*pwcsName
,
5823 IStorage
*pstgPriority
,
5827 IStorage
**ppstgOpen
)
5829 StorageImpl
* newStorage
= 0;
5834 WCHAR fullname
[MAX_PATH
];
5836 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5837 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5838 snbExclude
, reserved
, ppstgOpen
);
5842 hr
= STG_E_INVALIDNAME
;
5848 hr
= STG_E_INVALIDPOINTER
;
5854 hr
= STG_E_INVALIDPARAMETER
;
5858 if (grfMode
& STGM_PRIORITY
)
5860 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5861 return STG_E_INVALIDFLAG
;
5862 if (grfMode
& STGM_DELETEONRELEASE
)
5863 return STG_E_INVALIDFUNCTION
;
5864 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5865 return STG_E_INVALIDFLAG
;
5866 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5867 grfMode
|= STGM_SHARE_DENY_NONE
;
5869 /* STGM_PRIORITY stops other IStorage objects on the same file from
5870 * committing until the STGM_PRIORITY IStorage is closed. it also
5871 * stops non-transacted mode StgOpenStorage calls with write access from
5872 * succeeding. obviously, both of these cannot be achieved through just
5873 * file share flags */
5874 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5878 * Validate the sharing mode
5880 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5881 switch(STGM_SHARE_MODE(grfMode
))
5883 case STGM_SHARE_EXCLUSIVE
:
5884 case STGM_SHARE_DENY_WRITE
:
5887 hr
= STG_E_INVALIDFLAG
;
5891 if ( FAILED( validateSTGM(grfMode
) ) ||
5892 (grfMode
&STGM_CREATE
))
5894 hr
= STG_E_INVALIDFLAG
;
5898 /* shared reading requires transacted mode */
5899 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5900 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5901 !(grfMode
&STGM_TRANSACTED
) )
5903 hr
= STG_E_INVALIDFLAG
;
5908 * Interpret the STGM value grfMode
5910 shareMode
= GetShareModeFromSTGM(grfMode
);
5911 accessMode
= GetAccessModeFromSTGM(grfMode
);
5915 hFile
= CreateFileW( pwcsName
,
5920 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5923 if (hFile
==INVALID_HANDLE_VALUE
)
5925 DWORD last_error
= GetLastError();
5931 case ERROR_FILE_NOT_FOUND
:
5932 hr
= STG_E_FILENOTFOUND
;
5935 case ERROR_PATH_NOT_FOUND
:
5936 hr
= STG_E_PATHNOTFOUND
;
5939 case ERROR_ACCESS_DENIED
:
5940 case ERROR_WRITE_PROTECT
:
5941 hr
= STG_E_ACCESSDENIED
;
5944 case ERROR_SHARING_VIOLATION
:
5945 hr
= STG_E_SHAREVIOLATION
;
5956 * Refuse to open the file if it's too small to be a structured storage file
5957 * FIXME: verify the file when reading instead of here
5959 if (GetFileSize(hFile
, NULL
) < 0x100)
5962 hr
= STG_E_FILEALREADYEXISTS
;
5967 * Allocate and initialize the new IStorage32object.
5969 hr
= StorageImpl_Construct(
5981 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5983 if(hr
== STG_E_INVALIDHEADER
)
5984 hr
= STG_E_FILEALREADYEXISTS
;
5988 /* prepare the file name string given in lieu of the root property name */
5989 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5990 memcpy(newStorage
->base
.filename
, fullname
, DIRENTRY_NAME_BUFFER_LEN
);
5991 newStorage
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = '\0';
5994 * Get an "out" pointer for the caller.
5996 *ppstgOpen
= (IStorage
*)newStorage
;
5999 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6003 /******************************************************************************
6004 * StgCreateDocfileOnILockBytes [OLE32.@]
6006 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6010 IStorage
** ppstgOpen
)
6012 StorageImpl
* newStorage
= 0;
6015 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6016 return STG_E_INVALIDPOINTER
;
6019 * Allocate and initialize the new IStorage object.
6021 hr
= StorageImpl_Construct(
6036 * Get an "out" pointer for the caller.
6038 *ppstgOpen
= (IStorage
*)newStorage
;
6043 /******************************************************************************
6044 * StgOpenStorageOnILockBytes [OLE32.@]
6046 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6048 IStorage
*pstgPriority
,
6052 IStorage
**ppstgOpen
)
6054 StorageImpl
* newStorage
= 0;
6057 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6058 return STG_E_INVALIDPOINTER
;
6060 if ( FAILED( validateSTGM(grfMode
) ))
6061 return STG_E_INVALIDFLAG
;
6066 * Allocate and initialize the new IStorage object.
6068 hr
= StorageImpl_Construct(
6083 * Get an "out" pointer for the caller.
6085 *ppstgOpen
= (IStorage
*)newStorage
;
6090 /******************************************************************************
6091 * StgSetTimes [ole32.@]
6092 * StgSetTimes [OLE32.@]
6096 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6097 FILETIME
const *patime
, FILETIME
const *pmtime
)
6099 IStorage
*stg
= NULL
;
6102 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6104 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6108 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6109 IStorage_Release(stg
);
6115 /******************************************************************************
6116 * StgIsStorageILockBytes [OLE32.@]
6118 * Determines if the ILockBytes contains a storage object.
6120 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6123 ULARGE_INTEGER offset
;
6125 offset
.u
.HighPart
= 0;
6126 offset
.u
.LowPart
= 0;
6128 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6130 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6136 /******************************************************************************
6137 * WriteClassStg [OLE32.@]
6139 * This method will store the specified CLSID in the specified storage object
6141 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6146 return E_INVALIDARG
;
6149 return STG_E_INVALIDPOINTER
;
6151 hRes
= IStorage_SetClass(pStg
, rclsid
);
6156 /***********************************************************************
6157 * ReadClassStg (OLE32.@)
6159 * This method reads the CLSID previously written to a storage object with
6160 * the WriteClassStg.
6163 * pstg [I] IStorage pointer
6164 * pclsid [O] Pointer to where the CLSID is written
6168 * Failure: HRESULT code.
6170 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6175 TRACE("(%p, %p)\n", pstg
, pclsid
);
6177 if(!pstg
|| !pclsid
)
6178 return E_INVALIDARG
;
6181 * read a STATSTG structure (contains the clsid) from the storage
6183 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6186 *pclsid
=pstatstg
.clsid
;
6191 /***********************************************************************
6192 * OleLoadFromStream (OLE32.@)
6194 * This function loads an object from stream
6196 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6200 LPPERSISTSTREAM xstm
;
6202 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6204 res
=ReadClassStm(pStm
,&clsid
);
6207 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6210 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6212 IUnknown_Release((IUnknown
*)*ppvObj
);
6215 res
=IPersistStream_Load(xstm
,pStm
);
6216 IPersistStream_Release(xstm
);
6217 /* FIXME: all refcounts ok at this point? I think they should be:
6220 * xstm : 0 (released)
6225 /***********************************************************************
6226 * OleSaveToStream (OLE32.@)
6228 * This function saves an object with the IPersistStream interface on it
6229 * to the specified stream.
6231 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6237 TRACE("(%p,%p)\n",pPStm
,pStm
);
6239 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6241 if (SUCCEEDED(res
)){
6243 res
=WriteClassStm(pStm
,&clsid
);
6247 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6250 TRACE("Finished Save\n");
6254 /****************************************************************************
6255 * This method validate a STGM parameter that can contain the values below
6257 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6258 * The stgm values contained in 0xffff0000 are bitmasks.
6260 * STGM_DIRECT 0x00000000
6261 * STGM_TRANSACTED 0x00010000
6262 * STGM_SIMPLE 0x08000000
6264 * STGM_READ 0x00000000
6265 * STGM_WRITE 0x00000001
6266 * STGM_READWRITE 0x00000002
6268 * STGM_SHARE_DENY_NONE 0x00000040
6269 * STGM_SHARE_DENY_READ 0x00000030
6270 * STGM_SHARE_DENY_WRITE 0x00000020
6271 * STGM_SHARE_EXCLUSIVE 0x00000010
6273 * STGM_PRIORITY 0x00040000
6274 * STGM_DELETEONRELEASE 0x04000000
6276 * STGM_CREATE 0x00001000
6277 * STGM_CONVERT 0x00020000
6278 * STGM_FAILIFTHERE 0x00000000
6280 * STGM_NOSCRATCH 0x00100000
6281 * STGM_NOSNAPSHOT 0x00200000
6283 static HRESULT
validateSTGM(DWORD stgm
)
6285 DWORD access
= STGM_ACCESS_MODE(stgm
);
6286 DWORD share
= STGM_SHARE_MODE(stgm
);
6287 DWORD create
= STGM_CREATE_MODE(stgm
);
6289 if (stgm
&~STGM_KNOWN_FLAGS
)
6291 ERR("unknown flags %08x\n", stgm
);
6299 case STGM_READWRITE
:
6307 case STGM_SHARE_DENY_NONE
:
6308 case STGM_SHARE_DENY_READ
:
6309 case STGM_SHARE_DENY_WRITE
:
6310 case STGM_SHARE_EXCLUSIVE
:
6319 case STGM_FAILIFTHERE
:
6326 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6328 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6332 * STGM_CREATE | STGM_CONVERT
6333 * if both are false, STGM_FAILIFTHERE is set to TRUE
6335 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6339 * STGM_NOSCRATCH requires STGM_TRANSACTED
6341 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6345 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6346 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6348 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6349 (!(stgm
& STGM_TRANSACTED
) ||
6350 share
== STGM_SHARE_EXCLUSIVE
||
6351 share
== STGM_SHARE_DENY_WRITE
) )
6357 /****************************************************************************
6358 * GetShareModeFromSTGM
6360 * This method will return a share mode flag from a STGM value.
6361 * The STGM value is assumed valid.
6363 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6365 switch (STGM_SHARE_MODE(stgm
))
6367 case STGM_SHARE_DENY_NONE
:
6368 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6369 case STGM_SHARE_DENY_READ
:
6370 return FILE_SHARE_WRITE
;
6371 case STGM_SHARE_DENY_WRITE
:
6372 return FILE_SHARE_READ
;
6373 case STGM_SHARE_EXCLUSIVE
:
6376 ERR("Invalid share mode!\n");
6381 /****************************************************************************
6382 * GetAccessModeFromSTGM
6384 * This method will return an access mode flag from a STGM value.
6385 * The STGM value is assumed valid.
6387 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6389 switch (STGM_ACCESS_MODE(stgm
))
6392 return GENERIC_READ
;
6394 case STGM_READWRITE
:
6395 return GENERIC_READ
| GENERIC_WRITE
;
6397 ERR("Invalid access mode!\n");
6402 /****************************************************************************
6403 * GetCreationModeFromSTGM
6405 * This method will return a creation mode flag from a STGM value.
6406 * The STGM value is assumed valid.
6408 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6410 switch(STGM_CREATE_MODE(stgm
))
6413 return CREATE_ALWAYS
;
6415 FIXME("STGM_CONVERT not implemented!\n");
6417 case STGM_FAILIFTHERE
:
6420 ERR("Invalid create mode!\n");
6426 /*************************************************************************
6427 * OLECONVERT_LoadOLE10 [Internal]
6429 * Loads the OLE10 STREAM to memory
6432 * pOleStream [I] The OLESTREAM
6433 * pData [I] Data Structure for the OLESTREAM Data
6437 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6438 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6441 * This function is used by OleConvertOLESTREAMToIStorage only.
6443 * Memory allocated for pData must be freed by the caller
6445 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6448 HRESULT hRes
= S_OK
;
6452 pData
->pData
= NULL
;
6453 pData
->pstrOleObjFileName
= NULL
;
6455 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6458 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6459 if(dwSize
!= sizeof(pData
->dwOleID
))
6461 hRes
= CONVERT10_E_OLESTREAM_GET
;
6463 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6465 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6476 /* Get the TypeID... more info needed for this field */
6477 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6478 if(dwSize
!= sizeof(pData
->dwTypeID
))
6480 hRes
= CONVERT10_E_OLESTREAM_GET
;
6485 if(pData
->dwTypeID
!= 0)
6487 /* Get the length of the OleTypeName */
6488 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6489 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6491 hRes
= CONVERT10_E_OLESTREAM_GET
;
6496 if(pData
->dwOleTypeNameLength
> 0)
6498 /* Get the OleTypeName */
6499 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6500 if(dwSize
!= pData
->dwOleTypeNameLength
)
6502 hRes
= CONVERT10_E_OLESTREAM_GET
;
6508 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6509 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6511 hRes
= CONVERT10_E_OLESTREAM_GET
;
6515 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6516 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6517 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6518 if(pData
->pstrOleObjFileName
)
6520 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
6521 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6523 hRes
= CONVERT10_E_OLESTREAM_GET
;
6527 hRes
= CONVERT10_E_OLESTREAM_GET
;
6532 /* Get the Width of the Metafile */
6533 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6534 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6536 hRes
= CONVERT10_E_OLESTREAM_GET
;
6540 /* Get the Height of the Metafile */
6541 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6542 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6544 hRes
= CONVERT10_E_OLESTREAM_GET
;
6550 /* Get the Length of the Data */
6551 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6552 if(dwSize
!= sizeof(pData
->dwDataLength
))
6554 hRes
= CONVERT10_E_OLESTREAM_GET
;
6558 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
6560 if(!bStrem1
) /* if it is a second OLE stream data */
6562 pData
->dwDataLength
-= 8;
6563 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
6564 if(dwSize
!= sizeof(pData
->strUnknown
))
6566 hRes
= CONVERT10_E_OLESTREAM_GET
;
6572 if(pData
->dwDataLength
> 0)
6574 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6576 /* Get Data (ex. IStorage, Metafile, or BMP) */
6579 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6580 if(dwSize
!= pData
->dwDataLength
)
6582 hRes
= CONVERT10_E_OLESTREAM_GET
;
6587 hRes
= CONVERT10_E_OLESTREAM_GET
;
6596 /*************************************************************************
6597 * OLECONVERT_SaveOLE10 [Internal]
6599 * Saves the OLE10 STREAM From memory
6602 * pData [I] Data Structure for the OLESTREAM Data
6603 * pOleStream [I] The OLESTREAM to save
6607 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6610 * This function is used by OleConvertIStorageToOLESTREAM only.
6613 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6616 HRESULT hRes
= S_OK
;
6620 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6621 if(dwSize
!= sizeof(pData
->dwOleID
))
6623 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6628 /* Set the TypeID */
6629 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6630 if(dwSize
!= sizeof(pData
->dwTypeID
))
6632 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6636 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6638 /* Set the Length of the OleTypeName */
6639 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6640 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6642 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6647 if(pData
->dwOleTypeNameLength
> 0)
6649 /* Set the OleTypeName */
6650 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6651 if(dwSize
!= pData
->dwOleTypeNameLength
)
6653 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6660 /* Set the width of the Metafile */
6661 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6662 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6664 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6670 /* Set the height of the Metafile */
6671 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6672 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6674 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6680 /* Set the length of the Data */
6681 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6682 if(dwSize
!= sizeof(pData
->dwDataLength
))
6684 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6690 if(pData
->dwDataLength
> 0)
6692 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6693 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6694 if(dwSize
!= pData
->dwDataLength
)
6696 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6704 /*************************************************************************
6705 * OLECONVERT_GetOLE20FromOLE10[Internal]
6707 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6708 * opens it, and copies the content to the dest IStorage for
6709 * OleConvertOLESTREAMToIStorage
6713 * pDestStorage [I] The IStorage to copy the data to
6714 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6715 * nBufferLength [I] The size of the buffer
6724 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
6728 IStorage
*pTempStorage
;
6729 DWORD dwNumOfBytesWritten
;
6730 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6731 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6733 /* Create a temp File */
6734 GetTempPathW(MAX_PATH
, wstrTempDir
);
6735 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6736 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6738 if(hFile
!= INVALID_HANDLE_VALUE
)
6740 /* Write IStorage Data to File */
6741 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6744 /* Open and copy temp storage to the Dest Storage */
6745 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6748 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6749 IStorage_Release(pTempStorage
);
6751 DeleteFileW(wstrTempFile
);
6756 /*************************************************************************
6757 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6759 * Saves the OLE10 STREAM From memory
6762 * pStorage [I] The Src IStorage to copy
6763 * pData [I] The Dest Memory to write to.
6766 * The size in bytes allocated for pData
6769 * Memory allocated for pData must be freed by the caller
6771 * Used by OleConvertIStorageToOLESTREAM only.
6774 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6778 DWORD nDataLength
= 0;
6779 IStorage
*pTempStorage
;
6780 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6781 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6785 /* Create temp Storage */
6786 GetTempPathW(MAX_PATH
, wstrTempDir
);
6787 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6788 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6792 /* Copy Src Storage to the Temp Storage */
6793 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6794 IStorage_Release(pTempStorage
);
6796 /* Open Temp Storage as a file and copy to memory */
6797 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6798 if(hFile
!= INVALID_HANDLE_VALUE
)
6800 nDataLength
= GetFileSize(hFile
, NULL
);
6801 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6802 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6805 DeleteFileW(wstrTempFile
);
6810 /*************************************************************************
6811 * OLECONVERT_CreateOleStream [Internal]
6813 * Creates the "\001OLE" stream in the IStorage if necessary.
6816 * pStorage [I] Dest storage to create the stream in
6822 * This function is used by OleConvertOLESTREAMToIStorage only.
6824 * This stream is still unknown, MS Word seems to have extra data
6825 * but since the data is stored in the OLESTREAM there should be
6826 * no need to recreate the stream. If the stream is manually
6827 * deleted it will create it with this default data.
6830 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6834 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6835 BYTE pOleStreamHeader
[] =
6837 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6839 0x00, 0x00, 0x00, 0x00
6842 /* Create stream if not present */
6843 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6844 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6848 /* Write default Data */
6849 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6850 IStream_Release(pStream
);
6854 /* write a string to a stream, preceded by its length */
6855 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6862 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6863 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6868 str
= CoTaskMemAlloc( len
);
6869 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6870 r
= IStream_Write( stm
, str
, len
, NULL
);
6871 CoTaskMemFree( str
);
6875 /* read a string preceded by its length from a stream */
6876 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6879 DWORD len
, count
= 0;
6883 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6886 if( count
!= sizeof(len
) )
6887 return E_OUTOFMEMORY
;
6889 TRACE("%d bytes\n",len
);
6891 str
= CoTaskMemAlloc( len
);
6893 return E_OUTOFMEMORY
;
6895 r
= IStream_Read( stm
, str
, len
, &count
);
6900 CoTaskMemFree( str
);
6901 return E_OUTOFMEMORY
;
6904 TRACE("Read string %s\n",debugstr_an(str
,len
));
6906 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6907 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6909 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6910 CoTaskMemFree( str
);
6918 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6919 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6923 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6925 static const BYTE unknown1
[12] =
6926 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6927 0xFF, 0xFF, 0xFF, 0xFF};
6928 static const BYTE unknown2
[16] =
6929 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6932 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6933 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6934 debugstr_w(szProgIDName
));
6936 /* Create a CompObj stream */
6937 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6938 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6942 /* Write CompObj Structure to stream */
6943 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6945 if( SUCCEEDED( r
) )
6946 r
= WriteClassStm( pstm
, clsid
);
6948 if( SUCCEEDED( r
) )
6949 r
= STREAM_WriteString( pstm
, lpszUserType
);
6950 if( SUCCEEDED( r
) )
6951 r
= STREAM_WriteString( pstm
, szClipName
);
6952 if( SUCCEEDED( r
) )
6953 r
= STREAM_WriteString( pstm
, szProgIDName
);
6954 if( SUCCEEDED( r
) )
6955 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6957 IStream_Release( pstm
);
6962 /***********************************************************************
6963 * WriteFmtUserTypeStg (OLE32.@)
6965 HRESULT WINAPI
WriteFmtUserTypeStg(
6966 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6969 WCHAR szwClipName
[0x40];
6970 CLSID clsid
= CLSID_NULL
;
6971 LPWSTR wstrProgID
= NULL
;
6974 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6976 /* get the clipboard format name */
6977 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
6980 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6982 /* FIXME: There's room to save a CLSID and its ProgID, but
6983 the CLSID is not looked up in the registry and in all the
6984 tests I wrote it was CLSID_NULL. Where does it come from?
6987 /* get the real program ID. This may fail, but that's fine */
6988 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6990 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6992 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6993 lpszUserType
, szwClipName
, wstrProgID
);
6995 CoTaskMemFree(wstrProgID
);
7001 /******************************************************************************
7002 * ReadFmtUserTypeStg [OLE32.@]
7004 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7008 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7009 unsigned char unknown1
[12];
7010 unsigned char unknown2
[16];
7012 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7015 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7017 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7018 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7021 WARN("Failed to open stream r = %08x\n", r
);
7025 /* read the various parts of the structure */
7026 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7027 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7029 r
= ReadClassStm( stm
, &clsid
);
7033 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7037 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7041 r
= STREAM_ReadString( stm
, &szProgIDName
);
7045 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7046 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7049 /* ok, success... now we just need to store what we found */
7051 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7052 CoTaskMemFree( szOleTypeName
);
7054 if( lplpszUserType
)
7055 *lplpszUserType
= szCLSIDName
;
7056 CoTaskMemFree( szProgIDName
);
7059 IStream_Release( stm
);
7065 /*************************************************************************
7066 * OLECONVERT_CreateCompObjStream [Internal]
7068 * Creates a "\001CompObj" is the destination IStorage if necessary.
7071 * pStorage [I] The dest IStorage to create the CompObj Stream
7073 * strOleTypeName [I] The ProgID
7077 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7080 * This function is used by OleConvertOLESTREAMToIStorage only.
7082 * The stream data is stored in the OLESTREAM and there should be
7083 * no need to recreate the stream. If the stream is manually
7084 * deleted it will attempt to create it by querying the registry.
7088 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7091 HRESULT hStorageRes
, hRes
= S_OK
;
7092 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7093 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7094 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7096 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7097 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7099 /* Initialize the CompObj structure */
7100 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7101 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7102 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7105 /* Create a CompObj stream if it doesn't exist */
7106 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7107 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7108 if(hStorageRes
== S_OK
)
7110 /* copy the OleTypeName to the compobj struct */
7111 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7112 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7114 /* copy the OleTypeName to the compobj struct */
7115 /* Note: in the test made, these were Identical */
7116 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7117 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7120 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7121 bufferW
, OLESTREAM_MAX_STR_LEN
);
7122 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7128 /* Get the CLSID Default Name from the Registry */
7129 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7130 if(hErr
== ERROR_SUCCESS
)
7132 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7133 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7134 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7135 if(hErr
== ERROR_SUCCESS
)
7137 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7143 /* Write CompObj Structure to stream */
7144 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7146 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7148 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7149 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7151 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7153 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7154 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7156 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7158 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7159 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7161 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7163 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7164 IStream_Release(pStream
);
7170 /*************************************************************************
7171 * OLECONVERT_CreateOlePresStream[Internal]
7173 * Creates the "\002OlePres000" Stream with the Metafile data
7176 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7177 * dwExtentX [I] Width of the Metafile
7178 * dwExtentY [I] Height of the Metafile
7179 * pData [I] Metafile data
7180 * dwDataLength [I] Size of the Metafile data
7184 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7187 * This function is used by OleConvertOLESTREAMToIStorage only.
7190 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7194 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7195 BYTE pOlePresStreamHeader
[] =
7197 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7198 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7199 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7200 0x00, 0x00, 0x00, 0x00
7203 BYTE pOlePresStreamHeaderEmpty
[] =
7205 0x00, 0x00, 0x00, 0x00,
7206 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7207 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7208 0x00, 0x00, 0x00, 0x00
7211 /* Create the OlePres000 Stream */
7212 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7213 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7218 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7220 memset(&OlePres
, 0, sizeof(OlePres
));
7221 /* Do we have any metafile data to save */
7222 if(dwDataLength
> 0)
7224 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7225 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7229 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7230 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7232 /* Set width and height of the metafile */
7233 OlePres
.dwExtentX
= dwExtentX
;
7234 OlePres
.dwExtentY
= -dwExtentY
;
7236 /* Set Data and Length */
7237 if(dwDataLength
> sizeof(METAFILEPICT16
))
7239 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7240 OlePres
.pData
= &(pData
[8]);
7242 /* Save OlePres000 Data to Stream */
7243 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7244 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7245 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7246 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7247 if(OlePres
.dwSize
> 0)
7249 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7251 IStream_Release(pStream
);
7255 /*************************************************************************
7256 * OLECONVERT_CreateOle10NativeStream [Internal]
7258 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7261 * pStorage [I] Dest storage to create the stream in
7262 * pData [I] Ole10 Native Data (ex. bmp)
7263 * dwDataLength [I] Size of the Ole10 Native Data
7269 * This function is used by OleConvertOLESTREAMToIStorage only.
7271 * Might need to verify the data and return appropriate error message
7274 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7278 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7280 /* Create the Ole10Native Stream */
7281 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7282 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7286 /* Write info to stream */
7287 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7288 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7289 IStream_Release(pStream
);
7294 /*************************************************************************
7295 * OLECONVERT_GetOLE10ProgID [Internal]
7297 * Finds the ProgID (or OleTypeID) from the IStorage
7300 * pStorage [I] The Src IStorage to get the ProgID
7301 * strProgID [I] the ProgID string to get
7302 * dwSize [I] the size of the string
7306 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7309 * This function is used by OleConvertIStorageToOLESTREAM only.
7313 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7317 LARGE_INTEGER iSeekPos
;
7318 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7319 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7321 /* Open the CompObj Stream */
7322 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7323 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7327 /*Get the OleType from the CompObj Stream */
7328 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7329 iSeekPos
.u
.HighPart
= 0;
7331 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7332 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7333 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7334 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7335 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7336 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7337 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7339 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7342 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7344 IStream_Release(pStream
);
7349 LPOLESTR wstrProgID
;
7351 /* Get the OleType from the registry */
7352 REFCLSID clsid
= &(stat
.clsid
);
7353 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7354 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7357 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7364 /*************************************************************************
7365 * OLECONVERT_GetOle10PresData [Internal]
7367 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7370 * pStorage [I] Src IStroage
7371 * pOleStream [I] Dest OleStream Mem Struct
7377 * This function is used by OleConvertIStorageToOLESTREAM only.
7379 * Memory allocated for pData must be freed by the caller
7383 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7388 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7390 /* Initialize Default data for OLESTREAM */
7391 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7392 pOleStreamData
[0].dwTypeID
= 2;
7393 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7394 pOleStreamData
[1].dwTypeID
= 0;
7395 pOleStreamData
[0].dwMetaFileWidth
= 0;
7396 pOleStreamData
[0].dwMetaFileHeight
= 0;
7397 pOleStreamData
[0].pData
= NULL
;
7398 pOleStreamData
[1].pData
= NULL
;
7400 /* Open Ole10Native Stream */
7401 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7402 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7406 /* Read Size and Data */
7407 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7408 if(pOleStreamData
->dwDataLength
> 0)
7410 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7411 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7413 IStream_Release(pStream
);
7419 /*************************************************************************
7420 * OLECONVERT_GetOle20PresData[Internal]
7422 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7425 * pStorage [I] Src IStroage
7426 * pOleStreamData [I] Dest OleStream Mem Struct
7432 * This function is used by OleConvertIStorageToOLESTREAM only.
7434 * Memory allocated for pData must be freed by the caller
7436 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7440 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7441 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7443 /* Initialize Default data for OLESTREAM */
7444 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7445 pOleStreamData
[0].dwTypeID
= 2;
7446 pOleStreamData
[0].dwMetaFileWidth
= 0;
7447 pOleStreamData
[0].dwMetaFileHeight
= 0;
7448 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7449 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7450 pOleStreamData
[1].dwTypeID
= 0;
7451 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7452 pOleStreamData
[1].strOleTypeName
[0] = 0;
7453 pOleStreamData
[1].dwMetaFileWidth
= 0;
7454 pOleStreamData
[1].dwMetaFileHeight
= 0;
7455 pOleStreamData
[1].pData
= NULL
;
7456 pOleStreamData
[1].dwDataLength
= 0;
7459 /* Open OlePress000 stream */
7460 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7461 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7464 LARGE_INTEGER iSeekPos
;
7465 METAFILEPICT16 MetaFilePict
;
7466 static const char strMetafilePictName
[] = "METAFILEPICT";
7468 /* Set the TypeID for a Metafile */
7469 pOleStreamData
[1].dwTypeID
= 5;
7471 /* Set the OleTypeName to Metafile */
7472 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7473 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7475 iSeekPos
.u
.HighPart
= 0;
7476 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7478 /* Get Presentation Data */
7479 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7480 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7481 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7482 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7484 /*Set width and Height */
7485 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7486 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7487 if(olePress
.dwSize
> 0)
7490 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7492 /* Set MetaFilePict struct */
7493 MetaFilePict
.mm
= 8;
7494 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7495 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7496 MetaFilePict
.hMF
= 0;
7498 /* Get Metafile Data */
7499 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7500 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7501 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7503 IStream_Release(pStream
);
7507 /*************************************************************************
7508 * OleConvertOLESTREAMToIStorage [OLE32.@]
7513 * DVTARGETDEVICE parameter is not handled
7514 * Still unsure of some mem fields for OLE 10 Stream
7515 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7516 * and "\001OLE" streams
7519 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7520 LPOLESTREAM pOleStream
,
7522 const DVTARGETDEVICE
* ptd
)
7526 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7528 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7530 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7534 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7537 if(pstg
== NULL
|| pOleStream
== NULL
)
7539 hRes
= E_INVALIDARG
;
7544 /* Load the OLESTREAM to Memory */
7545 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7550 /* Load the OLESTREAM to Memory (part 2)*/
7551 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7557 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7559 /* Do we have the IStorage Data in the OLESTREAM */
7560 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7562 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7563 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7567 /* It must be an original OLE 1.0 source */
7568 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7573 /* It must be an original OLE 1.0 source */
7574 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7577 /* Create CompObj Stream if necessary */
7578 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7581 /*Create the Ole Stream if necessary */
7582 OLECONVERT_CreateOleStream(pstg
);
7587 /* Free allocated memory */
7588 for(i
=0; i
< 2; i
++)
7590 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7591 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7592 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7597 /*************************************************************************
7598 * OleConvertIStorageToOLESTREAM [OLE32.@]
7605 * Still unsure of some mem fields for OLE 10 Stream
7606 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7607 * and "\001OLE" streams.
7610 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7612 LPOLESTREAM pOleStream
)
7615 HRESULT hRes
= S_OK
;
7617 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7618 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7620 TRACE("%p %p\n", pstg
, pOleStream
);
7622 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7624 if(pstg
== NULL
|| pOleStream
== NULL
)
7626 hRes
= E_INVALIDARG
;
7630 /* Get the ProgID */
7631 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7632 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7636 /* Was it originally Ole10 */
7637 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7640 IStream_Release(pStream
);
7641 /* Get Presentation Data for Ole10Native */
7642 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7646 /* Get Presentation Data (OLE20) */
7647 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7650 /* Save OLESTREAM */
7651 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7654 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7659 /* Free allocated memory */
7660 for(i
=0; i
< 2; i
++)
7662 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7668 /***********************************************************************
7669 * GetConvertStg (OLE32.@)
7671 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7672 FIXME("unimplemented stub!\n");
7676 /******************************************************************************
7677 * StgIsStorageFile [OLE32.@]
7678 * Verify if the file contains a storage object
7684 * S_OK if file has magic bytes as a storage object
7685 * S_FALSE if file is not storage
7688 StgIsStorageFile(LPCOLESTR fn
)
7694 TRACE("%s\n", debugstr_w(fn
));
7695 hf
= CreateFileW(fn
, GENERIC_READ
,
7696 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7697 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7699 if (hf
== INVALID_HANDLE_VALUE
)
7700 return STG_E_FILENOTFOUND
;
7702 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7704 WARN(" unable to read file\n");
7711 if (bytes_read
!= 8) {
7712 WARN(" too short\n");
7716 if (!memcmp(magic
,STORAGE_magic
,8)) {
7721 WARN(" -> Invalid header.\n");
7725 /***********************************************************************
7726 * WriteClassStm (OLE32.@)
7728 * Writes a CLSID to a stream.
7731 * pStm [I] Stream to write to.
7732 * rclsid [I] CLSID to write.
7736 * Failure: HRESULT code.
7738 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7740 TRACE("(%p,%p)\n",pStm
,rclsid
);
7742 if (!pStm
|| !rclsid
)
7743 return E_INVALIDARG
;
7745 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7748 /***********************************************************************
7749 * ReadClassStm (OLE32.@)
7751 * Reads a CLSID from a stream.
7754 * pStm [I] Stream to read from.
7755 * rclsid [O] CLSID to read.
7759 * Failure: HRESULT code.
7761 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7766 TRACE("(%p,%p)\n",pStm
,pclsid
);
7768 if (!pStm
|| !pclsid
)
7769 return E_INVALIDARG
;
7771 /* clear the output args */
7772 *pclsid
= CLSID_NULL
;
7774 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7779 if (nbByte
!= sizeof(CLSID
))
7780 return STG_E_READFAULT
;