2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry
;
86 StorageBaseImpl
*parentStorage
;
88 typedef struct StorageInternalImpl StorageInternalImpl
;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
92 DWORD openFlags
, DirRef storageDirEntry
);
93 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
94 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
95 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
96 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
98 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
99 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
101 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
102 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
103 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
104 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
107 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
108 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
109 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
111 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
112 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
113 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
114 ULONG blockIndex
, ULONG offset
, DWORD value
);
115 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
116 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
118 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
119 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base
;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl
*snapshot
;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl
*transactedParent
;
137 } TransactedSnapshotImpl
;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength
;
149 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
150 CHAR
*pstrOleObjFileName
;
151 DWORD dwOleObjFileNameLength
;
152 DWORD dwMetaFileWidth
;
153 DWORD dwMetaFileHeight
;
154 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA
;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength
;
166 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
167 DWORD dwOleTypeNameLength
;
168 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
169 DWORD dwProgIDNameLength
;
170 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
172 }OLECONVERT_ISTORAGE_COMPOBJ
;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES
;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT
deleteStorageContents(
192 StorageBaseImpl
*parentStorage
,
193 DirRef indexToDelete
,
194 DirEntry entryDataToDelete
);
196 static HRESULT
deleteStreamContents(
197 StorageBaseImpl
*parentStorage
,
198 DirRef indexToDelete
,
199 DirEntry entryDataToDelete
);
201 static HRESULT
removeFromTree(
202 StorageBaseImpl
*This
,
203 DirRef parentStorageIndex
,
204 DirRef deletedIndex
);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT
insertIntoTree(
211 StorageBaseImpl
*This
,
212 DirRef parentStorageIndex
,
213 DirRef newEntryIndex
);
215 static LONG
entryNameCmp(
216 const OLECHAR
*name1
,
217 const OLECHAR
*name2
);
219 static DirRef
findElement(
220 StorageBaseImpl
*storage
,
225 static HRESULT
findTreeParent(
226 StorageBaseImpl
*storage
,
228 const OLECHAR
*childName
,
229 DirEntry
*parentData
,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT
validateSTGM(DWORD stgmValue
);
238 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
239 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
240 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref
; /* Reference count */
258 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
259 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
262 * The current implementation of the IEnumSTATSTGImpl class uses a stack
263 * to walk the directory entries to get the content of a storage. This stack
264 * is implemented by the following 3 data members
268 DirRef
* stackToVisit
;
270 #define ENUMSTATSGT_SIZE_INCREMENT 10
274 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
275 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
276 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, DirRef nodeToPush
);
277 static DirRef
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
279 /************************************************************************
283 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
285 if (index
== 0xffffffff)
290 return index
* BIG_BLOCK_SIZE
;
293 /************************************************************************
294 ** Storage32BaseImpl implementation
296 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
297 ULARGE_INTEGER offset
,
302 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
305 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
306 ULARGE_INTEGER offset
,
311 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
314 /************************************************************************
315 * Storage32BaseImpl_QueryInterface (IUnknown)
317 * This method implements the common QueryInterface for all IStorage32
318 * implementations contained in this file.
320 * See Windows documentation for more details on IUnknown methods.
322 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
327 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
329 if ( (This
==0) || (ppvObject
==0) )
334 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
335 IsEqualGUID(&IID_IStorage
, riid
))
339 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
341 *ppvObject
= &This
->pssVtbl
;
345 return E_NOINTERFACE
;
347 IStorage_AddRef(iface
);
352 /************************************************************************
353 * Storage32BaseImpl_AddRef (IUnknown)
355 * This method implements the common AddRef for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI
StorageBaseImpl_AddRef(
363 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
364 ULONG ref
= InterlockedIncrement(&This
->ref
);
366 TRACE("(%p) AddRef to %d\n", This
, ref
);
371 /************************************************************************
372 * Storage32BaseImpl_Release (IUnknown)
374 * This method implements the common Release for all IStorage32
375 * implementations contained in this file.
377 * See Windows documentation for more details on IUnknown methods.
379 static ULONG WINAPI
StorageBaseImpl_Release(
382 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
384 ULONG ref
= InterlockedDecrement(&This
->ref
);
386 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
391 * Since we are using a system of base-classes, we want to call the
392 * destructor of the appropriate derived class. To do this, we are
393 * using virtual functions to implement the destructor.
395 StorageBaseImpl_Destroy(This
);
401 /************************************************************************
402 * Storage32BaseImpl_OpenStream (IStorage)
404 * This method will open the specified stream object from the current storage.
406 * See Windows documentation for more details on IStorage methods.
408 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
410 const OLECHAR
* pwcsName
, /* [string][in] */
411 void* reserved1
, /* [unique][in] */
412 DWORD grfMode
, /* [in] */
413 DWORD reserved2
, /* [in] */
414 IStream
** ppstm
) /* [out] */
416 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
417 StgStreamImpl
* newStream
;
418 DirEntry currentEntry
;
419 DirRef streamEntryRef
;
420 HRESULT res
= STG_E_UNKNOWN
;
422 TRACE("(%p, %s, %p, %x, %d, %p)\n",
423 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
425 if ( (pwcsName
==NULL
) || (ppstm
==0) )
433 if ( FAILED( validateSTGM(grfMode
) ) ||
434 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
436 res
= STG_E_INVALIDFLAG
;
443 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
445 res
= STG_E_INVALIDFUNCTION
;
451 res
= STG_E_REVERTED
;
456 * Check that we're compatible with the parent's storage mode, but
457 * only if we are not in transacted mode
459 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
460 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
462 res
= STG_E_ACCESSDENIED
;
468 * Search for the element with the given name
470 streamEntryRef
= findElement(
472 This
->storageDirEntry
,
477 * If it was found, construct the stream object and return a pointer to it.
479 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
480 (currentEntry
.stgType
==STGTY_STREAM
) )
482 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
484 /* A single stream cannot be opened a second time. */
485 res
= STG_E_ACCESSDENIED
;
489 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
493 newStream
->grfMode
= grfMode
;
494 *ppstm
= (IStream
*)newStream
;
496 IStream_AddRef(*ppstm
);
506 res
= STG_E_FILENOTFOUND
;
510 TRACE("<-- IStream %p\n", *ppstm
);
511 TRACE("<-- %08x\n", res
);
515 /************************************************************************
516 * Storage32BaseImpl_OpenStorage (IStorage)
518 * This method will open a new storage object from the current storage.
520 * See Windows documentation for more details on IStorage methods.
522 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
524 const OLECHAR
* pwcsName
, /* [string][unique][in] */
525 IStorage
* pstgPriority
, /* [unique][in] */
526 DWORD grfMode
, /* [in] */
527 SNB snbExclude
, /* [unique][in] */
528 DWORD reserved
, /* [in] */
529 IStorage
** ppstg
) /* [out] */
531 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
532 StorageInternalImpl
* newStorage
;
533 StorageBaseImpl
* newTransactedStorage
;
534 DirEntry currentEntry
;
535 DirRef storageEntryRef
;
536 HRESULT res
= STG_E_UNKNOWN
;
538 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
539 iface
, debugstr_w(pwcsName
), pstgPriority
,
540 grfMode
, snbExclude
, reserved
, ppstg
);
542 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
548 if (This
->openFlags
& STGM_SIMPLE
)
550 res
= STG_E_INVALIDFUNCTION
;
555 if (snbExclude
!= NULL
)
557 res
= STG_E_INVALIDPARAMETER
;
561 if ( FAILED( validateSTGM(grfMode
) ))
563 res
= STG_E_INVALIDFLAG
;
570 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
571 (grfMode
& STGM_DELETEONRELEASE
) ||
572 (grfMode
& STGM_PRIORITY
) )
574 res
= STG_E_INVALIDFUNCTION
;
579 return STG_E_REVERTED
;
582 * Check that we're compatible with the parent's storage mode,
583 * but only if we are not transacted
585 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
586 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
588 res
= STG_E_ACCESSDENIED
;
595 storageEntryRef
= findElement(
597 This
->storageDirEntry
,
601 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
602 (currentEntry
.stgType
==STGTY_STORAGE
) )
604 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
606 /* A single storage cannot be opened a second time. */
607 res
= STG_E_ACCESSDENIED
;
611 newStorage
= StorageInternalImpl_Construct(
618 if (grfMode
& STGM_TRANSACTED
)
620 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
624 HeapFree(GetProcessHeap(), 0, newStorage
);
628 *ppstg
= (IStorage
*)newTransactedStorage
;
632 *ppstg
= (IStorage
*)newStorage
;
635 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
641 res
= STG_E_INSUFFICIENTMEMORY
;
645 res
= STG_E_FILENOTFOUND
;
648 TRACE("<-- %08x\n", res
);
652 /************************************************************************
653 * Storage32BaseImpl_EnumElements (IStorage)
655 * This method will create an enumerator object that can be used to
656 * retrieve information about all the elements in the storage object.
658 * See Windows documentation for more details on IStorage methods.
660 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
662 DWORD reserved1
, /* [in] */
663 void* reserved2
, /* [size_is][unique][in] */
664 DWORD reserved3
, /* [in] */
665 IEnumSTATSTG
** ppenum
) /* [out] */
667 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
668 IEnumSTATSTGImpl
* newEnum
;
670 TRACE("(%p, %d, %p, %d, %p)\n",
671 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
673 if ( (This
==0) || (ppenum
==0))
677 return STG_E_REVERTED
;
679 newEnum
= IEnumSTATSTGImpl_Construct(
681 This
->storageDirEntry
);
685 *ppenum
= (IEnumSTATSTG
*)newEnum
;
687 IEnumSTATSTG_AddRef(*ppenum
);
692 return E_OUTOFMEMORY
;
695 /************************************************************************
696 * Storage32BaseImpl_Stat (IStorage)
698 * This method will retrieve information about this storage object.
700 * See Windows documentation for more details on IStorage methods.
702 static HRESULT WINAPI
StorageBaseImpl_Stat(
704 STATSTG
* pstatstg
, /* [out] */
705 DWORD grfStatFlag
) /* [in] */
707 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
708 DirEntry currentEntry
;
709 HRESULT res
= STG_E_UNKNOWN
;
711 TRACE("(%p, %p, %x)\n",
712 iface
, pstatstg
, grfStatFlag
);
714 if ( (This
==0) || (pstatstg
==0))
722 res
= STG_E_REVERTED
;
726 res
= StorageBaseImpl_ReadDirEntry(
728 This
->storageDirEntry
,
733 StorageUtl_CopyDirEntryToSTATSTG(
739 pstatstg
->grfMode
= This
->openFlags
;
740 pstatstg
->grfStateBits
= This
->stateBits
;
746 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
);
748 TRACE("<-- %08x\n", res
);
752 /************************************************************************
753 * Storage32BaseImpl_RenameElement (IStorage)
755 * This method will rename the specified element.
757 * See Windows documentation for more details on IStorage methods.
759 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
761 const OLECHAR
* pwcsOldName
, /* [in] */
762 const OLECHAR
* pwcsNewName
) /* [in] */
764 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
765 DirEntry currentEntry
;
766 DirRef currentEntryRef
;
768 TRACE("(%p, %s, %s)\n",
769 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
772 return STG_E_REVERTED
;
774 currentEntryRef
= findElement(This
,
775 This
->storageDirEntry
,
779 if (currentEntryRef
!= DIRENTRY_NULL
)
782 * There is already an element with the new name
784 return STG_E_FILEALREADYEXISTS
;
788 * Search for the old element name
790 currentEntryRef
= findElement(This
,
791 This
->storageDirEntry
,
795 if (currentEntryRef
!= DIRENTRY_NULL
)
797 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
798 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
800 WARN("Element is already open; cannot rename.\n");
801 return STG_E_ACCESSDENIED
;
804 /* Remove the element from its current position in the tree */
805 removeFromTree(This
, This
->storageDirEntry
,
808 /* Change the name of the element */
809 strcpyW(currentEntry
.name
, pwcsNewName
);
811 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
814 /* Insert the element in a new position in the tree */
815 insertIntoTree(This
, This
->storageDirEntry
,
821 * There is no element with the old name
823 return STG_E_FILENOTFOUND
;
829 /************************************************************************
830 * Storage32BaseImpl_CreateStream (IStorage)
832 * This method will create a stream object within this storage
834 * See Windows documentation for more details on IStorage methods.
836 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
838 const OLECHAR
* pwcsName
, /* [string][in] */
839 DWORD grfMode
, /* [in] */
840 DWORD reserved1
, /* [in] */
841 DWORD reserved2
, /* [in] */
842 IStream
** ppstm
) /* [out] */
844 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
845 StgStreamImpl
* newStream
;
846 DirEntry currentEntry
, newStreamEntry
;
847 DirRef currentEntryRef
, newStreamEntryRef
;
849 TRACE("(%p, %s, %x, %d, %d, %p)\n",
850 iface
, debugstr_w(pwcsName
), grfMode
,
851 reserved1
, reserved2
, ppstm
);
854 return STG_E_INVALIDPOINTER
;
857 return STG_E_INVALIDNAME
;
859 if (reserved1
|| reserved2
)
860 return STG_E_INVALIDPARAMETER
;
862 if ( FAILED( validateSTGM(grfMode
) ))
863 return STG_E_INVALIDFLAG
;
865 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
866 return STG_E_INVALIDFLAG
;
869 return STG_E_REVERTED
;
874 if ((grfMode
& STGM_DELETEONRELEASE
) ||
875 (grfMode
& STGM_TRANSACTED
))
876 return STG_E_INVALIDFUNCTION
;
878 /* Can't create a stream on read-only storage */
879 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
880 return STG_E_ACCESSDENIED
;
883 * Check that we're compatible with the parent's storage mode
884 * if not in transacted mode
886 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
887 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
888 return STG_E_ACCESSDENIED
;
891 if(This
->openFlags
& STGM_SIMPLE
)
892 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
896 currentEntryRef
= findElement(This
,
897 This
->storageDirEntry
,
901 if (currentEntryRef
!= DIRENTRY_NULL
)
904 * An element with this name already exists
906 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
908 IStorage_DestroyElement(iface
, pwcsName
);
911 return STG_E_FILEALREADYEXISTS
;
913 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
915 WARN("read-only storage\n");
916 return STG_E_ACCESSDENIED
;
920 * memset the empty entry
922 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
924 newStreamEntry
.sizeOfNameString
=
925 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
927 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
928 return STG_E_INVALIDNAME
;
930 strcpyW(newStreamEntry
.name
, pwcsName
);
932 newStreamEntry
.stgType
= STGTY_STREAM
;
933 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
934 newStreamEntry
.size
.u
.LowPart
= 0;
935 newStreamEntry
.size
.u
.HighPart
= 0;
937 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
938 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
939 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
941 /* call CoFileTime to get the current time
946 /* newStreamEntry.clsid */
949 * Create an entry with the new data
951 StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
953 * Insert the new entry in the parent storage's tree.
957 This
->storageDirEntry
,
961 * Open the stream to return it.
963 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
967 *ppstm
= (IStream
*)newStream
;
969 IStream_AddRef(*ppstm
);
973 return STG_E_INSUFFICIENTMEMORY
;
979 /************************************************************************
980 * Storage32BaseImpl_SetClass (IStorage)
982 * This method will write the specified CLSID in the directory entry of this
985 * See Windows documentation for more details on IStorage methods.
987 static HRESULT WINAPI
StorageBaseImpl_SetClass(
989 REFCLSID clsid
) /* [in] */
991 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
993 DirEntry currentEntry
;
995 TRACE("(%p, %p)\n", iface
, clsid
);
998 return STG_E_REVERTED
;
1000 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1001 This
->storageDirEntry
,
1003 if (SUCCEEDED(hRes
))
1005 currentEntry
.clsid
= *clsid
;
1007 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1008 This
->storageDirEntry
,
1015 /************************************************************************
1016 ** Storage32Impl implementation
1019 /************************************************************************
1020 * Storage32BaseImpl_CreateStorage (IStorage)
1022 * This method will create the storage object within the provided storage.
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1028 const OLECHAR
*pwcsName
, /* [string][in] */
1029 DWORD grfMode
, /* [in] */
1030 DWORD reserved1
, /* [in] */
1031 DWORD reserved2
, /* [in] */
1032 IStorage
**ppstg
) /* [out] */
1034 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1036 DirEntry currentEntry
;
1038 DirRef currentEntryRef
;
1042 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1043 iface
, debugstr_w(pwcsName
), grfMode
,
1044 reserved1
, reserved2
, ppstg
);
1047 return STG_E_INVALIDPOINTER
;
1049 if (This
->openFlags
& STGM_SIMPLE
)
1051 return STG_E_INVALIDFUNCTION
;
1055 return STG_E_INVALIDNAME
;
1059 if ( FAILED( validateSTGM(grfMode
) ) ||
1060 (grfMode
& STGM_DELETEONRELEASE
) )
1062 WARN("bad grfMode: 0x%x\n", grfMode
);
1063 return STG_E_INVALIDFLAG
;
1067 return STG_E_REVERTED
;
1070 * Check that we're compatible with the parent's storage mode
1072 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1074 WARN("access denied\n");
1075 return STG_E_ACCESSDENIED
;
1078 currentEntryRef
= findElement(This
,
1079 This
->storageDirEntry
,
1083 if (currentEntryRef
!= DIRENTRY_NULL
)
1086 * An element with this name already exists
1088 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1089 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
)
1091 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS
;
1101 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1103 WARN("read-only storage\n");
1104 return STG_E_ACCESSDENIED
;
1107 memset(&newEntry
, 0, sizeof(DirEntry
));
1109 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1111 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1113 FIXME("name too long\n");
1114 return STG_E_INVALIDNAME
;
1117 strcpyW(newEntry
.name
, pwcsName
);
1119 newEntry
.stgType
= STGTY_STORAGE
;
1120 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1121 newEntry
.size
.u
.LowPart
= 0;
1122 newEntry
.size
.u
.HighPart
= 0;
1124 newEntry
.leftChild
= DIRENTRY_NULL
;
1125 newEntry
.rightChild
= DIRENTRY_NULL
;
1126 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1128 /* call CoFileTime to get the current time
1133 /* newEntry.clsid */
1136 * Create a new directory entry for the storage
1138 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1141 * Insert the new directory entry into the parent storage's tree
1145 This
->storageDirEntry
,
1149 * Open it to get a pointer to return.
1151 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1153 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1163 /***************************************************************************
1167 * Reserve a directory entry in the file and initialize it.
1169 static HRESULT
StorageImpl_CreateDirEntry(
1170 StorageBaseImpl
*base
,
1171 const DirEntry
*newData
,
1174 StorageImpl
*storage
= (StorageImpl
*)base
;
1175 ULONG currentEntryIndex
= 0;
1176 ULONG newEntryIndex
= DIRENTRY_NULL
;
1178 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1179 WORD sizeOfNameString
;
1183 hr
= StorageImpl_ReadRawDirEntry(storage
,
1189 StorageUtl_ReadWord(
1191 OFFSET_PS_NAMELENGTH
,
1194 if (sizeOfNameString
== 0)
1197 * The entry exists and is available, we found it.
1199 newEntryIndex
= currentEntryIndex
;
1205 * We exhausted the directory entries, we will create more space below
1207 newEntryIndex
= currentEntryIndex
;
1209 currentEntryIndex
++;
1211 } while (newEntryIndex
== DIRENTRY_NULL
);
1214 * grow the directory stream
1218 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1219 ULARGE_INTEGER newSize
;
1221 ULONG lastEntry
= 0;
1222 ULONG blockCount
= 0;
1225 * obtain the new count of blocks in the directory stream
1227 blockCount
= BlockChainStream_GetCount(
1228 storage
->rootBlockChain
)+1;
1231 * initialize the size used by the directory stream
1233 newSize
.u
.HighPart
= 0;
1234 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1237 * add a block to the directory stream
1239 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1242 * memset the empty entry in order to initialize the unused newly
1245 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1250 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1253 entryIndex
= newEntryIndex
+ 1;
1254 entryIndex
< lastEntry
;
1257 StorageImpl_WriteRawDirEntry(
1264 UpdateRawDirEntry(currentData
, newData
);
1266 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1269 *index
= newEntryIndex
;
1274 /***************************************************************************
1278 * Mark a directory entry in the file as free.
1280 static HRESULT
StorageImpl_DestroyDirEntry(
1281 StorageBaseImpl
*base
,
1285 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1286 StorageImpl
*storage
= (StorageImpl
*)base
;
1288 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1290 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1296 /***************************************************************************
1300 * Destroy an entry, its attached data, and all entries reachable from it.
1302 static HRESULT
DestroyReachableEntries(
1303 StorageBaseImpl
*base
,
1308 ULARGE_INTEGER zero
;
1312 if (index
!= DIRENTRY_NULL
)
1314 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1317 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1320 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1323 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1326 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1329 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1336 /****************************************************************************
1340 * Case insensitive comparison of DirEntry.name by first considering
1343 * Returns <0 when name1 < name2
1344 * >0 when name1 > name2
1345 * 0 when name1 == name2
1347 static LONG
entryNameCmp(
1348 const OLECHAR
*name1
,
1349 const OLECHAR
*name2
)
1351 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1356 * We compare the string themselves only when they are of the same length
1358 diff
= lstrcmpiW( name1
, name2
);
1364 /****************************************************************************
1368 * Add a directory entry to a storage
1370 static HRESULT
insertIntoTree(
1371 StorageBaseImpl
*This
,
1372 DirRef parentStorageIndex
,
1373 DirRef newEntryIndex
)
1375 DirEntry currentEntry
;
1379 * Read the inserted entry
1381 StorageBaseImpl_ReadDirEntry(This
,
1386 * Read the storage entry
1388 StorageBaseImpl_ReadDirEntry(This
,
1392 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1395 * The root storage contains some element, therefore, start the research
1396 * for the appropriate location.
1399 DirRef current
, next
, previous
, currentEntryId
;
1402 * Keep a reference to the root of the storage's element tree
1404 currentEntryId
= currentEntry
.dirRootEntry
;
1409 StorageBaseImpl_ReadDirEntry(This
,
1410 currentEntry
.dirRootEntry
,
1413 previous
= currentEntry
.leftChild
;
1414 next
= currentEntry
.rightChild
;
1415 current
= currentEntryId
;
1419 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1423 if (previous
!= DIRENTRY_NULL
)
1425 StorageBaseImpl_ReadDirEntry(This
,
1432 currentEntry
.leftChild
= newEntryIndex
;
1433 StorageBaseImpl_WriteDirEntry(This
,
1441 if (next
!= DIRENTRY_NULL
)
1443 StorageBaseImpl_ReadDirEntry(This
,
1450 currentEntry
.rightChild
= newEntryIndex
;
1451 StorageBaseImpl_WriteDirEntry(This
,
1460 * Trying to insert an item with the same name in the
1461 * subtree structure.
1463 return STG_E_FILEALREADYEXISTS
;
1466 previous
= currentEntry
.leftChild
;
1467 next
= currentEntry
.rightChild
;
1473 * The storage is empty, make the new entry the root of its element tree
1475 currentEntry
.dirRootEntry
= newEntryIndex
;
1476 StorageBaseImpl_WriteDirEntry(This
,
1484 /****************************************************************************
1488 * Find and read the element of a storage with the given name.
1490 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1491 const OLECHAR
*name
, DirEntry
*data
)
1493 DirRef currentEntry
;
1495 /* Read the storage entry to find the root of the tree. */
1496 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1498 currentEntry
= data
->dirRootEntry
;
1500 while (currentEntry
!= DIRENTRY_NULL
)
1504 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1506 cmp
= entryNameCmp(name
, data
->name
);
1513 currentEntry
= data
->leftChild
;
1516 currentEntry
= data
->rightChild
;
1519 return currentEntry
;
1522 /****************************************************************************
1526 * Find and read the binary tree parent of the element with the given name.
1528 * If there is no such element, find a place where it could be inserted and
1529 * return STG_E_FILENOTFOUND.
1531 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1532 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1538 /* Read the storage entry to find the root of the tree. */
1539 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1541 *parentEntry
= storageEntry
;
1542 *relation
= DIRENTRY_RELATION_DIR
;
1544 childEntry
= parentData
->dirRootEntry
;
1546 while (childEntry
!= DIRENTRY_NULL
)
1550 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1552 cmp
= entryNameCmp(childName
, childData
.name
);
1560 *parentData
= childData
;
1561 *parentEntry
= childEntry
;
1562 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1564 childEntry
= parentData
->leftChild
;
1569 *parentData
= childData
;
1570 *parentEntry
= childEntry
;
1571 *relation
= DIRENTRY_RELATION_NEXT
;
1573 childEntry
= parentData
->rightChild
;
1577 if (childEntry
== DIRENTRY_NULL
)
1578 return STG_E_FILENOTFOUND
;
1584 /*************************************************************************
1587 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1589 DWORD ciidExclude
, /* [in] */
1590 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1591 SNB snbExclude
, /* [unique][in] */
1592 IStorage
* pstgDest
) /* [unique][in] */
1594 IEnumSTATSTG
*elements
= 0;
1595 STATSTG curElement
, strStat
;
1597 IStorage
*pstgTmp
, *pstgChild
;
1598 IStream
*pstrTmp
, *pstrChild
;
1599 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1602 TRACE("(%p, %d, %p, %p, %p)\n",
1603 iface
, ciidExclude
, rgiidExclude
,
1604 snbExclude
, pstgDest
);
1606 if ( pstgDest
== 0 )
1607 return STG_E_INVALIDPOINTER
;
1610 * Enumerate the elements
1612 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1620 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1621 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1623 for(i
= 0; i
< ciidExclude
; ++i
)
1625 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1626 skip_storage
= TRUE
;
1627 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1630 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1636 * Obtain the next element
1638 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1640 if ( hr
== S_FALSE
)
1642 hr
= S_OK
; /* done, every element has been copied */
1648 WCHAR
**snb
= snbExclude
;
1650 while ( *snb
!= NULL
&& !skip
)
1652 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1661 if (curElement
.type
== STGTY_STORAGE
)
1667 * open child source storage
1669 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1670 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1671 NULL
, 0, &pstgChild
);
1677 * Check if destination storage is not a child of the source
1678 * storage, which will cause an infinite loop
1680 if (pstgChild
== pstgDest
)
1682 IEnumSTATSTG_Release(elements
);
1684 return STG_E_ACCESSDENIED
;
1688 * create a new storage in destination storage
1690 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1691 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1695 * if it already exist, don't create a new one use this one
1697 if (hr
== STG_E_FILEALREADYEXISTS
)
1699 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1700 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1701 NULL
, 0, &pstgTmp
);
1709 * do the copy recursively
1711 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1714 IStorage_Release( pstgTmp
);
1715 IStorage_Release( pstgChild
);
1717 else if (curElement
.type
== STGTY_STREAM
)
1723 * create a new stream in destination storage. If the stream already
1724 * exist, it will be deleted and a new one will be created.
1726 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1727 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1734 * open child stream storage
1736 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1737 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1744 * Get the size of the source stream
1746 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1749 * Set the size of the destination stream.
1751 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1756 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1759 IStream_Release( pstrTmp
);
1760 IStream_Release( pstrChild
);
1764 WARN("unknown element type: %d\n", curElement
.type
);
1767 } while (hr
== S_OK
);
1772 IEnumSTATSTG_Release(elements
);
1777 /*************************************************************************
1778 * MoveElementTo (IStorage)
1780 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1782 const OLECHAR
*pwcsName
, /* [string][in] */
1783 IStorage
*pstgDest
, /* [unique][in] */
1784 const OLECHAR
*pwcsNewName
,/* [string][in] */
1785 DWORD grfFlags
) /* [in] */
1787 FIXME("(%p %s %p %s %u): stub\n", iface
,
1788 debugstr_w(pwcsName
), pstgDest
,
1789 debugstr_w(pwcsNewName
), grfFlags
);
1793 /*************************************************************************
1796 * Ensures that any changes made to a storage object open in transacted mode
1797 * are reflected in the parent storage
1800 * Wine doesn't implement transacted mode, which seems to be a basic
1801 * optimization, so we can ignore this stub for now.
1803 static HRESULT WINAPI
StorageImpl_Commit(
1805 DWORD grfCommitFlags
)/* [in] */
1807 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1811 /*************************************************************************
1814 * Discard all changes that have been made since the last commit operation
1816 static HRESULT WINAPI
StorageImpl_Revert(
1819 FIXME("(%p): stub\n", iface
);
1823 /*************************************************************************
1824 * DestroyElement (IStorage)
1826 * Strategy: This implementation is built this way for simplicity not for speed.
1827 * I always delete the topmost element of the enumeration and adjust
1828 * the deleted element pointer all the time. This takes longer to
1829 * do but allow to reinvoke DestroyElement whenever we encounter a
1830 * storage object. The optimisation resides in the usage of another
1831 * enumeration strategy that would give all the leaves of a storage
1832 * first. (postfix order)
1834 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1836 const OLECHAR
*pwcsName
)/* [string][in] */
1838 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1841 DirEntry entryToDelete
;
1842 DirRef entryToDeleteRef
;
1845 iface
, debugstr_w(pwcsName
));
1848 return STG_E_INVALIDPOINTER
;
1851 return STG_E_REVERTED
;
1853 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1854 return STG_E_ACCESSDENIED
;
1856 entryToDeleteRef
= findElement(
1858 This
->storageDirEntry
,
1862 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1864 return STG_E_FILENOTFOUND
;
1867 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1869 hr
= deleteStorageContents(
1874 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1876 hr
= deleteStreamContents(
1886 * Remove the entry from its parent storage
1888 hr
= removeFromTree(
1890 This
->storageDirEntry
,
1894 * Invalidate the entry
1897 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1903 /******************************************************************************
1904 * Internal stream list handlers
1907 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1909 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1910 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1913 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1915 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1916 list_remove(&(strm
->StrmListEntry
));
1919 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1921 StgStreamImpl
*strm
;
1923 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1925 if (strm
->dirEntry
== streamEntry
)
1934 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1936 StorageInternalImpl
*childstg
;
1938 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1940 if (childstg
->base
.storageDirEntry
== storageEntry
)
1949 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1951 struct list
*cur
, *cur2
;
1952 StgStreamImpl
*strm
=NULL
;
1953 StorageInternalImpl
*childstg
=NULL
;
1955 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1956 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1957 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1958 strm
->parentStorage
= NULL
;
1962 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1963 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1964 StorageBaseImpl_Invalidate( &childstg
->base
);
1967 if (stg
->transactedChild
)
1969 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1971 stg
->transactedChild
= NULL
;
1976 /*********************************************************************
1980 * Delete the contents of a storage entry.
1983 static HRESULT
deleteStorageContents(
1984 StorageBaseImpl
*parentStorage
,
1985 DirRef indexToDelete
,
1986 DirEntry entryDataToDelete
)
1988 IEnumSTATSTG
*elements
= 0;
1989 IStorage
*childStorage
= 0;
1990 STATSTG currentElement
;
1992 HRESULT destroyHr
= S_OK
;
1993 StorageInternalImpl
*stg
, *stg2
;
1995 /* Invalidate any open storage objects. */
1996 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1998 if (stg
->base
.storageDirEntry
== indexToDelete
)
2000 StorageBaseImpl_Invalidate(&stg
->base
);
2005 * Open the storage and enumerate it
2007 hr
= StorageBaseImpl_OpenStorage(
2008 (IStorage
*)parentStorage
,
2009 entryDataToDelete
.name
,
2011 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2022 * Enumerate the elements
2024 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2029 * Obtain the next element
2031 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2034 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2036 CoTaskMemFree(currentElement
.pwcsName
);
2040 * We need to Reset the enumeration every time because we delete elements
2041 * and the enumeration could be invalid
2043 IEnumSTATSTG_Reset(elements
);
2045 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2047 IStorage_Release(childStorage
);
2048 IEnumSTATSTG_Release(elements
);
2053 /*********************************************************************
2057 * Perform the deletion of a stream's data
2060 static HRESULT
deleteStreamContents(
2061 StorageBaseImpl
*parentStorage
,
2062 DirRef indexToDelete
,
2063 DirEntry entryDataToDelete
)
2067 ULARGE_INTEGER size
;
2068 StgStreamImpl
*strm
, *strm2
;
2070 /* Invalidate any open stream objects. */
2071 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2073 if (strm
->dirEntry
== indexToDelete
)
2075 TRACE("Stream deleted %p\n", strm
);
2076 strm
->parentStorage
= NULL
;
2077 list_remove(&strm
->StrmListEntry
);
2081 size
.u
.HighPart
= 0;
2084 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2085 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2095 hr
= IStream_SetSize(pis
, size
);
2103 * Release the stream object.
2105 IStream_Release(pis
);
2110 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2114 case DIRENTRY_RELATION_PREVIOUS
:
2115 entry
->leftChild
= new_target
;
2117 case DIRENTRY_RELATION_NEXT
:
2118 entry
->rightChild
= new_target
;
2120 case DIRENTRY_RELATION_DIR
:
2121 entry
->dirRootEntry
= new_target
;
2128 /*************************************************************************
2132 * This method removes a directory entry from its parent storage tree without
2133 * freeing any resources attached to it.
2135 static HRESULT
removeFromTree(
2136 StorageBaseImpl
*This
,
2137 DirRef parentStorageIndex
,
2138 DirRef deletedIndex
)
2141 DirEntry entryToDelete
;
2142 DirEntry parentEntry
;
2143 DirRef parentEntryRef
;
2144 ULONG typeOfRelation
;
2146 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2152 * Find the element that links to the one we want to delete.
2154 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2155 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2160 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2163 * Replace the deleted entry with its left child
2165 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2167 hr
= StorageBaseImpl_WriteDirEntry(
2176 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2179 * We need to reinsert the right child somewhere. We already know it and
2180 * its children are greater than everything in the left tree, so we
2181 * insert it at the rightmost point in the left tree.
2183 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2184 DirEntry newRightChildParentEntry
;
2188 hr
= StorageBaseImpl_ReadDirEntry(
2190 newRightChildParent
,
2191 &newRightChildParentEntry
);
2197 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2198 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2199 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2201 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2203 hr
= StorageBaseImpl_WriteDirEntry(
2205 newRightChildParent
,
2206 &newRightChildParentEntry
);
2216 * Replace the deleted entry with its right child
2218 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2220 hr
= StorageBaseImpl_WriteDirEntry(
2234 /******************************************************************************
2235 * SetElementTimes (IStorage)
2237 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2239 const OLECHAR
*pwcsName
,/* [string][in] */
2240 const FILETIME
*pctime
, /* [in] */
2241 const FILETIME
*patime
, /* [in] */
2242 const FILETIME
*pmtime
) /* [in] */
2244 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2248 /******************************************************************************
2249 * SetStateBits (IStorage)
2251 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2253 DWORD grfStateBits
,/* [in] */
2254 DWORD grfMask
) /* [in] */
2256 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2259 return STG_E_REVERTED
;
2261 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2265 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2266 DirRef index
, const DirEntry
*data
)
2268 StorageImpl
*This
= (StorageImpl
*)base
;
2269 return StorageImpl_WriteDirEntry(This
, index
, data
);
2272 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2273 DirRef index
, DirEntry
*data
)
2275 StorageImpl
*This
= (StorageImpl
*)base
;
2276 return StorageImpl_ReadDirEntry(This
, index
, data
);
2279 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2280 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2282 StorageImpl
*This
= (StorageImpl
*)base
;
2287 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2288 if (FAILED(hr
)) return hr
;
2290 if (data
.size
.QuadPart
== 0)
2296 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2298 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2305 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2307 SmallBlockChainStream
*stream
;
2309 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2310 if (!stream
) return E_OUTOFMEMORY
;
2312 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2314 SmallBlockChainStream_Destroy(stream
);
2320 BlockChainStream
*stream
;
2322 stream
= BlockChainStream_Construct(This
, NULL
, index
);
2323 if (!stream
) return E_OUTOFMEMORY
;
2325 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2327 BlockChainStream_Destroy(stream
);
2333 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2334 ULARGE_INTEGER newsize
)
2336 StorageImpl
*This
= (StorageImpl
*)base
;
2339 SmallBlockChainStream
*smallblock
=NULL
;
2340 BlockChainStream
*bigblock
=NULL
;
2342 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2343 if (FAILED(hr
)) return hr
;
2345 /* In simple mode keep the stream size above the small block limit */
2346 if (This
->base
.openFlags
& STGM_SIMPLE
)
2347 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2349 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2352 /* Create a block chain object of the appropriate type */
2353 if (data
.size
.QuadPart
== 0)
2355 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2357 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2358 if (!smallblock
) return E_OUTOFMEMORY
;
2362 bigblock
= BlockChainStream_Construct(This
, NULL
, index
);
2363 if (!bigblock
) return E_OUTOFMEMORY
;
2366 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2368 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2369 if (!smallblock
) return E_OUTOFMEMORY
;
2373 bigblock
= BlockChainStream_Construct(This
, NULL
, index
);
2374 if (!bigblock
) return E_OUTOFMEMORY
;
2377 /* Change the block chain type if necessary. */
2378 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2380 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2383 SmallBlockChainStream_Destroy(smallblock
);
2387 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2389 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, &bigblock
);
2392 BlockChainStream_Destroy(bigblock
);
2397 /* Set the size of the block chain. */
2400 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2401 SmallBlockChainStream_Destroy(smallblock
);
2405 BlockChainStream_SetSize(bigblock
, newsize
);
2406 BlockChainStream_Destroy(bigblock
);
2409 /* Set the size in the directory entry. */
2410 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2413 data
.size
= newsize
;
2415 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2420 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2421 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2423 StorageImpl
*This
= (StorageImpl
*)base
;
2426 ULARGE_INTEGER newSize
;
2428 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2429 if (FAILED(hr
)) return hr
;
2431 /* Grow the stream if necessary */
2432 newSize
.QuadPart
= 0;
2433 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2435 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2437 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2441 data
.size
= newSize
;
2444 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2446 SmallBlockChainStream
*stream
;
2448 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2449 if (!stream
) return E_OUTOFMEMORY
;
2451 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2453 SmallBlockChainStream_Destroy(stream
);
2459 BlockChainStream
*stream
;
2461 stream
= BlockChainStream_Construct(This
, NULL
, index
);
2462 if (!stream
) return E_OUTOFMEMORY
;
2464 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2466 BlockChainStream_Destroy(stream
);
2473 * Virtual function table for the IStorage32Impl class.
2475 static const IStorageVtbl Storage32Impl_Vtbl
=
2477 StorageBaseImpl_QueryInterface
,
2478 StorageBaseImpl_AddRef
,
2479 StorageBaseImpl_Release
,
2480 StorageBaseImpl_CreateStream
,
2481 StorageBaseImpl_OpenStream
,
2482 StorageBaseImpl_CreateStorage
,
2483 StorageBaseImpl_OpenStorage
,
2484 StorageBaseImpl_CopyTo
,
2485 StorageBaseImpl_MoveElementTo
,
2488 StorageBaseImpl_EnumElements
,
2489 StorageBaseImpl_DestroyElement
,
2490 StorageBaseImpl_RenameElement
,
2491 StorageBaseImpl_SetElementTimes
,
2492 StorageBaseImpl_SetClass
,
2493 StorageBaseImpl_SetStateBits
,
2494 StorageBaseImpl_Stat
2497 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2499 StorageImpl_Destroy
,
2500 StorageImpl_Invalidate
,
2501 StorageImpl_CreateDirEntry
,
2502 StorageImpl_BaseWriteDirEntry
,
2503 StorageImpl_BaseReadDirEntry
,
2504 StorageImpl_DestroyDirEntry
,
2505 StorageImpl_StreamReadAt
,
2506 StorageImpl_StreamWriteAt
,
2507 StorageImpl_StreamSetSize
2510 static HRESULT
StorageImpl_Construct(
2517 StorageImpl
** result
)
2521 DirEntry currentEntry
;
2522 DirRef currentEntryRef
;
2523 WCHAR fullpath
[MAX_PATH
];
2525 if ( FAILED( validateSTGM(openFlags
) ))
2526 return STG_E_INVALIDFLAG
;
2528 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2530 return E_OUTOFMEMORY
;
2532 memset(This
, 0, sizeof(StorageImpl
));
2534 list_init(&This
->base
.strmHead
);
2536 list_init(&This
->base
.storageHead
);
2538 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2539 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2540 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2541 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2543 This
->base
.create
= create
;
2545 This
->base
.reverted
= 0;
2547 This
->hFile
= hFile
;
2550 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2552 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2554 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2555 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2556 if (!This
->pwcsName
)
2558 hr
= STG_E_INSUFFICIENTMEMORY
;
2561 strcpyW(This
->pwcsName
, fullpath
);
2562 This
->base
.filename
= This
->pwcsName
;
2566 * Initialize the big block cache.
2568 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2569 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2570 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2576 if (This
->bigBlockFile
== 0)
2584 ULARGE_INTEGER size
;
2585 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2588 * Initialize all header variables:
2589 * - The big block depot consists of one block and it is at block 0
2590 * - The directory table starts at block 1
2591 * - There is no small block depot
2593 memset( This
->bigBlockDepotStart
,
2595 sizeof(This
->bigBlockDepotStart
));
2597 This
->bigBlockDepotCount
= 1;
2598 This
->bigBlockDepotStart
[0] = 0;
2599 This
->rootStartBlock
= 1;
2600 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2601 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2602 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2603 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2604 This
->extBigBlockDepotCount
= 0;
2606 StorageImpl_SaveFileHeader(This
);
2609 * Add one block for the big block depot and one block for the directory table
2611 size
.u
.HighPart
= 0;
2612 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2613 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2616 * Initialize the big block depot
2618 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2619 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2620 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2621 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2626 * Load the header for the file.
2628 hr
= StorageImpl_LoadFileHeader(This
);
2637 * There is no block depot cached yet.
2639 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2642 * Start searching for free blocks with block 0.
2644 This
->prevFreeBlock
= 0;
2647 * Create the block chain abstractions.
2649 if(!(This
->rootBlockChain
=
2650 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2652 hr
= STG_E_READFAULT
;
2656 if(!(This
->smallBlockDepotChain
=
2657 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2660 hr
= STG_E_READFAULT
;
2665 * Write the root storage entry (memory only)
2671 * Initialize the directory table
2673 memset(&rootEntry
, 0, sizeof(rootEntry
));
2674 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2675 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2676 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2677 rootEntry
.stgType
= STGTY_ROOT
;
2678 rootEntry
.leftChild
= DIRENTRY_NULL
;
2679 rootEntry
.rightChild
= DIRENTRY_NULL
;
2680 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2681 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2682 rootEntry
.size
.u
.HighPart
= 0;
2683 rootEntry
.size
.u
.LowPart
= 0;
2685 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2689 * Find the ID of the root storage.
2691 currentEntryRef
= 0;
2695 hr
= StorageImpl_ReadDirEntry(
2702 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2703 (currentEntry
.stgType
== STGTY_ROOT
) )
2705 This
->base
.storageDirEntry
= currentEntryRef
;
2711 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2715 hr
= STG_E_READFAULT
;
2720 * Create the block chain abstraction for the small block root chain.
2722 if(!(This
->smallBlockRootChain
=
2723 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2725 hr
= STG_E_READFAULT
;
2731 IStorage_Release((IStorage
*)This
);
2740 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2742 StorageImpl
*This
= (StorageImpl
*) iface
;
2744 StorageBaseImpl_DeleteAll(&This
->base
);
2746 This
->base
.reverted
= 1;
2749 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2751 StorageImpl
*This
= (StorageImpl
*) iface
;
2752 TRACE("(%p)\n", This
);
2754 StorageImpl_Invalidate(iface
);
2756 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2758 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2759 BlockChainStream_Destroy(This
->rootBlockChain
);
2760 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2762 if (This
->bigBlockFile
)
2763 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2764 HeapFree(GetProcessHeap(), 0, This
);
2767 /******************************************************************************
2768 * Storage32Impl_GetNextFreeBigBlock
2770 * Returns the index of the next free big block.
2771 * If the big block depot is filled, this method will enlarge it.
2774 static ULONG
StorageImpl_GetNextFreeBigBlock(
2777 ULONG depotBlockIndexPos
;
2778 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2780 ULONG depotBlockOffset
;
2781 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2782 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2784 ULONG freeBlock
= BLOCK_UNUSED
;
2786 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2787 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2790 * Scan the entire big block depot until we find a block marked free
2792 while (nextBlockIndex
!= BLOCK_UNUSED
)
2794 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2796 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2799 * Grow the primary depot.
2801 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2803 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2806 * Add a block depot.
2808 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2809 This
->bigBlockDepotCount
++;
2810 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2813 * Flag it as a block depot.
2815 StorageImpl_SetNextBlockInChain(This
,
2819 /* Save new header information.
2821 StorageImpl_SaveFileHeader(This
);
2826 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2828 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2831 * Grow the extended depot.
2833 ULONG extIndex
= BLOCK_UNUSED
;
2834 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2835 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2837 if (extBlockOffset
== 0)
2839 /* We need an extended block.
2841 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2842 This
->extBigBlockDepotCount
++;
2843 depotBlockIndexPos
= extIndex
+ 1;
2846 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2849 * Add a block depot and mark it in the extended block.
2851 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2852 This
->bigBlockDepotCount
++;
2853 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2855 /* Flag the block depot.
2857 StorageImpl_SetNextBlockInChain(This
,
2861 /* If necessary, flag the extended depot block.
2863 if (extIndex
!= BLOCK_UNUSED
)
2864 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2866 /* Save header information.
2868 StorageImpl_SaveFileHeader(This
);
2872 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2876 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2877 ( nextBlockIndex
!= BLOCK_UNUSED
))
2879 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2881 if (nextBlockIndex
== BLOCK_UNUSED
)
2883 freeBlock
= (depotIndex
* blocksPerDepot
) +
2884 (depotBlockOffset
/sizeof(ULONG
));
2887 depotBlockOffset
+= sizeof(ULONG
);
2892 depotBlockOffset
= 0;
2896 * make sure that the block physically exists before using it
2898 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2900 This
->prevFreeBlock
= freeBlock
;
2905 /******************************************************************************
2906 * Storage32Impl_AddBlockDepot
2908 * This will create a depot block, essentially it is a block initialized
2911 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2913 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2916 * Initialize blocks as free
2918 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2919 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2922 /******************************************************************************
2923 * Storage32Impl_GetExtDepotBlock
2925 * Returns the index of the block that corresponds to the specified depot
2926 * index. This method is only for depot indexes equal or greater than
2927 * COUNT_BBDEPOTINHEADER.
2929 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2931 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2932 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2933 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2934 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2935 ULONG blockIndex
= BLOCK_UNUSED
;
2936 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2938 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2940 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2941 return BLOCK_UNUSED
;
2943 while (extBlockCount
> 0)
2945 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2949 if (extBlockIndex
!= BLOCK_UNUSED
)
2950 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2951 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2956 /******************************************************************************
2957 * Storage32Impl_SetExtDepotBlock
2959 * Associates the specified block index to the specified depot index.
2960 * This method is only for depot indexes equal or greater than
2961 * COUNT_BBDEPOTINHEADER.
2963 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2965 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2966 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2967 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2968 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2969 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2971 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2973 while (extBlockCount
> 0)
2975 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2979 if (extBlockIndex
!= BLOCK_UNUSED
)
2981 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2982 extBlockOffset
* sizeof(ULONG
),
2987 /******************************************************************************
2988 * Storage32Impl_AddExtBlockDepot
2990 * Creates an extended depot block.
2992 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2994 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2995 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2996 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2997 ULONG index
= BLOCK_UNUSED
;
2998 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2999 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3000 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3002 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3003 blocksPerDepotBlock
;
3005 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3008 * The first extended block.
3010 This
->extBigBlockDepotStart
= index
;
3016 * Follow the chain to the last one.
3018 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3020 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3024 * Add the new extended block to the chain.
3026 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3031 * Initialize this block.
3033 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3034 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3039 /******************************************************************************
3040 * Storage32Impl_FreeBigBlock
3042 * This method will flag the specified block as free in the big block depot.
3044 static void StorageImpl_FreeBigBlock(
3048 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3050 if (blockIndex
< This
->prevFreeBlock
)
3051 This
->prevFreeBlock
= blockIndex
;
3054 /************************************************************************
3055 * Storage32Impl_GetNextBlockInChain
3057 * This method will retrieve the block index of the next big block in
3060 * Params: This - Pointer to the Storage object.
3061 * blockIndex - Index of the block to retrieve the chain
3063 * nextBlockIndex - receives the return value.
3065 * Returns: This method returns the index of the next block in the chain.
3066 * It will return the constants:
3067 * BLOCK_SPECIAL - If the block given was not part of a
3069 * BLOCK_END_OF_CHAIN - If the block given was the last in
3071 * BLOCK_UNUSED - If the block given was not past of a chain
3073 * BLOCK_EXTBBDEPOT - This block is part of the extended
3076 * See Windows documentation for more details on IStorage methods.
3078 static HRESULT
StorageImpl_GetNextBlockInChain(
3081 ULONG
* nextBlockIndex
)
3083 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3084 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3085 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3086 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3088 ULONG depotBlockIndexPos
;
3091 *nextBlockIndex
= BLOCK_SPECIAL
;
3093 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3095 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3096 This
->bigBlockDepotCount
);
3097 return STG_E_READFAULT
;
3101 * Cache the currently accessed depot block.
3103 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3105 This
->indexBlockDepotCached
= depotBlockCount
;
3107 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3109 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3114 * We have to look in the extended depot.
3116 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3119 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3122 return STG_E_READFAULT
;
3124 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3126 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3127 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3131 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3136 /******************************************************************************
3137 * Storage32Impl_GetNextExtendedBlock
3139 * Given an extended block this method will return the next extended block.
3142 * The last ULONG of an extended block is the block index of the next
3143 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3147 * - The index of the next extended block
3148 * - BLOCK_UNUSED: there is no next extended block.
3149 * - Any other return values denotes failure.
3151 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3153 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3154 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3156 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3159 return nextBlockIndex
;
3162 /******************************************************************************
3163 * Storage32Impl_SetNextBlockInChain
3165 * This method will write the index of the specified block's next block
3166 * in the big block depot.
3168 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3171 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3172 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3173 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3176 static void StorageImpl_SetNextBlockInChain(
3181 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3182 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3183 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3184 ULONG depotBlockIndexPos
;
3186 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3187 assert(blockIndex
!= nextBlock
);
3189 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3191 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3196 * We have to look in the extended depot.
3198 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3201 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3204 * Update the cached block depot, if necessary.
3206 if (depotBlockCount
== This
->indexBlockDepotCached
)
3208 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3212 /******************************************************************************
3213 * Storage32Impl_LoadFileHeader
3215 * This method will read in the file header, i.e. big block index -1.
3217 static HRESULT
StorageImpl_LoadFileHeader(
3220 HRESULT hr
= STG_E_FILENOTFOUND
;
3221 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3227 * Get a pointer to the big block of data containing the header.
3229 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3232 * Extract the information from the header.
3237 * Check for the "magic number" signature and return an error if it is not
3240 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3242 return STG_E_OLDFORMAT
;
3245 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3247 return STG_E_INVALIDHEADER
;
3250 StorageUtl_ReadWord(
3252 OFFSET_BIGBLOCKSIZEBITS
,
3253 &This
->bigBlockSizeBits
);
3255 StorageUtl_ReadWord(
3257 OFFSET_SMALLBLOCKSIZEBITS
,
3258 &This
->smallBlockSizeBits
);
3260 StorageUtl_ReadDWord(
3262 OFFSET_BBDEPOTCOUNT
,
3263 &This
->bigBlockDepotCount
);
3265 StorageUtl_ReadDWord(
3267 OFFSET_ROOTSTARTBLOCK
,
3268 &This
->rootStartBlock
);
3270 StorageUtl_ReadDWord(
3272 OFFSET_SBDEPOTSTART
,
3273 &This
->smallBlockDepotStart
);
3275 StorageUtl_ReadDWord(
3277 OFFSET_EXTBBDEPOTSTART
,
3278 &This
->extBigBlockDepotStart
);
3280 StorageUtl_ReadDWord(
3282 OFFSET_EXTBBDEPOTCOUNT
,
3283 &This
->extBigBlockDepotCount
);
3285 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3287 StorageUtl_ReadDWord(
3289 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3290 &(This
->bigBlockDepotStart
[index
]));
3294 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3296 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3297 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3300 * Right now, the code is making some assumptions about the size of the
3301 * blocks, just make sure they are what we're expecting.
3303 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3304 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3306 WARN("Broken OLE storage file\n");
3307 hr
= STG_E_INVALIDHEADER
;
3316 /******************************************************************************
3317 * Storage32Impl_SaveFileHeader
3319 * This method will save to the file the header, i.e. big block -1.
3321 static void StorageImpl_SaveFileHeader(
3324 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3329 * Get a pointer to the big block of data containing the header.
3331 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3334 * If the block read failed, the file is probably new.
3339 * Initialize for all unknown fields.
3341 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3344 * Initialize the magic number.
3346 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3349 * And a bunch of things we don't know what they mean
3351 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3352 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3353 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3354 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3358 * Write the information to the header.
3360 StorageUtl_WriteWord(
3362 OFFSET_BIGBLOCKSIZEBITS
,
3363 This
->bigBlockSizeBits
);
3365 StorageUtl_WriteWord(
3367 OFFSET_SMALLBLOCKSIZEBITS
,
3368 This
->smallBlockSizeBits
);
3370 StorageUtl_WriteDWord(
3372 OFFSET_BBDEPOTCOUNT
,
3373 This
->bigBlockDepotCount
);
3375 StorageUtl_WriteDWord(
3377 OFFSET_ROOTSTARTBLOCK
,
3378 This
->rootStartBlock
);
3380 StorageUtl_WriteDWord(
3382 OFFSET_SBDEPOTSTART
,
3383 This
->smallBlockDepotStart
);
3385 StorageUtl_WriteDWord(
3387 OFFSET_SBDEPOTCOUNT
,
3388 This
->smallBlockDepotChain
?
3389 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3391 StorageUtl_WriteDWord(
3393 OFFSET_EXTBBDEPOTSTART
,
3394 This
->extBigBlockDepotStart
);
3396 StorageUtl_WriteDWord(
3398 OFFSET_EXTBBDEPOTCOUNT
,
3399 This
->extBigBlockDepotCount
);
3401 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3403 StorageUtl_WriteDWord(
3405 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3406 (This
->bigBlockDepotStart
[index
]));
3410 * Write the big block back to the file.
3412 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3415 /******************************************************************************
3416 * StorageImpl_ReadRawDirEntry
3418 * This method will read the raw data from a directory entry in the file.
3420 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3422 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3424 ULARGE_INTEGER offset
;
3428 offset
.u
.HighPart
= 0;
3429 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3431 hr
= BlockChainStream_ReadAt(
3432 This
->rootBlockChain
,
3441 /******************************************************************************
3442 * StorageImpl_WriteRawDirEntry
3444 * This method will write the raw data from a directory entry in the file.
3446 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3448 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3450 ULARGE_INTEGER offset
;
3454 offset
.u
.HighPart
= 0;
3455 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3457 hr
= BlockChainStream_WriteAt(
3458 This
->rootBlockChain
,
3467 /******************************************************************************
3470 * Update raw directory entry data from the fields in newData.
3472 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3474 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3476 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3479 buffer
+ OFFSET_PS_NAME
,
3481 DIRENTRY_NAME_BUFFER_LEN
);
3483 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3485 StorageUtl_WriteWord(
3487 OFFSET_PS_NAMELENGTH
,
3488 newData
->sizeOfNameString
);
3490 StorageUtl_WriteDWord(
3492 OFFSET_PS_LEFTCHILD
,
3493 newData
->leftChild
);
3495 StorageUtl_WriteDWord(
3497 OFFSET_PS_RIGHTCHILD
,
3498 newData
->rightChild
);
3500 StorageUtl_WriteDWord(
3503 newData
->dirRootEntry
);
3505 StorageUtl_WriteGUID(
3510 StorageUtl_WriteDWord(
3513 newData
->ctime
.dwLowDateTime
);
3515 StorageUtl_WriteDWord(
3517 OFFSET_PS_CTIMEHIGH
,
3518 newData
->ctime
.dwHighDateTime
);
3520 StorageUtl_WriteDWord(
3523 newData
->mtime
.dwLowDateTime
);
3525 StorageUtl_WriteDWord(
3527 OFFSET_PS_MTIMEHIGH
,
3528 newData
->ctime
.dwHighDateTime
);
3530 StorageUtl_WriteDWord(
3532 OFFSET_PS_STARTBLOCK
,
3533 newData
->startingBlock
);
3535 StorageUtl_WriteDWord(
3538 newData
->size
.u
.LowPart
);
3541 /******************************************************************************
3542 * Storage32Impl_ReadDirEntry
3544 * This method will read the specified directory entry.
3546 HRESULT
StorageImpl_ReadDirEntry(
3551 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3554 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3556 if (SUCCEEDED(readRes
))
3558 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3561 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3562 DIRENTRY_NAME_BUFFER_LEN
);
3563 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3565 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3567 StorageUtl_ReadWord(
3569 OFFSET_PS_NAMELENGTH
,
3570 &buffer
->sizeOfNameString
);
3572 StorageUtl_ReadDWord(
3574 OFFSET_PS_LEFTCHILD
,
3575 &buffer
->leftChild
);
3577 StorageUtl_ReadDWord(
3579 OFFSET_PS_RIGHTCHILD
,
3580 &buffer
->rightChild
);
3582 StorageUtl_ReadDWord(
3585 &buffer
->dirRootEntry
);
3587 StorageUtl_ReadGUID(
3592 StorageUtl_ReadDWord(
3595 &buffer
->ctime
.dwLowDateTime
);
3597 StorageUtl_ReadDWord(
3599 OFFSET_PS_CTIMEHIGH
,
3600 &buffer
->ctime
.dwHighDateTime
);
3602 StorageUtl_ReadDWord(
3605 &buffer
->mtime
.dwLowDateTime
);
3607 StorageUtl_ReadDWord(
3609 OFFSET_PS_MTIMEHIGH
,
3610 &buffer
->mtime
.dwHighDateTime
);
3612 StorageUtl_ReadDWord(
3614 OFFSET_PS_STARTBLOCK
,
3615 &buffer
->startingBlock
);
3617 StorageUtl_ReadDWord(
3620 &buffer
->size
.u
.LowPart
);
3622 buffer
->size
.u
.HighPart
= 0;
3628 /*********************************************************************
3629 * Write the specified directory entry to the file
3631 HRESULT
StorageImpl_WriteDirEntry(
3634 const DirEntry
* buffer
)
3636 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3639 UpdateRawDirEntry(currentEntry
, buffer
);
3641 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3645 static BOOL
StorageImpl_ReadBigBlock(
3650 ULARGE_INTEGER ulOffset
;
3653 ulOffset
.u
.HighPart
= 0;
3654 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3656 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3657 return (read
== This
->bigBlockSize
);
3660 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3666 ULARGE_INTEGER ulOffset
;
3670 ulOffset
.u
.HighPart
= 0;
3671 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3672 ulOffset
.u
.LowPart
+= offset
;
3674 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3675 *value
= lendian32toh(tmp
);
3676 return (read
== sizeof(DWORD
));
3679 static BOOL
StorageImpl_WriteBigBlock(
3684 ULARGE_INTEGER ulOffset
;
3687 ulOffset
.u
.HighPart
= 0;
3688 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3690 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3691 return (wrote
== This
->bigBlockSize
);
3694 static BOOL
StorageImpl_WriteDWordToBigBlock(
3700 ULARGE_INTEGER ulOffset
;
3703 ulOffset
.u
.HighPart
= 0;
3704 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3705 ulOffset
.u
.LowPart
+= offset
;
3707 value
= htole32(value
);
3708 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3709 return (wrote
== sizeof(DWORD
));
3712 /******************************************************************************
3713 * Storage32Impl_SmallBlocksToBigBlocks
3715 * This method will convert a small block chain to a big block chain.
3716 * The small block chain will be destroyed.
3718 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3720 SmallBlockChainStream
** ppsbChain
)
3722 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3723 ULARGE_INTEGER size
, offset
;
3724 ULONG cbRead
, cbWritten
;
3725 ULARGE_INTEGER cbTotalRead
;
3726 DirRef streamEntryRef
;
3727 HRESULT resWrite
= S_OK
;
3729 DirEntry streamEntry
;
3731 BlockChainStream
*bbTempChain
= NULL
;
3732 BlockChainStream
*bigBlockChain
= NULL
;
3735 * Create a temporary big block chain that doesn't have
3736 * an associated directory entry. This temporary chain will be
3737 * used to copy data from small blocks to big blocks.
3739 bbTempChain
= BlockChainStream_Construct(This
,
3742 if(!bbTempChain
) return NULL
;
3744 * Grow the big block chain.
3746 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3747 BlockChainStream_SetSize(bbTempChain
, size
);
3750 * Copy the contents of the small block chain to the big block chain
3751 * by small block size increments.
3753 offset
.u
.LowPart
= 0;
3754 offset
.u
.HighPart
= 0;
3755 cbTotalRead
.QuadPart
= 0;
3757 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3760 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3762 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3765 if (FAILED(resRead
))
3770 cbTotalRead
.QuadPart
+= cbRead
;
3772 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3778 if (FAILED(resWrite
))
3781 offset
.u
.LowPart
+= cbRead
;
3783 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3784 HeapFree(GetProcessHeap(),0,buffer
);
3786 size
.u
.HighPart
= 0;
3789 if (FAILED(resRead
) || FAILED(resWrite
))
3791 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3792 BlockChainStream_SetSize(bbTempChain
, size
);
3793 BlockChainStream_Destroy(bbTempChain
);
3798 * Destroy the small block chain.
3800 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3801 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3802 SmallBlockChainStream_Destroy(*ppsbChain
);
3806 * Change the directory entry. This chain is now a big block chain
3807 * and it doesn't reside in the small blocks chain anymore.
3809 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3811 streamEntry
.startingBlock
= bbHeadOfChain
;
3813 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3816 * Destroy the temporary entryless big block chain.
3817 * Create a new big block chain associated with this entry.
3819 BlockChainStream_Destroy(bbTempChain
);
3820 bigBlockChain
= BlockChainStream_Construct(This
,
3824 return bigBlockChain
;
3827 /******************************************************************************
3828 * Storage32Impl_BigBlocksToSmallBlocks
3830 * This method will convert a big block chain to a small block chain.
3831 * The big block chain will be destroyed on success.
3833 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3835 BlockChainStream
** ppbbChain
)
3837 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3838 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3839 DirRef streamEntryRef
;
3840 HRESULT resWrite
= S_OK
, resRead
;
3841 DirEntry streamEntry
;
3843 SmallBlockChainStream
* sbTempChain
;
3845 TRACE("%p %p\n", This
, ppbbChain
);
3847 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3853 size
= BlockChainStream_GetSize(*ppbbChain
);
3854 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3856 offset
.u
.HighPart
= 0;
3857 offset
.u
.LowPart
= 0;
3858 cbTotalRead
.QuadPart
= 0;
3859 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3862 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3863 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3871 cbTotalRead
.QuadPart
+= cbRead
;
3873 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3874 cbRead
, buffer
, &cbWritten
);
3876 if(FAILED(resWrite
))
3879 offset
.u
.LowPart
+= cbRead
;
3881 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3882 HeapFree(GetProcessHeap(), 0, buffer
);
3884 size
.u
.HighPart
= 0;
3887 if(FAILED(resRead
) || FAILED(resWrite
))
3889 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3890 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3891 SmallBlockChainStream_Destroy(sbTempChain
);
3895 /* destroy the original big block chain */
3896 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3897 BlockChainStream_SetSize(*ppbbChain
, size
);
3898 BlockChainStream_Destroy(*ppbbChain
);
3901 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3902 streamEntry
.startingBlock
= sbHeadOfChain
;
3903 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3905 SmallBlockChainStream_Destroy(sbTempChain
);
3906 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3909 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3912 DirEntry parentData
, snapshotData
;
3914 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3915 0, (IStorage
**)snapshot
);
3919 hr
= StorageBaseImpl_ReadDirEntry(original
,
3920 original
->storageDirEntry
, &parentData
);
3923 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3924 (*snapshot
)->storageDirEntry
, &snapshotData
);
3928 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3929 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3930 snapshotData
.stgType
= parentData
.stgType
;
3931 snapshotData
.clsid
= parentData
.clsid
;
3932 snapshotData
.ctime
= parentData
.ctime
;
3933 snapshotData
.mtime
= parentData
.mtime
;
3934 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3935 (*snapshot
)->storageDirEntry
, &snapshotData
);
3939 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
3940 (IStorage
*)(*snapshot
));
3942 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
3948 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
3950 DWORD grfCommitFlags
) /* [in] */
3952 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
3954 DirEntry data
, tempStorageData
, snapshotRootData
;
3955 DirRef tempStorageEntry
, oldDirRoot
;
3956 StorageInternalImpl
*tempStorage
;
3958 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
3960 /* Cannot commit a read-only transacted storage */
3961 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
3962 return STG_E_ACCESSDENIED
;
3964 /* To prevent data loss, we create the new structure in the file before we
3965 * delete the old one, so that in case of errors the old data is intact. We
3966 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
3967 * needed in the rare situation where we have just enough free disk space to
3968 * overwrite the existing data. */
3970 /* Create an orphaned storage in the parent for the new directory structure. */
3971 memset(&data
, 0, sizeof(data
));
3973 data
.sizeOfNameString
= 1;
3974 data
.stgType
= STGTY_STORAGE
;
3975 data
.leftChild
= DIRENTRY_NULL
;
3976 data
.rightChild
= DIRENTRY_NULL
;
3977 data
.dirRootEntry
= DIRENTRY_NULL
;
3978 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
3980 if (FAILED(hr
)) return hr
;
3982 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
3983 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
3986 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
3987 (IStorage
*)tempStorage
);
3989 list_init(&tempStorage
->ParentListEntry
);
3991 IStorage_Release((IStorage
*) tempStorage
);
3998 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4002 /* Update the storage to use the new data in one step. */
4003 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4004 This
->transactedParent
->storageDirEntry
, &data
);
4008 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4009 tempStorageEntry
, &tempStorageData
);
4014 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4015 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4020 oldDirRoot
= data
.dirRootEntry
;
4021 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4022 data
.clsid
= snapshotRootData
.clsid
;
4023 data
.ctime
= snapshotRootData
.ctime
;
4024 data
.mtime
= snapshotRootData
.mtime
;
4026 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4027 This
->transactedParent
->storageDirEntry
, &data
);
4032 /* Destroy the old now-orphaned data. */
4033 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4034 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4038 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4044 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4047 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4048 StorageBaseImpl
*newSnapshot
;
4051 TRACE("(%p)\n", iface
);
4053 /* Create a new copy of the parent data. */
4054 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4055 if (FAILED(hr
)) return hr
;
4057 /* Destroy the open objects. */
4058 StorageBaseImpl_DeleteAll(&This
->base
);
4060 /* Replace our current snapshot. */
4061 IStorage_Release((IStorage
*)This
->snapshot
);
4062 This
->snapshot
= newSnapshot
;
4067 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4069 if (!This
->reverted
)
4071 TRACE("Storage invalidated (stg=%p)\n", This
);
4075 StorageBaseImpl_DeleteAll(This
);
4079 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4081 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4083 TransactedSnapshotImpl_Invalidate(iface
);
4085 IStorage_Release((IStorage
*)This
->transactedParent
);
4087 IStorage_Release((IStorage
*)This
->snapshot
);
4089 HeapFree(GetProcessHeap(), 0, This
);
4092 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4093 const DirEntry
*newData
, DirRef
*index
)
4095 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4097 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4101 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4102 DirRef index
, const DirEntry
*data
)
4104 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4106 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4110 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4111 DirRef index
, DirEntry
*data
)
4113 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4115 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4119 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4122 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4124 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4128 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4129 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4131 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4133 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4134 index
, offset
, size
, buffer
, bytesRead
);
4137 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4138 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4140 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4142 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4143 index
, offset
, size
, buffer
, bytesWritten
);
4146 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4147 DirRef index
, ULARGE_INTEGER newsize
)
4149 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4151 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4157 StorageBaseImpl_QueryInterface
,
4158 StorageBaseImpl_AddRef
,
4159 StorageBaseImpl_Release
,
4160 StorageBaseImpl_CreateStream
,
4161 StorageBaseImpl_OpenStream
,
4162 StorageBaseImpl_CreateStorage
,
4163 StorageBaseImpl_OpenStorage
,
4164 StorageBaseImpl_CopyTo
,
4165 StorageBaseImpl_MoveElementTo
,
4166 TransactedSnapshotImpl_Commit
,
4167 TransactedSnapshotImpl_Revert
,
4168 StorageBaseImpl_EnumElements
,
4169 StorageBaseImpl_DestroyElement
,
4170 StorageBaseImpl_RenameElement
,
4171 StorageBaseImpl_SetElementTimes
,
4172 StorageBaseImpl_SetClass
,
4173 StorageBaseImpl_SetStateBits
,
4174 StorageBaseImpl_Stat
4177 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4179 TransactedSnapshotImpl_Destroy
,
4180 TransactedSnapshotImpl_Invalidate
,
4181 TransactedSnapshotImpl_CreateDirEntry
,
4182 TransactedSnapshotImpl_WriteDirEntry
,
4183 TransactedSnapshotImpl_ReadDirEntry
,
4184 TransactedSnapshotImpl_DestroyDirEntry
,
4185 TransactedSnapshotImpl_StreamReadAt
,
4186 TransactedSnapshotImpl_StreamWriteAt
,
4187 TransactedSnapshotImpl_StreamSetSize
4190 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4191 TransactedSnapshotImpl
** result
)
4195 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4198 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4200 /* This is OK because the property set storage functions use the IStorage functions. */
4201 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4203 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4205 list_init(&(*result
)->base
.strmHead
);
4207 list_init(&(*result
)->base
.storageHead
);
4209 (*result
)->base
.ref
= 1;
4211 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4213 (*result
)->base
.filename
= parentStorage
->filename
;
4215 /* Create a new temporary storage to act as the snapshot */
4216 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4220 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4222 /* parentStorage already has 1 reference, which we take over here. */
4223 (*result
)->transactedParent
= parentStorage
;
4225 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4228 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4233 return E_OUTOFMEMORY
;
4236 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4237 StorageBaseImpl
** result
)
4241 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4243 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4246 return TransactedSnapshotImpl_Construct(parentStorage
,
4247 (TransactedSnapshotImpl
**)result
);
4250 static HRESULT
Storage_Construct(
4257 StorageBaseImpl
** result
)
4259 StorageImpl
*newStorage
;
4260 StorageBaseImpl
*newTransactedStorage
;
4263 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4264 if (FAILED(hr
)) goto end
;
4266 if (openFlags
& STGM_TRANSACTED
)
4268 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4270 IStorage_Release((IStorage
*)newStorage
);
4272 *result
= newTransactedStorage
;
4275 *result
= &newStorage
->base
;
4281 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4283 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4285 if (!This
->base
.reverted
)
4287 TRACE("Storage invalidated (stg=%p)\n", This
);
4289 This
->base
.reverted
= 1;
4291 This
->parentStorage
= NULL
;
4293 StorageBaseImpl_DeleteAll(&This
->base
);
4295 list_remove(&This
->ParentListEntry
);
4299 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4301 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4303 StorageInternalImpl_Invalidate(&This
->base
);
4305 HeapFree(GetProcessHeap(), 0, This
);
4308 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4309 const DirEntry
*newData
, DirRef
*index
)
4311 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4313 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4317 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4318 DirRef index
, const DirEntry
*data
)
4320 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4322 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4326 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4327 DirRef index
, DirEntry
*data
)
4329 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4331 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4335 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4338 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4340 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4344 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4345 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4347 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4349 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4350 index
, offset
, size
, buffer
, bytesRead
);
4353 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4354 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4356 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4358 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4359 index
, offset
, size
, buffer
, bytesWritten
);
4362 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4363 DirRef index
, ULARGE_INTEGER newsize
)
4365 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4367 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4371 /******************************************************************************
4373 ** Storage32InternalImpl_Commit
4376 static HRESULT WINAPI
StorageInternalImpl_Commit(
4378 DWORD grfCommitFlags
) /* [in] */
4380 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4384 /******************************************************************************
4386 ** Storage32InternalImpl_Revert
4389 static HRESULT WINAPI
StorageInternalImpl_Revert(
4392 FIXME("(%p): stub\n", iface
);
4396 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4398 IStorage_Release((IStorage
*)This
->parentStorage
);
4399 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
4400 HeapFree(GetProcessHeap(), 0, This
);
4403 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4404 IEnumSTATSTG
* iface
,
4408 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4411 return E_INVALIDARG
;
4415 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4416 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4419 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4423 return E_NOINTERFACE
;
4426 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4427 IEnumSTATSTG
* iface
)
4429 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4430 return InterlockedIncrement(&This
->ref
);
4433 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4434 IEnumSTATSTG
* iface
)
4436 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4440 newRef
= InterlockedDecrement(&This
->ref
);
4444 IEnumSTATSTGImpl_Destroy(This
);
4450 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4451 IEnumSTATSTG
* iface
,
4454 ULONG
* pceltFetched
)
4456 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4458 DirEntry currentEntry
;
4459 STATSTG
* currentReturnStruct
= rgelt
;
4460 ULONG objectFetched
= 0;
4461 DirRef currentSearchNode
;
4463 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4464 return E_INVALIDARG
;
4467 * To avoid the special case, get another pointer to a ULONG value if
4468 * the caller didn't supply one.
4470 if (pceltFetched
==0)
4471 pceltFetched
= &objectFetched
;
4474 * Start the iteration, we will iterate until we hit the end of the
4475 * linked list or until we hit the number of items to iterate through
4480 * Start with the node at the top of the stack.
4482 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4484 while ( ( *pceltFetched
< celt
) &&
4485 ( currentSearchNode
!=DIRENTRY_NULL
) )
4488 * Remove the top node from the stack
4490 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4493 * Read the entry from the storage.
4495 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4500 * Copy the information to the return buffer.
4502 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4503 currentReturnStruct
,
4508 * Step to the next item in the iteration
4511 currentReturnStruct
++;
4514 * Push the next search node in the search stack.
4516 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
4519 * continue the iteration.
4521 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4524 if (*pceltFetched
== celt
)
4531 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4532 IEnumSTATSTG
* iface
,
4535 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4537 DirEntry currentEntry
;
4538 ULONG objectFetched
= 0;
4539 DirRef currentSearchNode
;
4542 * Start with the node at the top of the stack.
4544 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4546 while ( (objectFetched
< celt
) &&
4547 (currentSearchNode
!=DIRENTRY_NULL
) )
4550 * Remove the top node from the stack
4552 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4555 * Read the entry from the storage.
4557 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4562 * Step to the next item in the iteration
4567 * Push the next search node in the search stack.
4569 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
4572 * continue the iteration.
4574 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4577 if (objectFetched
== celt
)
4583 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4584 IEnumSTATSTG
* iface
)
4586 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4588 DirEntry storageEntry
;
4592 * Re-initialize the search stack to an empty stack
4594 This
->stackSize
= 0;
4597 * Read the storage entry from the top-level storage.
4599 hr
= StorageBaseImpl_ReadDirEntry(
4600 This
->parentStorage
,
4601 This
->storageDirEntry
,
4606 assert(storageEntry
.sizeOfNameString
!=0);
4609 * Push the search node in the search stack.
4611 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.dirRootEntry
);
4617 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4618 IEnumSTATSTG
* iface
,
4619 IEnumSTATSTG
** ppenum
)
4621 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4623 IEnumSTATSTGImpl
* newClone
;
4626 * Perform a sanity check on the parameters.
4629 return E_INVALIDARG
;
4631 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4632 This
->storageDirEntry
);
4636 * The new clone enumeration must point to the same current node as
4639 newClone
->stackSize
= This
->stackSize
;
4640 newClone
->stackMaxSize
= This
->stackMaxSize
;
4641 newClone
->stackToVisit
=
4642 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
4645 newClone
->stackToVisit
,
4647 sizeof(DirRef
) * newClone
->stackSize
);
4649 *ppenum
= (IEnumSTATSTG
*)newClone
;
4652 * Don't forget to nail down a reference to the clone before
4655 IEnumSTATSTGImpl_AddRef(*ppenum
);
4660 static void IEnumSTATSTGImpl_PushSearchNode(
4661 IEnumSTATSTGImpl
* This
,
4664 DirEntry storageEntry
;
4668 * First, make sure we're not trying to push an unexisting node.
4670 if (nodeToPush
==DIRENTRY_NULL
)
4674 * First push the node to the stack
4676 if (This
->stackSize
== This
->stackMaxSize
)
4678 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4680 This
->stackToVisit
= HeapReAlloc(
4684 sizeof(DirRef
) * This
->stackMaxSize
);
4687 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4691 * Read the storage entry from the top-level storage.
4693 hr
= StorageBaseImpl_ReadDirEntry(
4694 This
->parentStorage
,
4700 assert(storageEntry
.sizeOfNameString
!=0);
4703 * Push the previous search node in the search stack.
4705 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.leftChild
);
4709 static DirRef
IEnumSTATSTGImpl_PopSearchNode(
4710 IEnumSTATSTGImpl
* This
,
4715 if (This
->stackSize
== 0)
4716 return DIRENTRY_NULL
;
4718 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4727 * Virtual function table for the IEnumSTATSTGImpl class.
4729 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4731 IEnumSTATSTGImpl_QueryInterface
,
4732 IEnumSTATSTGImpl_AddRef
,
4733 IEnumSTATSTGImpl_Release
,
4734 IEnumSTATSTGImpl_Next
,
4735 IEnumSTATSTGImpl_Skip
,
4736 IEnumSTATSTGImpl_Reset
,
4737 IEnumSTATSTGImpl_Clone
4740 /******************************************************************************
4741 ** IEnumSTATSTGImpl implementation
4744 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4745 StorageBaseImpl
* parentStorage
,
4746 DirRef storageDirEntry
)
4748 IEnumSTATSTGImpl
* newEnumeration
;
4750 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4752 if (newEnumeration
!=0)
4755 * Set-up the virtual function table and reference count.
4757 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4758 newEnumeration
->ref
= 0;
4761 * We want to nail-down the reference to the storage in case the
4762 * enumeration out-lives the storage in the client application.
4764 newEnumeration
->parentStorage
= parentStorage
;
4765 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4767 newEnumeration
->storageDirEntry
= storageDirEntry
;
4770 * Initialize the search stack
4772 newEnumeration
->stackSize
= 0;
4773 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4774 newEnumeration
->stackToVisit
=
4775 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef
)*ENUMSTATSGT_SIZE_INCREMENT
);
4778 * Make sure the current node of the iterator is the first one.
4780 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4783 return newEnumeration
;
4787 * Virtual function table for the Storage32InternalImpl class.
4789 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4791 StorageBaseImpl_QueryInterface
,
4792 StorageBaseImpl_AddRef
,
4793 StorageBaseImpl_Release
,
4794 StorageBaseImpl_CreateStream
,
4795 StorageBaseImpl_OpenStream
,
4796 StorageBaseImpl_CreateStorage
,
4797 StorageBaseImpl_OpenStorage
,
4798 StorageBaseImpl_CopyTo
,
4799 StorageBaseImpl_MoveElementTo
,
4800 StorageInternalImpl_Commit
,
4801 StorageInternalImpl_Revert
,
4802 StorageBaseImpl_EnumElements
,
4803 StorageBaseImpl_DestroyElement
,
4804 StorageBaseImpl_RenameElement
,
4805 StorageBaseImpl_SetElementTimes
,
4806 StorageBaseImpl_SetClass
,
4807 StorageBaseImpl_SetStateBits
,
4808 StorageBaseImpl_Stat
4811 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4813 StorageInternalImpl_Destroy
,
4814 StorageInternalImpl_Invalidate
,
4815 StorageInternalImpl_CreateDirEntry
,
4816 StorageInternalImpl_WriteDirEntry
,
4817 StorageInternalImpl_ReadDirEntry
,
4818 StorageInternalImpl_DestroyDirEntry
,
4819 StorageInternalImpl_StreamReadAt
,
4820 StorageInternalImpl_StreamWriteAt
,
4821 StorageInternalImpl_StreamSetSize
4824 /******************************************************************************
4825 ** Storage32InternalImpl implementation
4828 static StorageInternalImpl
* StorageInternalImpl_Construct(
4829 StorageBaseImpl
* parentStorage
,
4831 DirRef storageDirEntry
)
4833 StorageInternalImpl
* newStorage
;
4835 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4839 list_init(&newStorage
->base
.strmHead
);
4841 list_init(&newStorage
->base
.storageHead
);
4844 * Initialize the virtual function table.
4846 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4847 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4848 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4850 newStorage
->base
.reverted
= 0;
4852 newStorage
->base
.ref
= 1;
4854 newStorage
->parentStorage
= parentStorage
;
4857 * Keep a reference to the directory entry of this storage
4859 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4861 newStorage
->base
.create
= 0;
4869 /******************************************************************************
4870 ** StorageUtl implementation
4873 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4877 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4878 *value
= lendian16toh(tmp
);
4881 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4883 value
= htole16(value
);
4884 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4887 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4891 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4892 *value
= lendian32toh(tmp
);
4895 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4897 value
= htole32(value
);
4898 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4901 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4902 ULARGE_INTEGER
* value
)
4904 #ifdef WORDS_BIGENDIAN
4907 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4908 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4909 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4911 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4915 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4916 const ULARGE_INTEGER
*value
)
4918 #ifdef WORDS_BIGENDIAN
4921 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4922 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4923 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4925 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4929 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4931 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4932 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4933 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4935 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4938 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4940 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4941 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4942 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4944 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4947 void StorageUtl_CopyDirEntryToSTATSTG(
4948 StorageBaseImpl
* storage
,
4949 STATSTG
* destination
,
4950 const DirEntry
* source
,
4955 if (source
->stgType
== STGTY_ROOT
)
4957 /* replace the name of root entry (often "Root Entry") by the file name */
4958 entryName
= storage
->filename
;
4962 entryName
= source
->name
;
4966 * The copy of the string occurs only when the flag is not set
4968 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4969 (entryName
== NULL
) ||
4970 (entryName
[0] == 0) )
4972 destination
->pwcsName
= 0;
4976 destination
->pwcsName
=
4977 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4979 strcpyW(destination
->pwcsName
, entryName
);
4982 switch (source
->stgType
)
4986 destination
->type
= STGTY_STORAGE
;
4989 destination
->type
= STGTY_STREAM
;
4992 destination
->type
= STGTY_STREAM
;
4996 destination
->cbSize
= source
->size
;
4998 currentReturnStruct->mtime = {0}; TODO
4999 currentReturnStruct->ctime = {0};
5000 currentReturnStruct->atime = {0};
5002 destination
->grfMode
= 0;
5003 destination
->grfLocksSupported
= 0;
5004 destination
->clsid
= source
->clsid
;
5005 destination
->grfStateBits
= 0;
5006 destination
->reserved
= 0;
5009 /******************************************************************************
5010 ** BlockChainStream implementation
5013 BlockChainStream
* BlockChainStream_Construct(
5014 StorageImpl
* parentStorage
,
5015 ULONG
* headOfStreamPlaceHolder
,
5018 BlockChainStream
* newStream
;
5021 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5023 newStream
->parentStorage
= parentStorage
;
5024 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5025 newStream
->ownerDirEntry
= dirEntry
;
5026 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
5027 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
5028 newStream
->numBlocks
= 0;
5030 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
5032 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5034 newStream
->numBlocks
++;
5035 newStream
->tailIndex
= blockIndex
;
5037 if(FAILED(StorageImpl_GetNextBlockInChain(
5042 HeapFree(GetProcessHeap(), 0, newStream
);
5050 void BlockChainStream_Destroy(BlockChainStream
* This
)
5052 HeapFree(GetProcessHeap(), 0, This
);
5055 /******************************************************************************
5056 * BlockChainStream_GetHeadOfChain
5058 * Returns the head of this stream chain.
5059 * Some special chains don't have directory entries, their heads are kept in
5060 * This->headOfStreamPlaceHolder.
5063 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5065 DirEntry chainEntry
;
5068 if (This
->headOfStreamPlaceHolder
!= 0)
5069 return *(This
->headOfStreamPlaceHolder
);
5071 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5073 hr
= StorageImpl_ReadDirEntry(
5074 This
->parentStorage
,
5075 This
->ownerDirEntry
,
5080 return chainEntry
.startingBlock
;
5084 return BLOCK_END_OF_CHAIN
;
5087 /******************************************************************************
5088 * BlockChainStream_GetCount
5090 * Returns the number of blocks that comprises this chain.
5091 * This is not the size of the stream as the last block may not be full!
5094 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5099 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5101 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5105 if(FAILED(StorageImpl_GetNextBlockInChain(
5106 This
->parentStorage
,
5115 /******************************************************************************
5116 * BlockChainStream_ReadAt
5118 * Reads a specified number of bytes from this chain at the specified offset.
5119 * bytesRead may be NULL.
5120 * Failure will be returned if the specified number of bytes has not been read.
5122 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5123 ULARGE_INTEGER offset
,
5128 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5129 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5130 ULONG bytesToReadInBuffer
;
5134 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5137 * Find the first block in the stream that contains part of the buffer.
5139 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5140 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5141 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5143 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5144 This
->lastBlockNoInSequence
= blockNoInSequence
;
5148 ULONG temp
= blockNoInSequence
;
5150 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5151 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5152 This
->lastBlockNoInSequence
= temp
;
5155 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5157 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5158 return STG_E_DOCFILECORRUPT
;
5159 blockNoInSequence
--;
5162 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5163 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5165 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5168 * Start reading the buffer.
5171 bufferWalker
= buffer
;
5173 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5175 ULARGE_INTEGER ulOffset
;
5178 * Calculate how many bytes we can copy from this big block.
5180 bytesToReadInBuffer
=
5181 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5183 TRACE("block %i\n",blockIndex
);
5184 ulOffset
.u
.HighPart
= 0;
5185 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5188 StorageImpl_ReadAt(This
->parentStorage
,
5191 bytesToReadInBuffer
,
5194 * Step to the next big block.
5196 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5197 return STG_E_DOCFILECORRUPT
;
5199 bufferWalker
+= bytesReadAt
;
5200 size
-= bytesReadAt
;
5201 *bytesRead
+= bytesReadAt
;
5202 offsetInBlock
= 0; /* There is no offset on the next block */
5204 if (bytesToReadInBuffer
!= bytesReadAt
)
5208 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5211 /******************************************************************************
5212 * BlockChainStream_WriteAt
5214 * Writes the specified number of bytes to this chain at the specified offset.
5215 * Will fail if not all specified number of bytes have been written.
5217 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5218 ULARGE_INTEGER offset
,
5221 ULONG
* bytesWritten
)
5223 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5224 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5227 const BYTE
* bufferWalker
;
5230 * Find the first block in the stream that contains part of the buffer.
5232 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5233 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5234 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5236 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5237 This
->lastBlockNoInSequence
= blockNoInSequence
;
5241 ULONG temp
= blockNoInSequence
;
5243 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5244 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5245 This
->lastBlockNoInSequence
= temp
;
5248 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5250 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5252 return STG_E_DOCFILECORRUPT
;
5253 blockNoInSequence
--;
5256 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5258 /* BlockChainStream_SetSize should have already been called to ensure we have
5259 * enough blocks in the chain to write into */
5260 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5262 ERR("not enough blocks in chain to write data\n");
5263 return STG_E_DOCFILECORRUPT
;
5267 bufferWalker
= buffer
;
5269 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5271 ULARGE_INTEGER ulOffset
;
5272 DWORD bytesWrittenAt
;
5274 * Calculate how many bytes we can copy from this big block.
5277 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5279 TRACE("block %i\n",blockIndex
);
5280 ulOffset
.u
.HighPart
= 0;
5281 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5284 StorageImpl_WriteAt(This
->parentStorage
,
5291 * Step to the next big block.
5293 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5295 return STG_E_DOCFILECORRUPT
;
5297 bufferWalker
+= bytesWrittenAt
;
5298 size
-= bytesWrittenAt
;
5299 *bytesWritten
+= bytesWrittenAt
;
5300 offsetInBlock
= 0; /* There is no offset on the next block */
5302 if (bytesWrittenAt
!= bytesToWrite
)
5306 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5309 /******************************************************************************
5310 * BlockChainStream_Shrink
5312 * Shrinks this chain in the big block depot.
5314 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5315 ULARGE_INTEGER newSize
)
5317 ULONG blockIndex
, extraBlock
;
5322 * Reset the last accessed block cache.
5324 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5325 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5328 * Figure out how many blocks are needed to contain the new size
5330 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5332 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5335 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5338 * Go to the new end of chain
5340 while (count
< numBlocks
)
5342 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5348 /* Get the next block before marking the new end */
5349 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5353 /* Mark the new end of chain */
5354 StorageImpl_SetNextBlockInChain(
5355 This
->parentStorage
,
5357 BLOCK_END_OF_CHAIN
);
5359 This
->tailIndex
= blockIndex
;
5360 This
->numBlocks
= numBlocks
;
5363 * Mark the extra blocks as free
5365 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5367 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5370 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5371 extraBlock
= blockIndex
;
5377 /******************************************************************************
5378 * BlockChainStream_Enlarge
5380 * Grows this chain in the big block depot.
5382 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5383 ULARGE_INTEGER newSize
)
5385 ULONG blockIndex
, currentBlock
;
5387 ULONG oldNumBlocks
= 0;
5389 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5392 * Empty chain. Create the head.
5394 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5396 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5397 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5399 BLOCK_END_OF_CHAIN
);
5401 if (This
->headOfStreamPlaceHolder
!= 0)
5403 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5407 DirEntry chainEntry
;
5408 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5410 StorageImpl_ReadDirEntry(
5411 This
->parentStorage
,
5412 This
->ownerDirEntry
,
5415 chainEntry
.startingBlock
= blockIndex
;
5417 StorageImpl_WriteDirEntry(
5418 This
->parentStorage
,
5419 This
->ownerDirEntry
,
5423 This
->tailIndex
= blockIndex
;
5424 This
->numBlocks
= 1;
5428 * Figure out how many blocks are needed to contain this stream
5430 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5432 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5436 * Go to the current end of chain
5438 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5440 currentBlock
= blockIndex
;
5442 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5445 currentBlock
= blockIndex
;
5447 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5452 This
->tailIndex
= currentBlock
;
5455 currentBlock
= This
->tailIndex
;
5456 oldNumBlocks
= This
->numBlocks
;
5459 * Add new blocks to the chain
5461 if (oldNumBlocks
< newNumBlocks
)
5463 while (oldNumBlocks
< newNumBlocks
)
5465 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5467 StorageImpl_SetNextBlockInChain(
5468 This
->parentStorage
,
5472 StorageImpl_SetNextBlockInChain(
5473 This
->parentStorage
,
5475 BLOCK_END_OF_CHAIN
);
5477 currentBlock
= blockIndex
;
5481 This
->tailIndex
= blockIndex
;
5482 This
->numBlocks
= newNumBlocks
;
5488 /******************************************************************************
5489 * BlockChainStream_SetSize
5491 * Sets the size of this stream. The big block depot will be updated.
5492 * The file will grow if we grow the chain.
5494 * TODO: Free the actual blocks in the file when we shrink the chain.
5495 * Currently, the blocks are still in the file. So the file size
5496 * doesn't shrink even if we shrink streams.
5498 BOOL
BlockChainStream_SetSize(
5499 BlockChainStream
* This
,
5500 ULARGE_INTEGER newSize
)
5502 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5504 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5507 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5509 BlockChainStream_Shrink(This
, newSize
);
5513 BlockChainStream_Enlarge(This
, newSize
);
5519 /******************************************************************************
5520 * BlockChainStream_GetSize
5522 * Returns the size of this chain.
5523 * Will return the block count if this chain doesn't have a directory entry.
5525 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5527 DirEntry chainEntry
;
5529 if(This
->headOfStreamPlaceHolder
== NULL
)
5532 * This chain has a directory entry so use the size value from there.
5534 StorageImpl_ReadDirEntry(
5535 This
->parentStorage
,
5536 This
->ownerDirEntry
,
5539 return chainEntry
.size
;
5544 * this chain is a chain that does not have a directory entry, figure out the
5545 * size by making the product number of used blocks times the
5548 ULARGE_INTEGER result
;
5549 result
.u
.HighPart
= 0;
5552 BlockChainStream_GetCount(This
) *
5553 This
->parentStorage
->bigBlockSize
;
5559 /******************************************************************************
5560 ** SmallBlockChainStream implementation
5563 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5564 StorageImpl
* parentStorage
,
5565 ULONG
* headOfStreamPlaceHolder
,
5568 SmallBlockChainStream
* newStream
;
5570 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5572 newStream
->parentStorage
= parentStorage
;
5573 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5574 newStream
->ownerDirEntry
= dirEntry
;
5579 void SmallBlockChainStream_Destroy(
5580 SmallBlockChainStream
* This
)
5582 HeapFree(GetProcessHeap(), 0, This
);
5585 /******************************************************************************
5586 * SmallBlockChainStream_GetHeadOfChain
5588 * Returns the head of this chain of small blocks.
5590 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5591 SmallBlockChainStream
* This
)
5593 DirEntry chainEntry
;
5596 if (This
->headOfStreamPlaceHolder
!= NULL
)
5597 return *(This
->headOfStreamPlaceHolder
);
5599 if (This
->ownerDirEntry
)
5601 hr
= StorageImpl_ReadDirEntry(
5602 This
->parentStorage
,
5603 This
->ownerDirEntry
,
5608 return chainEntry
.startingBlock
;
5613 return BLOCK_END_OF_CHAIN
;
5616 /******************************************************************************
5617 * SmallBlockChainStream_GetNextBlockInChain
5619 * Returns the index of the next small block in this chain.
5622 * - BLOCK_END_OF_CHAIN: end of this chain
5623 * - BLOCK_UNUSED: small block 'blockIndex' is free
5625 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5626 SmallBlockChainStream
* This
,
5628 ULONG
* nextBlockInChain
)
5630 ULARGE_INTEGER offsetOfBlockInDepot
;
5635 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5637 offsetOfBlockInDepot
.u
.HighPart
= 0;
5638 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5641 * Read those bytes in the buffer from the small block file.
5643 res
= BlockChainStream_ReadAt(
5644 This
->parentStorage
->smallBlockDepotChain
,
5645 offsetOfBlockInDepot
,
5652 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5659 /******************************************************************************
5660 * SmallBlockChainStream_SetNextBlockInChain
5662 * Writes the index of the next block of the specified block in the small
5664 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5665 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5667 static void SmallBlockChainStream_SetNextBlockInChain(
5668 SmallBlockChainStream
* This
,
5672 ULARGE_INTEGER offsetOfBlockInDepot
;
5676 offsetOfBlockInDepot
.u
.HighPart
= 0;
5677 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5679 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5682 * Read those bytes in the buffer from the small block file.
5684 BlockChainStream_WriteAt(
5685 This
->parentStorage
->smallBlockDepotChain
,
5686 offsetOfBlockInDepot
,
5692 /******************************************************************************
5693 * SmallBlockChainStream_FreeBlock
5695 * Flag small block 'blockIndex' as free in the small block depot.
5697 static void SmallBlockChainStream_FreeBlock(
5698 SmallBlockChainStream
* This
,
5701 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5704 /******************************************************************************
5705 * SmallBlockChainStream_GetNextFreeBlock
5707 * Returns the index of a free small block. The small block depot will be
5708 * enlarged if necessary. The small block chain will also be enlarged if
5711 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5712 SmallBlockChainStream
* This
)
5714 ULARGE_INTEGER offsetOfBlockInDepot
;
5717 ULONG blockIndex
= 0;
5718 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5720 ULONG smallBlocksPerBigBlock
;
5722 offsetOfBlockInDepot
.u
.HighPart
= 0;
5725 * Scan the small block depot for a free block
5727 while (nextBlockIndex
!= BLOCK_UNUSED
)
5729 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5731 res
= BlockChainStream_ReadAt(
5732 This
->parentStorage
->smallBlockDepotChain
,
5733 offsetOfBlockInDepot
,
5739 * If we run out of space for the small block depot, enlarge it
5743 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5745 if (nextBlockIndex
!= BLOCK_UNUSED
)
5751 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5753 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5754 ULONG nextBlock
, newsbdIndex
;
5755 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5757 nextBlock
= sbdIndex
;
5758 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5760 sbdIndex
= nextBlock
;
5761 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5764 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5765 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5766 StorageImpl_SetNextBlockInChain(
5767 This
->parentStorage
,
5771 StorageImpl_SetNextBlockInChain(
5772 This
->parentStorage
,
5774 BLOCK_END_OF_CHAIN
);
5777 * Initialize all the small blocks to free
5779 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5780 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5785 * We have just created the small block depot.
5791 * Save it in the header
5793 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5794 StorageImpl_SaveFileHeader(This
->parentStorage
);
5797 * And allocate the first big block that will contain small blocks
5800 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5802 StorageImpl_SetNextBlockInChain(
5803 This
->parentStorage
,
5805 BLOCK_END_OF_CHAIN
);
5807 StorageImpl_ReadDirEntry(
5808 This
->parentStorage
,
5809 This
->parentStorage
->base
.storageDirEntry
,
5812 rootEntry
.startingBlock
= sbStartIndex
;
5813 rootEntry
.size
.u
.HighPart
= 0;
5814 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5816 StorageImpl_WriteDirEntry(
5817 This
->parentStorage
,
5818 This
->parentStorage
->base
.storageDirEntry
,
5822 StorageImpl_SaveFileHeader(This
->parentStorage
);
5826 smallBlocksPerBigBlock
=
5827 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5830 * Verify if we have to allocate big blocks to contain small blocks
5832 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5835 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5837 StorageImpl_ReadDirEntry(
5838 This
->parentStorage
,
5839 This
->parentStorage
->base
.storageDirEntry
,
5842 if (rootEntry
.size
.u
.LowPart
<
5843 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5845 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5847 BlockChainStream_SetSize(
5848 This
->parentStorage
->smallBlockRootChain
,
5851 StorageImpl_WriteDirEntry(
5852 This
->parentStorage
,
5853 This
->parentStorage
->base
.storageDirEntry
,
5861 /******************************************************************************
5862 * SmallBlockChainStream_ReadAt
5864 * Reads a specified number of bytes from this chain at the specified offset.
5865 * bytesRead may be NULL.
5866 * Failure will be returned if the specified number of bytes has not been read.
5868 HRESULT
SmallBlockChainStream_ReadAt(
5869 SmallBlockChainStream
* This
,
5870 ULARGE_INTEGER offset
,
5876 ULARGE_INTEGER offsetInBigBlockFile
;
5877 ULONG blockNoInSequence
=
5878 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5880 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5881 ULONG bytesToReadInBuffer
;
5883 ULONG bytesReadFromBigBlockFile
;
5887 * This should never happen on a small block file.
5889 assert(offset
.u
.HighPart
==0);
5892 * Find the first block in the stream that contains part of the buffer.
5894 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5896 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5898 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5901 blockNoInSequence
--;
5905 * Start reading the buffer.
5908 bufferWalker
= buffer
;
5910 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5913 * Calculate how many bytes we can copy from this small block.
5915 bytesToReadInBuffer
=
5916 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5919 * Calculate the offset of the small block in the small block file.
5921 offsetInBigBlockFile
.u
.HighPart
= 0;
5922 offsetInBigBlockFile
.u
.LowPart
=
5923 blockIndex
* This
->parentStorage
->smallBlockSize
;
5925 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5928 * Read those bytes in the buffer from the small block file.
5929 * The small block has already been identified so it shouldn't fail
5930 * unless the file is corrupt.
5932 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5933 offsetInBigBlockFile
,
5934 bytesToReadInBuffer
,
5936 &bytesReadFromBigBlockFile
);
5942 * Step to the next big block.
5944 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5946 return STG_E_DOCFILECORRUPT
;
5948 bufferWalker
+= bytesReadFromBigBlockFile
;
5949 size
-= bytesReadFromBigBlockFile
;
5950 *bytesRead
+= bytesReadFromBigBlockFile
;
5951 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5954 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5957 /******************************************************************************
5958 * SmallBlockChainStream_WriteAt
5960 * Writes the specified number of bytes to this chain at the specified offset.
5961 * Will fail if not all specified number of bytes have been written.
5963 HRESULT
SmallBlockChainStream_WriteAt(
5964 SmallBlockChainStream
* This
,
5965 ULARGE_INTEGER offset
,
5968 ULONG
* bytesWritten
)
5970 ULARGE_INTEGER offsetInBigBlockFile
;
5971 ULONG blockNoInSequence
=
5972 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5974 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5975 ULONG bytesToWriteInBuffer
;
5977 ULONG bytesWrittenToBigBlockFile
;
5978 const BYTE
* bufferWalker
;
5982 * This should never happen on a small block file.
5984 assert(offset
.u
.HighPart
==0);
5987 * Find the first block in the stream that contains part of the buffer.
5989 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5991 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5993 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5994 return STG_E_DOCFILECORRUPT
;
5995 blockNoInSequence
--;
5999 * Start writing the buffer.
6002 bufferWalker
= buffer
;
6003 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6006 * Calculate how many bytes we can copy to this small block.
6008 bytesToWriteInBuffer
=
6009 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6012 * Calculate the offset of the small block in the small block file.
6014 offsetInBigBlockFile
.u
.HighPart
= 0;
6015 offsetInBigBlockFile
.u
.LowPart
=
6016 blockIndex
* This
->parentStorage
->smallBlockSize
;
6018 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6021 * Write those bytes in the buffer to the small block file.
6023 res
= BlockChainStream_WriteAt(
6024 This
->parentStorage
->smallBlockRootChain
,
6025 offsetInBigBlockFile
,
6026 bytesToWriteInBuffer
,
6028 &bytesWrittenToBigBlockFile
);
6033 * Step to the next big block.
6035 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6038 bufferWalker
+= bytesWrittenToBigBlockFile
;
6039 size
-= bytesWrittenToBigBlockFile
;
6040 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6041 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6044 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6047 /******************************************************************************
6048 * SmallBlockChainStream_Shrink
6050 * Shrinks this chain in the small block depot.
6052 static BOOL
SmallBlockChainStream_Shrink(
6053 SmallBlockChainStream
* This
,
6054 ULARGE_INTEGER newSize
)
6056 ULONG blockIndex
, extraBlock
;
6060 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6062 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6065 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6068 * Go to the new end of chain
6070 while (count
< numBlocks
)
6072 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6079 * If the count is 0, we have a special case, the head of the chain was
6084 DirEntry chainEntry
;
6086 StorageImpl_ReadDirEntry(This
->parentStorage
,
6087 This
->ownerDirEntry
,
6090 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6092 StorageImpl_WriteDirEntry(This
->parentStorage
,
6093 This
->ownerDirEntry
,
6097 * We start freeing the chain at the head block.
6099 extraBlock
= blockIndex
;
6103 /* Get the next block before marking the new end */
6104 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6108 /* Mark the new end of chain */
6109 SmallBlockChainStream_SetNextBlockInChain(
6112 BLOCK_END_OF_CHAIN
);
6116 * Mark the extra blocks as free
6118 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6123 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6124 extraBlock
= blockIndex
;
6130 /******************************************************************************
6131 * SmallBlockChainStream_Enlarge
6133 * Grows this chain in the small block depot.
6135 static BOOL
SmallBlockChainStream_Enlarge(
6136 SmallBlockChainStream
* This
,
6137 ULARGE_INTEGER newSize
)
6139 ULONG blockIndex
, currentBlock
;
6141 ULONG oldNumBlocks
= 0;
6143 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6146 * Empty chain. Create the head.
6148 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6150 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6151 SmallBlockChainStream_SetNextBlockInChain(
6154 BLOCK_END_OF_CHAIN
);
6156 if (This
->headOfStreamPlaceHolder
!= NULL
)
6158 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6162 DirEntry chainEntry
;
6164 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6167 chainEntry
.startingBlock
= blockIndex
;
6169 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6174 currentBlock
= blockIndex
;
6177 * Figure out how many blocks are needed to contain this stream
6179 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6181 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6185 * Go to the current end of chain
6187 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6190 currentBlock
= blockIndex
;
6191 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6196 * Add new blocks to the chain
6198 while (oldNumBlocks
< newNumBlocks
)
6200 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6201 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6203 SmallBlockChainStream_SetNextBlockInChain(
6206 BLOCK_END_OF_CHAIN
);
6208 currentBlock
= blockIndex
;
6215 /******************************************************************************
6216 * SmallBlockChainStream_SetSize
6218 * Sets the size of this stream.
6219 * The file will grow if we grow the chain.
6221 * TODO: Free the actual blocks in the file when we shrink the chain.
6222 * Currently, the blocks are still in the file. So the file size
6223 * doesn't shrink even if we shrink streams.
6225 BOOL
SmallBlockChainStream_SetSize(
6226 SmallBlockChainStream
* This
,
6227 ULARGE_INTEGER newSize
)
6229 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6231 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6234 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6236 SmallBlockChainStream_Shrink(This
, newSize
);
6240 SmallBlockChainStream_Enlarge(This
, newSize
);
6246 /******************************************************************************
6247 * SmallBlockChainStream_GetCount
6249 * Returns the number of small blocks that comprises this chain.
6250 * This is not the size of the stream as the last block may not be full!
6253 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6258 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6260 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6265 blockIndex
, &blockIndex
)))
6272 /******************************************************************************
6273 * SmallBlockChainStream_GetSize
6275 * Returns the size of this chain.
6277 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6279 DirEntry chainEntry
;
6281 if(This
->headOfStreamPlaceHolder
!= NULL
)
6283 ULARGE_INTEGER result
;
6284 result
.u
.HighPart
= 0;
6286 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6287 This
->parentStorage
->smallBlockSize
;
6292 StorageImpl_ReadDirEntry(
6293 This
->parentStorage
,
6294 This
->ownerDirEntry
,
6297 return chainEntry
.size
;
6300 /******************************************************************************
6301 * StgCreateDocfile [OLE32.@]
6302 * Creates a new compound file storage object
6305 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6306 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6307 * reserved [ ?] unused?, usually 0
6308 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6311 * S_OK if the file was successfully created
6312 * some STG_E_ value if error
6314 * if pwcsName is NULL, create file with new unique name
6315 * the function can returns
6316 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6319 HRESULT WINAPI
StgCreateDocfile(
6323 IStorage
**ppstgOpen
)
6325 StorageBaseImpl
* newStorage
= 0;
6326 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6327 HRESULT hr
= STG_E_INVALIDFLAG
;
6331 DWORD fileAttributes
;
6332 WCHAR tempFileName
[MAX_PATH
];
6334 TRACE("(%s, %x, %d, %p)\n",
6335 debugstr_w(pwcsName
), grfMode
,
6336 reserved
, ppstgOpen
);
6339 return STG_E_INVALIDPOINTER
;
6341 return STG_E_INVALIDPARAMETER
;
6343 /* if no share mode given then DENY_NONE is the default */
6344 if (STGM_SHARE_MODE(grfMode
) == 0)
6345 grfMode
|= STGM_SHARE_DENY_NONE
;
6347 if ( FAILED( validateSTGM(grfMode
) ))
6350 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6351 switch(STGM_ACCESS_MODE(grfMode
))
6354 case STGM_READWRITE
:
6360 /* in direct mode, can only use SHARE_EXCLUSIVE */
6361 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6364 /* but in transacted mode, any share mode is valid */
6367 * Generate a unique name.
6371 WCHAR tempPath
[MAX_PATH
];
6372 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6374 memset(tempPath
, 0, sizeof(tempPath
));
6375 memset(tempFileName
, 0, sizeof(tempFileName
));
6377 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6380 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6381 pwcsName
= tempFileName
;
6384 hr
= STG_E_INSUFFICIENTMEMORY
;
6388 creationMode
= TRUNCATE_EXISTING
;
6392 creationMode
= GetCreationModeFromSTGM(grfMode
);
6396 * Interpret the STGM value grfMode
6398 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6399 accessMode
= GetAccessModeFromSTGM(grfMode
);
6401 if (grfMode
& STGM_DELETEONRELEASE
)
6402 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6404 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6406 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6407 FIXME("Storage share mode not implemented.\n");
6409 if (grfMode
& STGM_TRANSACTED
)
6410 FIXME("Transacted mode not implemented.\n");
6414 hFile
= CreateFileW(pwcsName
,
6422 if (hFile
== INVALID_HANDLE_VALUE
)
6424 if(GetLastError() == ERROR_FILE_EXISTS
)
6425 hr
= STG_E_FILEALREADYEXISTS
;
6432 * Allocate and initialize the new IStorage32object.
6434 hr
= Storage_Construct(
6449 * Get an "out" pointer for the caller.
6451 *ppstgOpen
= (IStorage
*)newStorage
;
6454 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6459 /******************************************************************************
6460 * StgCreateStorageEx [OLE32.@]
6462 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6464 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6465 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6467 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6469 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6470 return STG_E_INVALIDPARAMETER
;
6473 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6475 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6476 return STG_E_INVALIDPARAMETER
;
6479 if (stgfmt
== STGFMT_FILE
)
6481 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6482 return STG_E_INVALIDPARAMETER
;
6485 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6487 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6488 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6491 ERR("Invalid stgfmt argument\n");
6492 return STG_E_INVALIDPARAMETER
;
6495 /******************************************************************************
6496 * StgCreatePropSetStg [OLE32.@]
6498 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6499 IPropertySetStorage
**ppPropSetStg
)
6503 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6505 hr
= STG_E_INVALIDPARAMETER
;
6507 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6508 (void**)ppPropSetStg
);
6512 /******************************************************************************
6513 * StgOpenStorageEx [OLE32.@]
6515 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6517 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6518 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6520 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6522 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6523 return STG_E_INVALIDPARAMETER
;
6529 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6530 return STG_E_INVALIDPARAMETER
;
6532 case STGFMT_STORAGE
:
6535 case STGFMT_DOCFILE
:
6536 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6538 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6539 return STG_E_INVALIDPARAMETER
;
6541 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6545 WARN("STGFMT_ANY assuming storage\n");
6549 return STG_E_INVALIDPARAMETER
;
6552 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6556 /******************************************************************************
6557 * StgOpenStorage [OLE32.@]
6559 HRESULT WINAPI
StgOpenStorage(
6560 const OLECHAR
*pwcsName
,
6561 IStorage
*pstgPriority
,
6565 IStorage
**ppstgOpen
)
6567 StorageBaseImpl
* newStorage
= 0;
6573 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6574 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6575 snbExclude
, reserved
, ppstgOpen
);
6579 hr
= STG_E_INVALIDNAME
;
6585 hr
= STG_E_INVALIDPOINTER
;
6591 hr
= STG_E_INVALIDPARAMETER
;
6595 if (grfMode
& STGM_PRIORITY
)
6597 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6598 return STG_E_INVALIDFLAG
;
6599 if (grfMode
& STGM_DELETEONRELEASE
)
6600 return STG_E_INVALIDFUNCTION
;
6601 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6602 return STG_E_INVALIDFLAG
;
6603 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6604 grfMode
|= STGM_SHARE_DENY_NONE
;
6606 /* STGM_PRIORITY stops other IStorage objects on the same file from
6607 * committing until the STGM_PRIORITY IStorage is closed. it also
6608 * stops non-transacted mode StgOpenStorage calls with write access from
6609 * succeeding. obviously, both of these cannot be achieved through just
6610 * file share flags */
6611 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6615 * Validate the sharing mode
6617 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6618 switch(STGM_SHARE_MODE(grfMode
))
6620 case STGM_SHARE_EXCLUSIVE
:
6621 case STGM_SHARE_DENY_WRITE
:
6624 hr
= STG_E_INVALIDFLAG
;
6628 if ( FAILED( validateSTGM(grfMode
) ) ||
6629 (grfMode
&STGM_CREATE
))
6631 hr
= STG_E_INVALIDFLAG
;
6635 /* shared reading requires transacted mode */
6636 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6637 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6638 !(grfMode
&STGM_TRANSACTED
) )
6640 hr
= STG_E_INVALIDFLAG
;
6645 * Interpret the STGM value grfMode
6647 shareMode
= GetShareModeFromSTGM(grfMode
);
6648 accessMode
= GetAccessModeFromSTGM(grfMode
);
6652 hFile
= CreateFileW( pwcsName
,
6657 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6660 if (hFile
==INVALID_HANDLE_VALUE
)
6662 DWORD last_error
= GetLastError();
6668 case ERROR_FILE_NOT_FOUND
:
6669 hr
= STG_E_FILENOTFOUND
;
6672 case ERROR_PATH_NOT_FOUND
:
6673 hr
= STG_E_PATHNOTFOUND
;
6676 case ERROR_ACCESS_DENIED
:
6677 case ERROR_WRITE_PROTECT
:
6678 hr
= STG_E_ACCESSDENIED
;
6681 case ERROR_SHARING_VIOLATION
:
6682 hr
= STG_E_SHAREVIOLATION
;
6693 * Refuse to open the file if it's too small to be a structured storage file
6694 * FIXME: verify the file when reading instead of here
6696 if (GetFileSize(hFile
, NULL
) < 0x100)
6699 hr
= STG_E_FILEALREADYEXISTS
;
6704 * Allocate and initialize the new IStorage32object.
6706 hr
= Storage_Construct(
6718 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6720 if(hr
== STG_E_INVALIDHEADER
)
6721 hr
= STG_E_FILEALREADYEXISTS
;
6726 * Get an "out" pointer for the caller.
6728 *ppstgOpen
= (IStorage
*)newStorage
;
6731 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6735 /******************************************************************************
6736 * StgCreateDocfileOnILockBytes [OLE32.@]
6738 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6742 IStorage
** ppstgOpen
)
6744 StorageBaseImpl
* newStorage
= 0;
6747 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6748 return STG_E_INVALIDPOINTER
;
6751 * Allocate and initialize the new IStorage object.
6753 hr
= Storage_Construct(
6768 * Get an "out" pointer for the caller.
6770 *ppstgOpen
= (IStorage
*)newStorage
;
6775 /******************************************************************************
6776 * StgOpenStorageOnILockBytes [OLE32.@]
6778 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6780 IStorage
*pstgPriority
,
6784 IStorage
**ppstgOpen
)
6786 StorageBaseImpl
* newStorage
= 0;
6789 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6790 return STG_E_INVALIDPOINTER
;
6792 if ( FAILED( validateSTGM(grfMode
) ))
6793 return STG_E_INVALIDFLAG
;
6798 * Allocate and initialize the new IStorage object.
6800 hr
= Storage_Construct(
6815 * Get an "out" pointer for the caller.
6817 *ppstgOpen
= (IStorage
*)newStorage
;
6822 /******************************************************************************
6823 * StgSetTimes [ole32.@]
6824 * StgSetTimes [OLE32.@]
6828 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6829 FILETIME
const *patime
, FILETIME
const *pmtime
)
6831 IStorage
*stg
= NULL
;
6834 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6836 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6840 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6841 IStorage_Release(stg
);
6847 /******************************************************************************
6848 * StgIsStorageILockBytes [OLE32.@]
6850 * Determines if the ILockBytes contains a storage object.
6852 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6855 ULARGE_INTEGER offset
;
6857 offset
.u
.HighPart
= 0;
6858 offset
.u
.LowPart
= 0;
6860 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6862 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6868 /******************************************************************************
6869 * WriteClassStg [OLE32.@]
6871 * This method will store the specified CLSID in the specified storage object
6873 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6878 return E_INVALIDARG
;
6881 return STG_E_INVALIDPOINTER
;
6883 hRes
= IStorage_SetClass(pStg
, rclsid
);
6888 /***********************************************************************
6889 * ReadClassStg (OLE32.@)
6891 * This method reads the CLSID previously written to a storage object with
6892 * the WriteClassStg.
6895 * pstg [I] IStorage pointer
6896 * pclsid [O] Pointer to where the CLSID is written
6900 * Failure: HRESULT code.
6902 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6907 TRACE("(%p, %p)\n", pstg
, pclsid
);
6909 if(!pstg
|| !pclsid
)
6910 return E_INVALIDARG
;
6913 * read a STATSTG structure (contains the clsid) from the storage
6915 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6918 *pclsid
=pstatstg
.clsid
;
6923 /***********************************************************************
6924 * OleLoadFromStream (OLE32.@)
6926 * This function loads an object from stream
6928 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6932 LPPERSISTSTREAM xstm
;
6934 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6936 res
=ReadClassStm(pStm
,&clsid
);
6939 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6942 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6944 IUnknown_Release((IUnknown
*)*ppvObj
);
6947 res
=IPersistStream_Load(xstm
,pStm
);
6948 IPersistStream_Release(xstm
);
6949 /* FIXME: all refcounts ok at this point? I think they should be:
6952 * xstm : 0 (released)
6957 /***********************************************************************
6958 * OleSaveToStream (OLE32.@)
6960 * This function saves an object with the IPersistStream interface on it
6961 * to the specified stream.
6963 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6969 TRACE("(%p,%p)\n",pPStm
,pStm
);
6971 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6973 if (SUCCEEDED(res
)){
6975 res
=WriteClassStm(pStm
,&clsid
);
6979 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6982 TRACE("Finished Save\n");
6986 /****************************************************************************
6987 * This method validate a STGM parameter that can contain the values below
6989 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6990 * The stgm values contained in 0xffff0000 are bitmasks.
6992 * STGM_DIRECT 0x00000000
6993 * STGM_TRANSACTED 0x00010000
6994 * STGM_SIMPLE 0x08000000
6996 * STGM_READ 0x00000000
6997 * STGM_WRITE 0x00000001
6998 * STGM_READWRITE 0x00000002
7000 * STGM_SHARE_DENY_NONE 0x00000040
7001 * STGM_SHARE_DENY_READ 0x00000030
7002 * STGM_SHARE_DENY_WRITE 0x00000020
7003 * STGM_SHARE_EXCLUSIVE 0x00000010
7005 * STGM_PRIORITY 0x00040000
7006 * STGM_DELETEONRELEASE 0x04000000
7008 * STGM_CREATE 0x00001000
7009 * STGM_CONVERT 0x00020000
7010 * STGM_FAILIFTHERE 0x00000000
7012 * STGM_NOSCRATCH 0x00100000
7013 * STGM_NOSNAPSHOT 0x00200000
7015 static HRESULT
validateSTGM(DWORD stgm
)
7017 DWORD access
= STGM_ACCESS_MODE(stgm
);
7018 DWORD share
= STGM_SHARE_MODE(stgm
);
7019 DWORD create
= STGM_CREATE_MODE(stgm
);
7021 if (stgm
&~STGM_KNOWN_FLAGS
)
7023 ERR("unknown flags %08x\n", stgm
);
7031 case STGM_READWRITE
:
7039 case STGM_SHARE_DENY_NONE
:
7040 case STGM_SHARE_DENY_READ
:
7041 case STGM_SHARE_DENY_WRITE
:
7042 case STGM_SHARE_EXCLUSIVE
:
7051 case STGM_FAILIFTHERE
:
7058 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7060 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7064 * STGM_CREATE | STGM_CONVERT
7065 * if both are false, STGM_FAILIFTHERE is set to TRUE
7067 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7071 * STGM_NOSCRATCH requires STGM_TRANSACTED
7073 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7077 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7078 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7080 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7081 (!(stgm
& STGM_TRANSACTED
) ||
7082 share
== STGM_SHARE_EXCLUSIVE
||
7083 share
== STGM_SHARE_DENY_WRITE
) )
7089 /****************************************************************************
7090 * GetShareModeFromSTGM
7092 * This method will return a share mode flag from a STGM value.
7093 * The STGM value is assumed valid.
7095 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7097 switch (STGM_SHARE_MODE(stgm
))
7099 case STGM_SHARE_DENY_NONE
:
7100 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7101 case STGM_SHARE_DENY_READ
:
7102 return FILE_SHARE_WRITE
;
7103 case STGM_SHARE_DENY_WRITE
:
7104 return FILE_SHARE_READ
;
7105 case STGM_SHARE_EXCLUSIVE
:
7108 ERR("Invalid share mode!\n");
7113 /****************************************************************************
7114 * GetAccessModeFromSTGM
7116 * This method will return an access mode flag from a STGM value.
7117 * The STGM value is assumed valid.
7119 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7121 switch (STGM_ACCESS_MODE(stgm
))
7124 return GENERIC_READ
;
7126 case STGM_READWRITE
:
7127 return GENERIC_READ
| GENERIC_WRITE
;
7129 ERR("Invalid access mode!\n");
7134 /****************************************************************************
7135 * GetCreationModeFromSTGM
7137 * This method will return a creation mode flag from a STGM value.
7138 * The STGM value is assumed valid.
7140 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7142 switch(STGM_CREATE_MODE(stgm
))
7145 return CREATE_ALWAYS
;
7147 FIXME("STGM_CONVERT not implemented!\n");
7149 case STGM_FAILIFTHERE
:
7152 ERR("Invalid create mode!\n");
7158 /*************************************************************************
7159 * OLECONVERT_LoadOLE10 [Internal]
7161 * Loads the OLE10 STREAM to memory
7164 * pOleStream [I] The OLESTREAM
7165 * pData [I] Data Structure for the OLESTREAM Data
7169 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7170 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7173 * This function is used by OleConvertOLESTREAMToIStorage only.
7175 * Memory allocated for pData must be freed by the caller
7177 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7180 HRESULT hRes
= S_OK
;
7184 pData
->pData
= NULL
;
7185 pData
->pstrOleObjFileName
= NULL
;
7187 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7190 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7191 if(dwSize
!= sizeof(pData
->dwOleID
))
7193 hRes
= CONVERT10_E_OLESTREAM_GET
;
7195 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7197 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7208 /* Get the TypeID... more info needed for this field */
7209 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7210 if(dwSize
!= sizeof(pData
->dwTypeID
))
7212 hRes
= CONVERT10_E_OLESTREAM_GET
;
7217 if(pData
->dwTypeID
!= 0)
7219 /* Get the length of the OleTypeName */
7220 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7221 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7223 hRes
= CONVERT10_E_OLESTREAM_GET
;
7228 if(pData
->dwOleTypeNameLength
> 0)
7230 /* Get the OleTypeName */
7231 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7232 if(dwSize
!= pData
->dwOleTypeNameLength
)
7234 hRes
= CONVERT10_E_OLESTREAM_GET
;
7240 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7241 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7243 hRes
= CONVERT10_E_OLESTREAM_GET
;
7247 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7248 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7249 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7250 if(pData
->pstrOleObjFileName
)
7252 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7253 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7255 hRes
= CONVERT10_E_OLESTREAM_GET
;
7259 hRes
= CONVERT10_E_OLESTREAM_GET
;
7264 /* Get the Width of the Metafile */
7265 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7266 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7268 hRes
= CONVERT10_E_OLESTREAM_GET
;
7272 /* Get the Height of the Metafile */
7273 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7274 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7276 hRes
= CONVERT10_E_OLESTREAM_GET
;
7282 /* Get the Length of the Data */
7283 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7284 if(dwSize
!= sizeof(pData
->dwDataLength
))
7286 hRes
= CONVERT10_E_OLESTREAM_GET
;
7290 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7292 if(!bStrem1
) /* if it is a second OLE stream data */
7294 pData
->dwDataLength
-= 8;
7295 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7296 if(dwSize
!= sizeof(pData
->strUnknown
))
7298 hRes
= CONVERT10_E_OLESTREAM_GET
;
7304 if(pData
->dwDataLength
> 0)
7306 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7308 /* Get Data (ex. IStorage, Metafile, or BMP) */
7311 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7312 if(dwSize
!= pData
->dwDataLength
)
7314 hRes
= CONVERT10_E_OLESTREAM_GET
;
7319 hRes
= CONVERT10_E_OLESTREAM_GET
;
7328 /*************************************************************************
7329 * OLECONVERT_SaveOLE10 [Internal]
7331 * Saves the OLE10 STREAM From memory
7334 * pData [I] Data Structure for the OLESTREAM Data
7335 * pOleStream [I] The OLESTREAM to save
7339 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7342 * This function is used by OleConvertIStorageToOLESTREAM only.
7345 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7348 HRESULT hRes
= S_OK
;
7352 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7353 if(dwSize
!= sizeof(pData
->dwOleID
))
7355 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7360 /* Set the TypeID */
7361 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7362 if(dwSize
!= sizeof(pData
->dwTypeID
))
7364 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7368 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7370 /* Set the Length of the OleTypeName */
7371 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7372 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7374 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7379 if(pData
->dwOleTypeNameLength
> 0)
7381 /* Set the OleTypeName */
7382 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7383 if(dwSize
!= pData
->dwOleTypeNameLength
)
7385 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7392 /* Set the width of the Metafile */
7393 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7394 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7396 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7402 /* Set the height of the Metafile */
7403 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7404 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7406 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7412 /* Set the length of the Data */
7413 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7414 if(dwSize
!= sizeof(pData
->dwDataLength
))
7416 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7422 if(pData
->dwDataLength
> 0)
7424 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7425 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7426 if(dwSize
!= pData
->dwDataLength
)
7428 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7436 /*************************************************************************
7437 * OLECONVERT_GetOLE20FromOLE10[Internal]
7439 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7440 * opens it, and copies the content to the dest IStorage for
7441 * OleConvertOLESTREAMToIStorage
7445 * pDestStorage [I] The IStorage to copy the data to
7446 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7447 * nBufferLength [I] The size of the buffer
7456 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7460 IStorage
*pTempStorage
;
7461 DWORD dwNumOfBytesWritten
;
7462 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7463 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7465 /* Create a temp File */
7466 GetTempPathW(MAX_PATH
, wstrTempDir
);
7467 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7468 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7470 if(hFile
!= INVALID_HANDLE_VALUE
)
7472 /* Write IStorage Data to File */
7473 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7476 /* Open and copy temp storage to the Dest Storage */
7477 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7480 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7481 IStorage_Release(pTempStorage
);
7483 DeleteFileW(wstrTempFile
);
7488 /*************************************************************************
7489 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7491 * Saves the OLE10 STREAM From memory
7494 * pStorage [I] The Src IStorage to copy
7495 * pData [I] The Dest Memory to write to.
7498 * The size in bytes allocated for pData
7501 * Memory allocated for pData must be freed by the caller
7503 * Used by OleConvertIStorageToOLESTREAM only.
7506 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7510 DWORD nDataLength
= 0;
7511 IStorage
*pTempStorage
;
7512 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7513 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7517 /* Create temp Storage */
7518 GetTempPathW(MAX_PATH
, wstrTempDir
);
7519 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7520 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7524 /* Copy Src Storage to the Temp Storage */
7525 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7526 IStorage_Release(pTempStorage
);
7528 /* Open Temp Storage as a file and copy to memory */
7529 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7530 if(hFile
!= INVALID_HANDLE_VALUE
)
7532 nDataLength
= GetFileSize(hFile
, NULL
);
7533 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7534 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7537 DeleteFileW(wstrTempFile
);
7542 /*************************************************************************
7543 * OLECONVERT_CreateOleStream [Internal]
7545 * Creates the "\001OLE" stream in the IStorage if necessary.
7548 * pStorage [I] Dest storage to create the stream in
7554 * This function is used by OleConvertOLESTREAMToIStorage only.
7556 * This stream is still unknown, MS Word seems to have extra data
7557 * but since the data is stored in the OLESTREAM there should be
7558 * no need to recreate the stream. If the stream is manually
7559 * deleted it will create it with this default data.
7562 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7566 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7567 BYTE pOleStreamHeader
[] =
7569 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7571 0x00, 0x00, 0x00, 0x00
7574 /* Create stream if not present */
7575 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7576 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7580 /* Write default Data */
7581 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7582 IStream_Release(pStream
);
7586 /* write a string to a stream, preceded by its length */
7587 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7594 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7595 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7600 str
= CoTaskMemAlloc( len
);
7601 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7602 r
= IStream_Write( stm
, str
, len
, NULL
);
7603 CoTaskMemFree( str
);
7607 /* read a string preceded by its length from a stream */
7608 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7611 DWORD len
, count
= 0;
7615 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7618 if( count
!= sizeof(len
) )
7619 return E_OUTOFMEMORY
;
7621 TRACE("%d bytes\n",len
);
7623 str
= CoTaskMemAlloc( len
);
7625 return E_OUTOFMEMORY
;
7627 r
= IStream_Read( stm
, str
, len
, &count
);
7632 CoTaskMemFree( str
);
7633 return E_OUTOFMEMORY
;
7636 TRACE("Read string %s\n",debugstr_an(str
,len
));
7638 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7639 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7641 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7642 CoTaskMemFree( str
);
7650 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7651 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7655 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7657 static const BYTE unknown1
[12] =
7658 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7659 0xFF, 0xFF, 0xFF, 0xFF};
7660 static const BYTE unknown2
[16] =
7661 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7662 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7664 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7665 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7666 debugstr_w(szProgIDName
));
7668 /* Create a CompObj stream */
7669 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7670 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7674 /* Write CompObj Structure to stream */
7675 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7677 if( SUCCEEDED( r
) )
7678 r
= WriteClassStm( pstm
, clsid
);
7680 if( SUCCEEDED( r
) )
7681 r
= STREAM_WriteString( pstm
, lpszUserType
);
7682 if( SUCCEEDED( r
) )
7683 r
= STREAM_WriteString( pstm
, szClipName
);
7684 if( SUCCEEDED( r
) )
7685 r
= STREAM_WriteString( pstm
, szProgIDName
);
7686 if( SUCCEEDED( r
) )
7687 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7689 IStream_Release( pstm
);
7694 /***********************************************************************
7695 * WriteFmtUserTypeStg (OLE32.@)
7697 HRESULT WINAPI
WriteFmtUserTypeStg(
7698 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7701 WCHAR szwClipName
[0x40];
7702 CLSID clsid
= CLSID_NULL
;
7703 LPWSTR wstrProgID
= NULL
;
7706 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7708 /* get the clipboard format name */
7709 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7712 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7714 /* FIXME: There's room to save a CLSID and its ProgID, but
7715 the CLSID is not looked up in the registry and in all the
7716 tests I wrote it was CLSID_NULL. Where does it come from?
7719 /* get the real program ID. This may fail, but that's fine */
7720 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7722 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7724 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7725 lpszUserType
, szwClipName
, wstrProgID
);
7727 CoTaskMemFree(wstrProgID
);
7733 /******************************************************************************
7734 * ReadFmtUserTypeStg [OLE32.@]
7736 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7740 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7741 unsigned char unknown1
[12];
7742 unsigned char unknown2
[16];
7744 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7747 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7749 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7750 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7753 WARN("Failed to open stream r = %08x\n", r
);
7757 /* read the various parts of the structure */
7758 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7759 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7761 r
= ReadClassStm( stm
, &clsid
);
7765 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7769 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7773 r
= STREAM_ReadString( stm
, &szProgIDName
);
7777 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7778 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7781 /* ok, success... now we just need to store what we found */
7783 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7784 CoTaskMemFree( szOleTypeName
);
7786 if( lplpszUserType
)
7787 *lplpszUserType
= szCLSIDName
;
7788 CoTaskMemFree( szProgIDName
);
7791 IStream_Release( stm
);
7797 /*************************************************************************
7798 * OLECONVERT_CreateCompObjStream [Internal]
7800 * Creates a "\001CompObj" is the destination IStorage if necessary.
7803 * pStorage [I] The dest IStorage to create the CompObj Stream
7805 * strOleTypeName [I] The ProgID
7809 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7812 * This function is used by OleConvertOLESTREAMToIStorage only.
7814 * The stream data is stored in the OLESTREAM and there should be
7815 * no need to recreate the stream. If the stream is manually
7816 * deleted it will attempt to create it by querying the registry.
7820 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7823 HRESULT hStorageRes
, hRes
= S_OK
;
7824 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7825 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7826 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7828 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7829 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7831 /* Initialize the CompObj structure */
7832 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7833 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7834 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7837 /* Create a CompObj stream if it doesn't exist */
7838 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7839 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7840 if(hStorageRes
== S_OK
)
7842 /* copy the OleTypeName to the compobj struct */
7843 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7844 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7846 /* copy the OleTypeName to the compobj struct */
7847 /* Note: in the test made, these were Identical */
7848 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7849 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7852 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7853 bufferW
, OLESTREAM_MAX_STR_LEN
);
7854 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7860 /* Get the CLSID Default Name from the Registry */
7861 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7862 if(hErr
== ERROR_SUCCESS
)
7864 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7865 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7866 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7867 if(hErr
== ERROR_SUCCESS
)
7869 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7875 /* Write CompObj Structure to stream */
7876 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7878 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7880 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7881 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7883 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7885 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7886 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7888 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7890 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7891 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7893 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7895 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7896 IStream_Release(pStream
);
7902 /*************************************************************************
7903 * OLECONVERT_CreateOlePresStream[Internal]
7905 * Creates the "\002OlePres000" Stream with the Metafile data
7908 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7909 * dwExtentX [I] Width of the Metafile
7910 * dwExtentY [I] Height of the Metafile
7911 * pData [I] Metafile data
7912 * dwDataLength [I] Size of the Metafile data
7916 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7919 * This function is used by OleConvertOLESTREAMToIStorage only.
7922 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7926 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7927 BYTE pOlePresStreamHeader
[] =
7929 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7930 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7931 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7932 0x00, 0x00, 0x00, 0x00
7935 BYTE pOlePresStreamHeaderEmpty
[] =
7937 0x00, 0x00, 0x00, 0x00,
7938 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7939 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7940 0x00, 0x00, 0x00, 0x00
7943 /* Create the OlePres000 Stream */
7944 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7945 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7950 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7952 memset(&OlePres
, 0, sizeof(OlePres
));
7953 /* Do we have any metafile data to save */
7954 if(dwDataLength
> 0)
7956 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7957 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7961 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7962 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7964 /* Set width and height of the metafile */
7965 OlePres
.dwExtentX
= dwExtentX
;
7966 OlePres
.dwExtentY
= -dwExtentY
;
7968 /* Set Data and Length */
7969 if(dwDataLength
> sizeof(METAFILEPICT16
))
7971 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7972 OlePres
.pData
= &(pData
[8]);
7974 /* Save OlePres000 Data to Stream */
7975 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7976 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7977 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7978 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7979 if(OlePres
.dwSize
> 0)
7981 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7983 IStream_Release(pStream
);
7987 /*************************************************************************
7988 * OLECONVERT_CreateOle10NativeStream [Internal]
7990 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7993 * pStorage [I] Dest storage to create the stream in
7994 * pData [I] Ole10 Native Data (ex. bmp)
7995 * dwDataLength [I] Size of the Ole10 Native Data
8001 * This function is used by OleConvertOLESTREAMToIStorage only.
8003 * Might need to verify the data and return appropriate error message
8006 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8010 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8012 /* Create the Ole10Native Stream */
8013 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8014 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8018 /* Write info to stream */
8019 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8020 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8021 IStream_Release(pStream
);
8026 /*************************************************************************
8027 * OLECONVERT_GetOLE10ProgID [Internal]
8029 * Finds the ProgID (or OleTypeID) from the IStorage
8032 * pStorage [I] The Src IStorage to get the ProgID
8033 * strProgID [I] the ProgID string to get
8034 * dwSize [I] the size of the string
8038 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8041 * This function is used by OleConvertIStorageToOLESTREAM only.
8045 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8049 LARGE_INTEGER iSeekPos
;
8050 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8051 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8053 /* Open the CompObj Stream */
8054 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8055 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8059 /*Get the OleType from the CompObj Stream */
8060 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8061 iSeekPos
.u
.HighPart
= 0;
8063 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8064 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8065 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8066 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8067 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8068 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8069 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8071 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8074 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8076 IStream_Release(pStream
);
8081 LPOLESTR wstrProgID
;
8083 /* Get the OleType from the registry */
8084 REFCLSID clsid
= &(stat
.clsid
);
8085 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8086 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8089 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8096 /*************************************************************************
8097 * OLECONVERT_GetOle10PresData [Internal]
8099 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8102 * pStorage [I] Src IStroage
8103 * pOleStream [I] Dest OleStream Mem Struct
8109 * This function is used by OleConvertIStorageToOLESTREAM only.
8111 * Memory allocated for pData must be freed by the caller
8115 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8120 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8122 /* Initialize Default data for OLESTREAM */
8123 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8124 pOleStreamData
[0].dwTypeID
= 2;
8125 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8126 pOleStreamData
[1].dwTypeID
= 0;
8127 pOleStreamData
[0].dwMetaFileWidth
= 0;
8128 pOleStreamData
[0].dwMetaFileHeight
= 0;
8129 pOleStreamData
[0].pData
= NULL
;
8130 pOleStreamData
[1].pData
= NULL
;
8132 /* Open Ole10Native Stream */
8133 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8134 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8138 /* Read Size and Data */
8139 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8140 if(pOleStreamData
->dwDataLength
> 0)
8142 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8143 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8145 IStream_Release(pStream
);
8151 /*************************************************************************
8152 * OLECONVERT_GetOle20PresData[Internal]
8154 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8157 * pStorage [I] Src IStroage
8158 * pOleStreamData [I] Dest OleStream Mem Struct
8164 * This function is used by OleConvertIStorageToOLESTREAM only.
8166 * Memory allocated for pData must be freed by the caller
8168 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8172 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8173 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8175 /* Initialize Default data for OLESTREAM */
8176 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8177 pOleStreamData
[0].dwTypeID
= 2;
8178 pOleStreamData
[0].dwMetaFileWidth
= 0;
8179 pOleStreamData
[0].dwMetaFileHeight
= 0;
8180 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8181 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8182 pOleStreamData
[1].dwTypeID
= 0;
8183 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8184 pOleStreamData
[1].strOleTypeName
[0] = 0;
8185 pOleStreamData
[1].dwMetaFileWidth
= 0;
8186 pOleStreamData
[1].dwMetaFileHeight
= 0;
8187 pOleStreamData
[1].pData
= NULL
;
8188 pOleStreamData
[1].dwDataLength
= 0;
8191 /* Open OlePress000 stream */
8192 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8193 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8196 LARGE_INTEGER iSeekPos
;
8197 METAFILEPICT16 MetaFilePict
;
8198 static const char strMetafilePictName
[] = "METAFILEPICT";
8200 /* Set the TypeID for a Metafile */
8201 pOleStreamData
[1].dwTypeID
= 5;
8203 /* Set the OleTypeName to Metafile */
8204 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8205 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8207 iSeekPos
.u
.HighPart
= 0;
8208 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8210 /* Get Presentation Data */
8211 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8212 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8213 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8214 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8216 /*Set width and Height */
8217 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8218 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8219 if(olePress
.dwSize
> 0)
8222 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8224 /* Set MetaFilePict struct */
8225 MetaFilePict
.mm
= 8;
8226 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8227 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8228 MetaFilePict
.hMF
= 0;
8230 /* Get Metafile Data */
8231 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8232 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8233 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8235 IStream_Release(pStream
);
8239 /*************************************************************************
8240 * OleConvertOLESTREAMToIStorage [OLE32.@]
8245 * DVTARGETDEVICE parameter is not handled
8246 * Still unsure of some mem fields for OLE 10 Stream
8247 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8248 * and "\001OLE" streams
8251 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8252 LPOLESTREAM pOleStream
,
8254 const DVTARGETDEVICE
* ptd
)
8258 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8260 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8262 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8266 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8269 if(pstg
== NULL
|| pOleStream
== NULL
)
8271 hRes
= E_INVALIDARG
;
8276 /* Load the OLESTREAM to Memory */
8277 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8282 /* Load the OLESTREAM to Memory (part 2)*/
8283 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8289 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8291 /* Do we have the IStorage Data in the OLESTREAM */
8292 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8294 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8295 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8299 /* It must be an original OLE 1.0 source */
8300 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8305 /* It must be an original OLE 1.0 source */
8306 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8309 /* Create CompObj Stream if necessary */
8310 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8313 /*Create the Ole Stream if necessary */
8314 OLECONVERT_CreateOleStream(pstg
);
8319 /* Free allocated memory */
8320 for(i
=0; i
< 2; i
++)
8322 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8323 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8324 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8329 /*************************************************************************
8330 * OleConvertIStorageToOLESTREAM [OLE32.@]
8337 * Still unsure of some mem fields for OLE 10 Stream
8338 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8339 * and "\001OLE" streams.
8342 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8344 LPOLESTREAM pOleStream
)
8347 HRESULT hRes
= S_OK
;
8349 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8350 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8352 TRACE("%p %p\n", pstg
, pOleStream
);
8354 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8356 if(pstg
== NULL
|| pOleStream
== NULL
)
8358 hRes
= E_INVALIDARG
;
8362 /* Get the ProgID */
8363 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8364 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8368 /* Was it originally Ole10 */
8369 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8372 IStream_Release(pStream
);
8373 /* Get Presentation Data for Ole10Native */
8374 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8378 /* Get Presentation Data (OLE20) */
8379 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8382 /* Save OLESTREAM */
8383 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8386 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8391 /* Free allocated memory */
8392 for(i
=0; i
< 2; i
++)
8394 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8400 /***********************************************************************
8401 * GetConvertStg (OLE32.@)
8403 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8404 FIXME("unimplemented stub!\n");
8408 /******************************************************************************
8409 * StgIsStorageFile [OLE32.@]
8410 * Verify if the file contains a storage object
8416 * S_OK if file has magic bytes as a storage object
8417 * S_FALSE if file is not storage
8420 StgIsStorageFile(LPCOLESTR fn
)
8426 TRACE("%s\n", debugstr_w(fn
));
8427 hf
= CreateFileW(fn
, GENERIC_READ
,
8428 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8429 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8431 if (hf
== INVALID_HANDLE_VALUE
)
8432 return STG_E_FILENOTFOUND
;
8434 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8436 WARN(" unable to read file\n");
8443 if (bytes_read
!= 8) {
8444 WARN(" too short\n");
8448 if (!memcmp(magic
,STORAGE_magic
,8)) {
8453 WARN(" -> Invalid header.\n");
8457 /***********************************************************************
8458 * WriteClassStm (OLE32.@)
8460 * Writes a CLSID to a stream.
8463 * pStm [I] Stream to write to.
8464 * rclsid [I] CLSID to write.
8468 * Failure: HRESULT code.
8470 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8472 TRACE("(%p,%p)\n",pStm
,rclsid
);
8474 if (!pStm
|| !rclsid
)
8475 return E_INVALIDARG
;
8477 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8480 /***********************************************************************
8481 * ReadClassStm (OLE32.@)
8483 * Reads a CLSID from a stream.
8486 * pStm [I] Stream to read from.
8487 * rclsid [O] CLSID to read.
8491 * Failure: HRESULT code.
8493 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8498 TRACE("(%p,%p)\n",pStm
,pclsid
);
8500 if (!pStm
|| !pclsid
)
8501 return E_INVALIDARG
;
8503 /* clear the output args */
8504 *pclsid
= CLSID_NULL
;
8506 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8511 if (nbByte
!= sizeof(CLSID
))
8512 return STG_E_READFAULT
;