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 typedef struct TransactedDirEntry
123 /* If applicable, a reference to the original DirEntry in the transacted
124 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
125 DirRef transactedParentEntry
;
127 /* True if this entry is being used. */
130 /* True if data is up to date. */
133 /* True if this entry has been modified. */
136 /* True if this entry's stream has been modified. */
139 /* True if this entry has been deleted in the transacted storage, but the
140 * delete has not yet been committed. */
143 /* If this entry's stream has been modified, a reference to where the stream
144 * is stored in the snapshot file. */
147 /* This directory entry's data, including any changes that have been made. */
150 /* A reference to the parent of this node. This is only valid while we are
151 * committing changes. */
154 /* A reference to a newly-created entry in the transacted parent. This is
155 * always equal to transactedParentEntry except when committing changes. */
156 DirRef newTransactedParentEntry
;
157 } TransactedDirEntry
;
159 /****************************************************************************
160 * Transacted storage object.
162 typedef struct TransactedSnapshotImpl
164 struct StorageBaseImpl base
;
167 * Modified streams are temporarily saved to the scratch file.
169 StorageBaseImpl
*scratch
;
171 /* The directory structure is kept here, so that we can track how these
172 * entries relate to those in the parent storage. */
173 TransactedDirEntry
*entries
;
175 ULONG firstFreeEntry
;
178 * Changes are committed to the transacted parent.
180 StorageBaseImpl
*transactedParent
;
181 } TransactedSnapshotImpl
;
183 /* Generic function to create a transacted wrapper for a direct storage object. */
184 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
186 /* OLESTREAM memory structure to use for Get and Put Routines */
187 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
192 DWORD dwOleTypeNameLength
;
193 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
194 CHAR
*pstrOleObjFileName
;
195 DWORD dwOleObjFileNameLength
;
196 DWORD dwMetaFileWidth
;
197 DWORD dwMetaFileHeight
;
198 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
201 }OLECONVERT_OLESTREAM_DATA
;
203 /* CompObj Stream structure */
204 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
209 DWORD dwCLSIDNameLength
;
210 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
211 DWORD dwOleTypeNameLength
;
212 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
213 DWORD dwProgIDNameLength
;
214 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
216 }OLECONVERT_ISTORAGE_COMPOBJ
;
219 /* Ole Presentation Stream structure */
220 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
228 }OLECONVERT_ISTORAGE_OLEPRES
;
232 /***********************************************************************
233 * Forward declaration of internal functions used by the method DestroyElement
235 static HRESULT
deleteStorageContents(
236 StorageBaseImpl
*parentStorage
,
237 DirRef indexToDelete
,
238 DirEntry entryDataToDelete
);
240 static HRESULT
deleteStreamContents(
241 StorageBaseImpl
*parentStorage
,
242 DirRef indexToDelete
,
243 DirEntry entryDataToDelete
);
245 static HRESULT
removeFromTree(
246 StorageBaseImpl
*This
,
247 DirRef parentStorageIndex
,
248 DirRef deletedIndex
);
250 /***********************************************************************
251 * Declaration of the functions used to manipulate DirEntry
254 static HRESULT
insertIntoTree(
255 StorageBaseImpl
*This
,
256 DirRef parentStorageIndex
,
257 DirRef newEntryIndex
);
259 static LONG
entryNameCmp(
260 const OLECHAR
*name1
,
261 const OLECHAR
*name2
);
263 static DirRef
findElement(
264 StorageBaseImpl
*storage
,
269 static HRESULT
findTreeParent(
270 StorageBaseImpl
*storage
,
272 const OLECHAR
*childName
,
273 DirEntry
*parentData
,
277 /***********************************************************************
278 * Declaration of miscellaneous functions...
280 static HRESULT
validateSTGM(DWORD stgmValue
);
282 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
283 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
284 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
286 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
289 /****************************************************************************
290 * IEnumSTATSTGImpl definitions.
292 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
293 * This class allows iterating through the content of a storage and to find
294 * specific items inside it.
296 struct IEnumSTATSTGImpl
298 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
299 * since we want to cast this in an IEnumSTATSTG pointer */
301 LONG ref
; /* Reference count */
302 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
303 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
305 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
309 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
310 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
312 /************************************************************************
316 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
318 return (index
+1) * This
->bigBlockSize
;
321 /************************************************************************
322 ** Storage32BaseImpl implementation
324 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
325 ULARGE_INTEGER offset
,
330 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
333 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
334 ULARGE_INTEGER offset
,
339 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
342 /************************************************************************
343 * Storage32BaseImpl_QueryInterface (IUnknown)
345 * This method implements the common QueryInterface for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
355 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
357 if ( (This
==0) || (ppvObject
==0) )
362 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
363 IsEqualGUID(&IID_IStorage
, riid
))
367 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
369 *ppvObject
= &This
->pssVtbl
;
373 return E_NOINTERFACE
;
375 IStorage_AddRef(iface
);
380 /************************************************************************
381 * Storage32BaseImpl_AddRef (IUnknown)
383 * This method implements the common AddRef for all IStorage32
384 * implementations contained in this file.
386 * See Windows documentation for more details on IUnknown methods.
388 static ULONG WINAPI
StorageBaseImpl_AddRef(
391 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
392 ULONG ref
= InterlockedIncrement(&This
->ref
);
394 TRACE("(%p) AddRef to %d\n", This
, ref
);
399 /************************************************************************
400 * Storage32BaseImpl_Release (IUnknown)
402 * This method implements the common Release for all IStorage32
403 * implementations contained in this file.
405 * See Windows documentation for more details on IUnknown methods.
407 static ULONG WINAPI
StorageBaseImpl_Release(
410 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
412 ULONG ref
= InterlockedDecrement(&This
->ref
);
414 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
419 * Since we are using a system of base-classes, we want to call the
420 * destructor of the appropriate derived class. To do this, we are
421 * using virtual functions to implement the destructor.
423 StorageBaseImpl_Destroy(This
);
429 /************************************************************************
430 * Storage32BaseImpl_OpenStream (IStorage)
432 * This method will open the specified stream object from the current storage.
434 * See Windows documentation for more details on IStorage methods.
436 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
438 const OLECHAR
* pwcsName
, /* [string][in] */
439 void* reserved1
, /* [unique][in] */
440 DWORD grfMode
, /* [in] */
441 DWORD reserved2
, /* [in] */
442 IStream
** ppstm
) /* [out] */
444 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
445 StgStreamImpl
* newStream
;
446 DirEntry currentEntry
;
447 DirRef streamEntryRef
;
448 HRESULT res
= STG_E_UNKNOWN
;
450 TRACE("(%p, %s, %p, %x, %d, %p)\n",
451 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
453 if ( (pwcsName
==NULL
) || (ppstm
==0) )
461 if ( FAILED( validateSTGM(grfMode
) ) ||
462 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
464 res
= STG_E_INVALIDFLAG
;
471 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
473 res
= STG_E_INVALIDFUNCTION
;
479 res
= STG_E_REVERTED
;
484 * Check that we're compatible with the parent's storage mode, but
485 * only if we are not in transacted mode
487 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
488 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
490 res
= STG_E_INVALIDFLAG
;
496 * Search for the element with the given name
498 streamEntryRef
= findElement(
500 This
->storageDirEntry
,
505 * If it was found, construct the stream object and return a pointer to it.
507 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
508 (currentEntry
.stgType
==STGTY_STREAM
) )
510 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
512 /* A single stream cannot be opened a second time. */
513 res
= STG_E_ACCESSDENIED
;
517 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
521 newStream
->grfMode
= grfMode
;
522 *ppstm
= (IStream
*)newStream
;
524 IStream_AddRef(*ppstm
);
534 res
= STG_E_FILENOTFOUND
;
538 TRACE("<-- IStream %p\n", *ppstm
);
539 TRACE("<-- %08x\n", res
);
543 /************************************************************************
544 * Storage32BaseImpl_OpenStorage (IStorage)
546 * This method will open a new storage object from the current storage.
548 * See Windows documentation for more details on IStorage methods.
550 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
552 const OLECHAR
* pwcsName
, /* [string][unique][in] */
553 IStorage
* pstgPriority
, /* [unique][in] */
554 DWORD grfMode
, /* [in] */
555 SNB snbExclude
, /* [unique][in] */
556 DWORD reserved
, /* [in] */
557 IStorage
** ppstg
) /* [out] */
559 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
560 StorageInternalImpl
* newStorage
;
561 StorageBaseImpl
* newTransactedStorage
;
562 DirEntry currentEntry
;
563 DirRef storageEntryRef
;
564 HRESULT res
= STG_E_UNKNOWN
;
566 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
567 iface
, debugstr_w(pwcsName
), pstgPriority
,
568 grfMode
, snbExclude
, reserved
, ppstg
);
570 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
576 if (This
->openFlags
& STGM_SIMPLE
)
578 res
= STG_E_INVALIDFUNCTION
;
583 if (snbExclude
!= NULL
)
585 res
= STG_E_INVALIDPARAMETER
;
589 if ( FAILED( validateSTGM(grfMode
) ))
591 res
= STG_E_INVALIDFLAG
;
598 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
599 (grfMode
& STGM_DELETEONRELEASE
) ||
600 (grfMode
& STGM_PRIORITY
) )
602 res
= STG_E_INVALIDFUNCTION
;
607 return STG_E_REVERTED
;
610 * Check that we're compatible with the parent's storage mode,
611 * but only if we are not transacted
613 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
614 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
616 res
= STG_E_ACCESSDENIED
;
623 storageEntryRef
= findElement(
625 This
->storageDirEntry
,
629 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
630 (currentEntry
.stgType
==STGTY_STORAGE
) )
632 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
634 /* A single storage cannot be opened a second time. */
635 res
= STG_E_ACCESSDENIED
;
639 newStorage
= StorageInternalImpl_Construct(
646 if (grfMode
& STGM_TRANSACTED
)
648 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
652 HeapFree(GetProcessHeap(), 0, newStorage
);
656 *ppstg
= (IStorage
*)newTransactedStorage
;
660 *ppstg
= (IStorage
*)newStorage
;
663 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
669 res
= STG_E_INSUFFICIENTMEMORY
;
673 res
= STG_E_FILENOTFOUND
;
676 TRACE("<-- %08x\n", res
);
680 /************************************************************************
681 * Storage32BaseImpl_EnumElements (IStorage)
683 * This method will create an enumerator object that can be used to
684 * retrieve information about all the elements in the storage object.
686 * See Windows documentation for more details on IStorage methods.
688 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
690 DWORD reserved1
, /* [in] */
691 void* reserved2
, /* [size_is][unique][in] */
692 DWORD reserved3
, /* [in] */
693 IEnumSTATSTG
** ppenum
) /* [out] */
695 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
696 IEnumSTATSTGImpl
* newEnum
;
698 TRACE("(%p, %d, %p, %d, %p)\n",
699 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
701 if ( (This
==0) || (ppenum
==0))
705 return STG_E_REVERTED
;
707 newEnum
= IEnumSTATSTGImpl_Construct(
709 This
->storageDirEntry
);
713 *ppenum
= (IEnumSTATSTG
*)newEnum
;
715 IEnumSTATSTG_AddRef(*ppenum
);
720 return E_OUTOFMEMORY
;
723 /************************************************************************
724 * Storage32BaseImpl_Stat (IStorage)
726 * This method will retrieve information about this storage object.
728 * See Windows documentation for more details on IStorage methods.
730 static HRESULT WINAPI
StorageBaseImpl_Stat(
732 STATSTG
* pstatstg
, /* [out] */
733 DWORD grfStatFlag
) /* [in] */
735 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
736 DirEntry currentEntry
;
737 HRESULT res
= STG_E_UNKNOWN
;
739 TRACE("(%p, %p, %x)\n",
740 iface
, pstatstg
, grfStatFlag
);
742 if ( (This
==0) || (pstatstg
==0))
750 res
= STG_E_REVERTED
;
754 res
= StorageBaseImpl_ReadDirEntry(
756 This
->storageDirEntry
,
761 StorageUtl_CopyDirEntryToSTATSTG(
767 pstatstg
->grfMode
= This
->openFlags
;
768 pstatstg
->grfStateBits
= This
->stateBits
;
774 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
);
776 TRACE("<-- %08x\n", res
);
780 /************************************************************************
781 * Storage32BaseImpl_RenameElement (IStorage)
783 * This method will rename the specified element.
785 * See Windows documentation for more details on IStorage methods.
787 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
789 const OLECHAR
* pwcsOldName
, /* [in] */
790 const OLECHAR
* pwcsNewName
) /* [in] */
792 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
793 DirEntry currentEntry
;
794 DirRef currentEntryRef
;
796 TRACE("(%p, %s, %s)\n",
797 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
800 return STG_E_REVERTED
;
802 currentEntryRef
= findElement(This
,
803 This
->storageDirEntry
,
807 if (currentEntryRef
!= DIRENTRY_NULL
)
810 * There is already an element with the new name
812 return STG_E_FILEALREADYEXISTS
;
816 * Search for the old element name
818 currentEntryRef
= findElement(This
,
819 This
->storageDirEntry
,
823 if (currentEntryRef
!= DIRENTRY_NULL
)
825 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
826 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
828 WARN("Element is already open; cannot rename.\n");
829 return STG_E_ACCESSDENIED
;
832 /* Remove the element from its current position in the tree */
833 removeFromTree(This
, This
->storageDirEntry
,
836 /* Change the name of the element */
837 strcpyW(currentEntry
.name
, pwcsNewName
);
839 /* Delete any sibling links */
840 currentEntry
.leftChild
= DIRENTRY_NULL
;
841 currentEntry
.rightChild
= DIRENTRY_NULL
;
843 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
846 /* Insert the element in a new position in the tree */
847 insertIntoTree(This
, This
->storageDirEntry
,
853 * There is no element with the old name
855 return STG_E_FILENOTFOUND
;
861 /************************************************************************
862 * Storage32BaseImpl_CreateStream (IStorage)
864 * This method will create a stream object within this storage
866 * See Windows documentation for more details on IStorage methods.
868 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
870 const OLECHAR
* pwcsName
, /* [string][in] */
871 DWORD grfMode
, /* [in] */
872 DWORD reserved1
, /* [in] */
873 DWORD reserved2
, /* [in] */
874 IStream
** ppstm
) /* [out] */
876 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
877 StgStreamImpl
* newStream
;
878 DirEntry currentEntry
, newStreamEntry
;
879 DirRef currentEntryRef
, newStreamEntryRef
;
882 TRACE("(%p, %s, %x, %d, %d, %p)\n",
883 iface
, debugstr_w(pwcsName
), grfMode
,
884 reserved1
, reserved2
, ppstm
);
887 return STG_E_INVALIDPOINTER
;
890 return STG_E_INVALIDNAME
;
892 if (reserved1
|| reserved2
)
893 return STG_E_INVALIDPARAMETER
;
895 if ( FAILED( validateSTGM(grfMode
) ))
896 return STG_E_INVALIDFLAG
;
898 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
899 return STG_E_INVALIDFLAG
;
902 return STG_E_REVERTED
;
907 if ((grfMode
& STGM_DELETEONRELEASE
) ||
908 (grfMode
& STGM_TRANSACTED
))
909 return STG_E_INVALIDFUNCTION
;
912 * Don't worry about permissions in transacted mode, as we can always write
913 * changes; we just can't always commit them.
915 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
916 /* Can't create a stream on read-only storage */
917 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
918 return STG_E_ACCESSDENIED
;
920 /* Can't create a stream with greater access than the parent. */
921 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
922 return STG_E_ACCESSDENIED
;
925 if(This
->openFlags
& STGM_SIMPLE
)
926 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
930 currentEntryRef
= findElement(This
,
931 This
->storageDirEntry
,
935 if (currentEntryRef
!= DIRENTRY_NULL
)
938 * An element with this name already exists
940 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
942 IStorage_DestroyElement(iface
, pwcsName
);
945 return STG_E_FILEALREADYEXISTS
;
949 * memset the empty entry
951 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
953 newStreamEntry
.sizeOfNameString
=
954 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
956 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
957 return STG_E_INVALIDNAME
;
959 strcpyW(newStreamEntry
.name
, pwcsName
);
961 newStreamEntry
.stgType
= STGTY_STREAM
;
962 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
963 newStreamEntry
.size
.u
.LowPart
= 0;
964 newStreamEntry
.size
.u
.HighPart
= 0;
966 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
967 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
968 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
970 /* call CoFileTime to get the current time
975 /* newStreamEntry.clsid */
978 * Create an entry with the new data
980 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
985 * Insert the new entry in the parent storage's tree.
989 This
->storageDirEntry
,
993 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
998 * Open the stream to return it.
1000 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1004 *ppstm
= (IStream
*)newStream
;
1006 IStream_AddRef(*ppstm
);
1010 return STG_E_INSUFFICIENTMEMORY
;
1016 /************************************************************************
1017 * Storage32BaseImpl_SetClass (IStorage)
1019 * This method will write the specified CLSID in the directory entry of this
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1026 REFCLSID clsid
) /* [in] */
1028 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1030 DirEntry currentEntry
;
1032 TRACE("(%p, %p)\n", iface
, clsid
);
1035 return STG_E_REVERTED
;
1037 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1038 This
->storageDirEntry
,
1040 if (SUCCEEDED(hRes
))
1042 currentEntry
.clsid
= *clsid
;
1044 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1045 This
->storageDirEntry
,
1052 /************************************************************************
1053 ** Storage32Impl implementation
1056 /************************************************************************
1057 * Storage32BaseImpl_CreateStorage (IStorage)
1059 * This method will create the storage object within the provided storage.
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1065 const OLECHAR
*pwcsName
, /* [string][in] */
1066 DWORD grfMode
, /* [in] */
1067 DWORD reserved1
, /* [in] */
1068 DWORD reserved2
, /* [in] */
1069 IStorage
**ppstg
) /* [out] */
1071 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1073 DirEntry currentEntry
;
1075 DirRef currentEntryRef
;
1079 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1080 iface
, debugstr_w(pwcsName
), grfMode
,
1081 reserved1
, reserved2
, ppstg
);
1084 return STG_E_INVALIDPOINTER
;
1086 if (This
->openFlags
& STGM_SIMPLE
)
1088 return STG_E_INVALIDFUNCTION
;
1092 return STG_E_INVALIDNAME
;
1096 if ( FAILED( validateSTGM(grfMode
) ) ||
1097 (grfMode
& STGM_DELETEONRELEASE
) )
1099 WARN("bad grfMode: 0x%x\n", grfMode
);
1100 return STG_E_INVALIDFLAG
;
1104 return STG_E_REVERTED
;
1107 * Check that we're compatible with the parent's storage mode
1109 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1110 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1112 WARN("access denied\n");
1113 return STG_E_ACCESSDENIED
;
1116 currentEntryRef
= findElement(This
,
1117 This
->storageDirEntry
,
1121 if (currentEntryRef
!= DIRENTRY_NULL
)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1127 ((This
->openFlags
& STGM_TRANSACTED
) ||
1128 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1130 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1136 WARN("file already exists\n");
1137 return STG_E_FILEALREADYEXISTS
;
1140 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1141 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1143 WARN("read-only storage\n");
1144 return STG_E_ACCESSDENIED
;
1147 memset(&newEntry
, 0, sizeof(DirEntry
));
1149 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1151 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1153 FIXME("name too long\n");
1154 return STG_E_INVALIDNAME
;
1157 strcpyW(newEntry
.name
, pwcsName
);
1159 newEntry
.stgType
= STGTY_STORAGE
;
1160 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1161 newEntry
.size
.u
.LowPart
= 0;
1162 newEntry
.size
.u
.HighPart
= 0;
1164 newEntry
.leftChild
= DIRENTRY_NULL
;
1165 newEntry
.rightChild
= DIRENTRY_NULL
;
1166 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1168 /* call CoFileTime to get the current time
1173 /* newEntry.clsid */
1176 * Create a new directory entry for the storage
1178 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1183 * Insert the new directory entry into the parent storage's tree
1185 hr
= insertIntoTree(
1187 This
->storageDirEntry
,
1191 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1196 * Open it to get a pointer to return.
1198 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1200 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1210 /***************************************************************************
1214 * Reserve a directory entry in the file and initialize it.
1216 static HRESULT
StorageImpl_CreateDirEntry(
1217 StorageBaseImpl
*base
,
1218 const DirEntry
*newData
,
1221 StorageImpl
*storage
= (StorageImpl
*)base
;
1222 ULONG currentEntryIndex
= 0;
1223 ULONG newEntryIndex
= DIRENTRY_NULL
;
1225 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1226 WORD sizeOfNameString
;
1230 hr
= StorageImpl_ReadRawDirEntry(storage
,
1236 StorageUtl_ReadWord(
1238 OFFSET_PS_NAMELENGTH
,
1241 if (sizeOfNameString
== 0)
1244 * The entry exists and is available, we found it.
1246 newEntryIndex
= currentEntryIndex
;
1252 * We exhausted the directory entries, we will create more space below
1254 newEntryIndex
= currentEntryIndex
;
1256 currentEntryIndex
++;
1258 } while (newEntryIndex
== DIRENTRY_NULL
);
1261 * grow the directory stream
1265 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1266 ULARGE_INTEGER newSize
;
1268 ULONG lastEntry
= 0;
1269 ULONG blockCount
= 0;
1272 * obtain the new count of blocks in the directory stream
1274 blockCount
= BlockChainStream_GetCount(
1275 storage
->rootBlockChain
)+1;
1278 * initialize the size used by the directory stream
1280 newSize
.u
.HighPart
= 0;
1281 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1284 * add a block to the directory stream
1286 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1289 * memset the empty entry in order to initialize the unused newly
1292 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1297 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1300 entryIndex
= newEntryIndex
+ 1;
1301 entryIndex
< lastEntry
;
1304 StorageImpl_WriteRawDirEntry(
1311 UpdateRawDirEntry(currentData
, newData
);
1313 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1316 *index
= newEntryIndex
;
1321 /***************************************************************************
1325 * Mark a directory entry in the file as free.
1327 static HRESULT
StorageImpl_DestroyDirEntry(
1328 StorageBaseImpl
*base
,
1332 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1333 StorageImpl
*storage
= (StorageImpl
*)base
;
1335 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1337 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1343 /****************************************************************************
1347 * Case insensitive comparison of DirEntry.name by first considering
1350 * Returns <0 when name1 < name2
1351 * >0 when name1 > name2
1352 * 0 when name1 == name2
1354 static LONG
entryNameCmp(
1355 const OLECHAR
*name1
,
1356 const OLECHAR
*name2
)
1358 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1360 while (diff
== 0 && *name1
!= 0)
1363 * We compare the string themselves only when they are of the same length
1365 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1371 /****************************************************************************
1375 * Add a directory entry to a storage
1377 static HRESULT
insertIntoTree(
1378 StorageBaseImpl
*This
,
1379 DirRef parentStorageIndex
,
1380 DirRef newEntryIndex
)
1382 DirEntry currentEntry
;
1386 * Read the inserted entry
1388 StorageBaseImpl_ReadDirEntry(This
,
1393 * Read the storage entry
1395 StorageBaseImpl_ReadDirEntry(This
,
1399 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1402 * The root storage contains some element, therefore, start the research
1403 * for the appropriate location.
1406 DirRef current
, next
, previous
, currentEntryId
;
1409 * Keep a reference to the root of the storage's element tree
1411 currentEntryId
= currentEntry
.dirRootEntry
;
1416 StorageBaseImpl_ReadDirEntry(This
,
1417 currentEntry
.dirRootEntry
,
1420 previous
= currentEntry
.leftChild
;
1421 next
= currentEntry
.rightChild
;
1422 current
= currentEntryId
;
1426 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1430 if (previous
!= DIRENTRY_NULL
)
1432 StorageBaseImpl_ReadDirEntry(This
,
1439 currentEntry
.leftChild
= newEntryIndex
;
1440 StorageBaseImpl_WriteDirEntry(This
,
1448 if (next
!= DIRENTRY_NULL
)
1450 StorageBaseImpl_ReadDirEntry(This
,
1457 currentEntry
.rightChild
= newEntryIndex
;
1458 StorageBaseImpl_WriteDirEntry(This
,
1467 * Trying to insert an item with the same name in the
1468 * subtree structure.
1470 return STG_E_FILEALREADYEXISTS
;
1473 previous
= currentEntry
.leftChild
;
1474 next
= currentEntry
.rightChild
;
1480 * The storage is empty, make the new entry the root of its element tree
1482 currentEntry
.dirRootEntry
= newEntryIndex
;
1483 StorageBaseImpl_WriteDirEntry(This
,
1491 /****************************************************************************
1495 * Find and read the element of a storage with the given name.
1497 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1498 const OLECHAR
*name
, DirEntry
*data
)
1500 DirRef currentEntry
;
1502 /* Read the storage entry to find the root of the tree. */
1503 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1505 currentEntry
= data
->dirRootEntry
;
1507 while (currentEntry
!= DIRENTRY_NULL
)
1511 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1513 cmp
= entryNameCmp(name
, data
->name
);
1520 currentEntry
= data
->leftChild
;
1523 currentEntry
= data
->rightChild
;
1526 return currentEntry
;
1529 /****************************************************************************
1533 * Find and read the binary tree parent of the element with the given name.
1535 * If there is no such element, find a place where it could be inserted and
1536 * return STG_E_FILENOTFOUND.
1538 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1539 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1548 *parentEntry
= storageEntry
;
1549 *relation
= DIRENTRY_RELATION_DIR
;
1551 childEntry
= parentData
->dirRootEntry
;
1553 while (childEntry
!= DIRENTRY_NULL
)
1557 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1559 cmp
= entryNameCmp(childName
, childData
.name
);
1567 *parentData
= childData
;
1568 *parentEntry
= childEntry
;
1569 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1571 childEntry
= parentData
->leftChild
;
1576 *parentData
= childData
;
1577 *parentEntry
= childEntry
;
1578 *relation
= DIRENTRY_RELATION_NEXT
;
1580 childEntry
= parentData
->rightChild
;
1584 if (childEntry
== DIRENTRY_NULL
)
1585 return STG_E_FILENOTFOUND
;
1591 /*************************************************************************
1594 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1596 DWORD ciidExclude
, /* [in] */
1597 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1598 SNB snbExclude
, /* [unique][in] */
1599 IStorage
* pstgDest
) /* [unique][in] */
1601 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1603 IEnumSTATSTG
*elements
= 0;
1604 STATSTG curElement
, strStat
;
1606 IStorage
*pstgTmp
, *pstgChild
;
1607 IStream
*pstrTmp
, *pstrChild
;
1610 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1613 TRACE("(%p, %d, %p, %p, %p)\n",
1614 iface
, ciidExclude
, rgiidExclude
,
1615 snbExclude
, pstgDest
);
1617 if ( pstgDest
== 0 )
1618 return STG_E_INVALIDPOINTER
;
1621 * Enumerate the elements
1623 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1631 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1632 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1634 for(i
= 0; i
< ciidExclude
; ++i
)
1636 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1637 skip_storage
= TRUE
;
1638 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1641 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1647 * Obtain the next element
1649 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1651 if ( hr
== S_FALSE
)
1653 hr
= S_OK
; /* done, every element has been copied */
1659 WCHAR
**snb
= snbExclude
;
1661 while ( *snb
!= NULL
&& !skip
)
1663 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1672 if (curElement
.type
== STGTY_STORAGE
)
1678 * open child source storage
1680 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1681 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1682 NULL
, 0, &pstgChild
);
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
);
1707 * do the copy recursively
1709 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1712 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. This operation must succeed even if the
1735 * stream is already open, so we use internal functions to do it.
1737 srcEntryRef
= findElement( This
, This
->storageDirEntry
, curElement
.pwcsName
,
1741 ERR("source stream not found\n");
1742 hr
= STG_E_DOCFILECORRUPT
;
1747 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntryRef
);
1749 IStream_AddRef(pstrChild
);
1757 * Get the size of the source stream
1759 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1762 * Set the size of the destination stream.
1764 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1769 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1772 IStream_Release( pstrChild
);
1775 IStream_Release( pstrTmp
);
1779 WARN("unknown element type: %d\n", curElement
.type
);
1783 CoTaskMemFree(curElement
.pwcsName
);
1784 } while (hr
== S_OK
);
1789 IEnumSTATSTG_Release(elements
);
1794 /*************************************************************************
1795 * MoveElementTo (IStorage)
1797 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1799 const OLECHAR
*pwcsName
, /* [string][in] */
1800 IStorage
*pstgDest
, /* [unique][in] */
1801 const OLECHAR
*pwcsNewName
,/* [string][in] */
1802 DWORD grfFlags
) /* [in] */
1804 FIXME("(%p %s %p %s %u): stub\n", iface
,
1805 debugstr_w(pwcsName
), pstgDest
,
1806 debugstr_w(pwcsNewName
), grfFlags
);
1810 /*************************************************************************
1813 * Ensures that any changes made to a storage object open in transacted mode
1814 * are reflected in the parent storage
1817 * Wine doesn't implement transacted mode, which seems to be a basic
1818 * optimization, so we can ignore this stub for now.
1820 static HRESULT WINAPI
StorageImpl_Commit(
1822 DWORD grfCommitFlags
)/* [in] */
1824 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1828 /*************************************************************************
1831 * Discard all changes that have been made since the last commit operation
1833 static HRESULT WINAPI
StorageImpl_Revert(
1836 TRACE("(%p)\n", iface
);
1840 /*************************************************************************
1841 * DestroyElement (IStorage)
1843 * Strategy: This implementation is built this way for simplicity not for speed.
1844 * I always delete the topmost element of the enumeration and adjust
1845 * the deleted element pointer all the time. This takes longer to
1846 * do but allow to reinvoke DestroyElement whenever we encounter a
1847 * storage object. The optimisation resides in the usage of another
1848 * enumeration strategy that would give all the leaves of a storage
1849 * first. (postfix order)
1851 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1853 const OLECHAR
*pwcsName
)/* [string][in] */
1855 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1858 DirEntry entryToDelete
;
1859 DirRef entryToDeleteRef
;
1862 iface
, debugstr_w(pwcsName
));
1865 return STG_E_INVALIDPOINTER
;
1868 return STG_E_REVERTED
;
1870 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1871 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1872 return STG_E_ACCESSDENIED
;
1874 entryToDeleteRef
= findElement(
1876 This
->storageDirEntry
,
1880 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1882 return STG_E_FILENOTFOUND
;
1885 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1887 hr
= deleteStorageContents(
1892 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1894 hr
= deleteStreamContents(
1904 * Remove the entry from its parent storage
1906 hr
= removeFromTree(
1908 This
->storageDirEntry
,
1912 * Invalidate the entry
1915 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1921 /******************************************************************************
1922 * Internal stream list handlers
1925 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1927 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1928 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1931 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1933 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1934 list_remove(&(strm
->StrmListEntry
));
1937 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1939 StgStreamImpl
*strm
;
1941 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1943 if (strm
->dirEntry
== streamEntry
)
1952 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1954 StorageInternalImpl
*childstg
;
1956 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1958 if (childstg
->base
.storageDirEntry
== storageEntry
)
1967 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1969 struct list
*cur
, *cur2
;
1970 StgStreamImpl
*strm
=NULL
;
1971 StorageInternalImpl
*childstg
=NULL
;
1973 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1974 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1975 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1976 strm
->parentStorage
= NULL
;
1980 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1981 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1982 StorageBaseImpl_Invalidate( &childstg
->base
);
1985 if (stg
->transactedChild
)
1987 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1989 stg
->transactedChild
= NULL
;
1994 /*********************************************************************
1998 * Delete the contents of a storage entry.
2001 static HRESULT
deleteStorageContents(
2002 StorageBaseImpl
*parentStorage
,
2003 DirRef indexToDelete
,
2004 DirEntry entryDataToDelete
)
2006 IEnumSTATSTG
*elements
= 0;
2007 IStorage
*childStorage
= 0;
2008 STATSTG currentElement
;
2010 HRESULT destroyHr
= S_OK
;
2011 StorageInternalImpl
*stg
, *stg2
;
2013 /* Invalidate any open storage objects. */
2014 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2016 if (stg
->base
.storageDirEntry
== indexToDelete
)
2018 StorageBaseImpl_Invalidate(&stg
->base
);
2023 * Open the storage and enumerate it
2025 hr
= StorageBaseImpl_OpenStorage(
2026 (IStorage
*)parentStorage
,
2027 entryDataToDelete
.name
,
2029 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2040 * Enumerate the elements
2042 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2047 * Obtain the next element
2049 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2052 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2054 CoTaskMemFree(currentElement
.pwcsName
);
2058 * We need to Reset the enumeration every time because we delete elements
2059 * and the enumeration could be invalid
2061 IEnumSTATSTG_Reset(elements
);
2063 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2065 IStorage_Release(childStorage
);
2066 IEnumSTATSTG_Release(elements
);
2071 /*********************************************************************
2075 * Perform the deletion of a stream's data
2078 static HRESULT
deleteStreamContents(
2079 StorageBaseImpl
*parentStorage
,
2080 DirRef indexToDelete
,
2081 DirEntry entryDataToDelete
)
2085 ULARGE_INTEGER size
;
2086 StgStreamImpl
*strm
, *strm2
;
2088 /* Invalidate any open stream objects. */
2089 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2091 if (strm
->dirEntry
== indexToDelete
)
2093 TRACE("Stream deleted %p\n", strm
);
2094 strm
->parentStorage
= NULL
;
2095 list_remove(&strm
->StrmListEntry
);
2099 size
.u
.HighPart
= 0;
2102 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2103 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2113 hr
= IStream_SetSize(pis
, size
);
2121 * Release the stream object.
2123 IStream_Release(pis
);
2128 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2132 case DIRENTRY_RELATION_PREVIOUS
:
2133 entry
->leftChild
= new_target
;
2135 case DIRENTRY_RELATION_NEXT
:
2136 entry
->rightChild
= new_target
;
2138 case DIRENTRY_RELATION_DIR
:
2139 entry
->dirRootEntry
= new_target
;
2146 /*************************************************************************
2150 * This method removes a directory entry from its parent storage tree without
2151 * freeing any resources attached to it.
2153 static HRESULT
removeFromTree(
2154 StorageBaseImpl
*This
,
2155 DirRef parentStorageIndex
,
2156 DirRef deletedIndex
)
2159 DirEntry entryToDelete
;
2160 DirEntry parentEntry
;
2161 DirRef parentEntryRef
;
2162 ULONG typeOfRelation
;
2164 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2170 * Find the element that links to the one we want to delete.
2172 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2173 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2178 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2181 * Replace the deleted entry with its left child
2183 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2185 hr
= StorageBaseImpl_WriteDirEntry(
2194 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2197 * We need to reinsert the right child somewhere. We already know it and
2198 * its children are greater than everything in the left tree, so we
2199 * insert it at the rightmost point in the left tree.
2201 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2202 DirEntry newRightChildParentEntry
;
2206 hr
= StorageBaseImpl_ReadDirEntry(
2208 newRightChildParent
,
2209 &newRightChildParentEntry
);
2215 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2216 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2217 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2219 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2221 hr
= StorageBaseImpl_WriteDirEntry(
2223 newRightChildParent
,
2224 &newRightChildParentEntry
);
2234 * Replace the deleted entry with its right child
2236 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2238 hr
= StorageBaseImpl_WriteDirEntry(
2252 /******************************************************************************
2253 * SetElementTimes (IStorage)
2255 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2257 const OLECHAR
*pwcsName
,/* [string][in] */
2258 const FILETIME
*pctime
, /* [in] */
2259 const FILETIME
*patime
, /* [in] */
2260 const FILETIME
*pmtime
) /* [in] */
2262 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2266 /******************************************************************************
2267 * SetStateBits (IStorage)
2269 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2271 DWORD grfStateBits
,/* [in] */
2272 DWORD grfMask
) /* [in] */
2274 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2277 return STG_E_REVERTED
;
2279 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2283 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2284 DirRef index
, const DirEntry
*data
)
2286 StorageImpl
*This
= (StorageImpl
*)base
;
2287 return StorageImpl_WriteDirEntry(This
, index
, data
);
2290 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2291 DirRef index
, DirEntry
*data
)
2293 StorageImpl
*This
= (StorageImpl
*)base
;
2294 return StorageImpl_ReadDirEntry(This
, index
, data
);
2297 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2301 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2303 if (!This
->blockChainCache
[i
])
2305 return &This
->blockChainCache
[i
];
2309 i
= This
->blockChainToEvict
;
2311 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2312 This
->blockChainCache
[i
] = NULL
;
2314 This
->blockChainToEvict
++;
2315 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2316 This
->blockChainToEvict
= 0;
2318 return &This
->blockChainCache
[i
];
2321 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2324 int i
, free_index
=-1;
2326 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2328 if (!This
->blockChainCache
[i
])
2330 if (free_index
== -1) free_index
= i
;
2332 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2334 return &This
->blockChainCache
[i
];
2338 if (free_index
== -1)
2340 free_index
= This
->blockChainToEvict
;
2342 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2343 This
->blockChainCache
[free_index
] = NULL
;
2345 This
->blockChainToEvict
++;
2346 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2347 This
->blockChainToEvict
= 0;
2350 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2351 return &This
->blockChainCache
[free_index
];
2354 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2355 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2357 StorageImpl
*This
= (StorageImpl
*)base
;
2362 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2363 if (FAILED(hr
)) return hr
;
2365 if (data
.size
.QuadPart
== 0)
2371 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2373 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2380 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2382 SmallBlockChainStream
*stream
;
2384 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2385 if (!stream
) return E_OUTOFMEMORY
;
2387 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2389 SmallBlockChainStream_Destroy(stream
);
2395 BlockChainStream
*stream
= NULL
;
2397 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2398 if (!stream
) return E_OUTOFMEMORY
;
2400 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2406 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2407 ULARGE_INTEGER newsize
)
2409 StorageImpl
*This
= (StorageImpl
*)base
;
2412 SmallBlockChainStream
*smallblock
=NULL
;
2413 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2415 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2416 if (FAILED(hr
)) return hr
;
2418 /* In simple mode keep the stream size above the small block limit */
2419 if (This
->base
.openFlags
& STGM_SIMPLE
)
2420 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2422 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2425 /* Create a block chain object of the appropriate type */
2426 if (data
.size
.QuadPart
== 0)
2428 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2430 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2431 if (!smallblock
) return E_OUTOFMEMORY
;
2435 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2436 bigblock
= *pbigblock
;
2437 if (!bigblock
) return E_OUTOFMEMORY
;
2440 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2442 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2443 if (!smallblock
) return E_OUTOFMEMORY
;
2447 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2448 bigblock
= *pbigblock
;
2449 if (!bigblock
) return E_OUTOFMEMORY
;
2452 /* Change the block chain type if necessary. */
2453 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2455 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2458 SmallBlockChainStream_Destroy(smallblock
);
2462 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2463 *pbigblock
= bigblock
;
2465 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2467 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2472 /* Set the size of the block chain. */
2475 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2476 SmallBlockChainStream_Destroy(smallblock
);
2480 BlockChainStream_SetSize(bigblock
, newsize
);
2483 /* Set the size in the directory entry. */
2484 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2487 data
.size
= newsize
;
2489 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2494 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2495 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2497 StorageImpl
*This
= (StorageImpl
*)base
;
2500 ULARGE_INTEGER newSize
;
2502 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2503 if (FAILED(hr
)) return hr
;
2505 /* Grow the stream if necessary */
2506 newSize
.QuadPart
= 0;
2507 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2509 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2511 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2515 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2516 if (FAILED(hr
)) return hr
;
2519 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2521 SmallBlockChainStream
*stream
;
2523 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2524 if (!stream
) return E_OUTOFMEMORY
;
2526 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2528 SmallBlockChainStream_Destroy(stream
);
2534 BlockChainStream
*stream
;
2536 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2537 if (!stream
) return E_OUTOFMEMORY
;
2539 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2546 * Virtual function table for the IStorage32Impl class.
2548 static const IStorageVtbl Storage32Impl_Vtbl
=
2550 StorageBaseImpl_QueryInterface
,
2551 StorageBaseImpl_AddRef
,
2552 StorageBaseImpl_Release
,
2553 StorageBaseImpl_CreateStream
,
2554 StorageBaseImpl_OpenStream
,
2555 StorageBaseImpl_CreateStorage
,
2556 StorageBaseImpl_OpenStorage
,
2557 StorageBaseImpl_CopyTo
,
2558 StorageBaseImpl_MoveElementTo
,
2561 StorageBaseImpl_EnumElements
,
2562 StorageBaseImpl_DestroyElement
,
2563 StorageBaseImpl_RenameElement
,
2564 StorageBaseImpl_SetElementTimes
,
2565 StorageBaseImpl_SetClass
,
2566 StorageBaseImpl_SetStateBits
,
2567 StorageBaseImpl_Stat
2570 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2572 StorageImpl_Destroy
,
2573 StorageImpl_Invalidate
,
2574 StorageImpl_CreateDirEntry
,
2575 StorageImpl_BaseWriteDirEntry
,
2576 StorageImpl_BaseReadDirEntry
,
2577 StorageImpl_DestroyDirEntry
,
2578 StorageImpl_StreamReadAt
,
2579 StorageImpl_StreamWriteAt
,
2580 StorageImpl_StreamSetSize
2583 static HRESULT
StorageImpl_Construct(
2591 StorageImpl
** result
)
2595 DirEntry currentEntry
;
2596 DirRef currentEntryRef
;
2597 WCHAR fullpath
[MAX_PATH
];
2599 if ( FAILED( validateSTGM(openFlags
) ))
2600 return STG_E_INVALIDFLAG
;
2602 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2604 return E_OUTOFMEMORY
;
2606 memset(This
, 0, sizeof(StorageImpl
));
2608 list_init(&This
->base
.strmHead
);
2610 list_init(&This
->base
.storageHead
);
2612 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2613 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2614 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2615 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2617 This
->base
.create
= create
;
2619 This
->base
.reverted
= 0;
2621 This
->hFile
= hFile
;
2624 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2626 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2628 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2629 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2630 if (!This
->pwcsName
)
2632 hr
= STG_E_INSUFFICIENTMEMORY
;
2635 strcpyW(This
->pwcsName
, fullpath
);
2636 This
->base
.filename
= This
->pwcsName
;
2640 * Initialize the big block cache.
2642 This
->bigBlockSize
= sector_size
;
2643 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2644 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2649 if (This
->bigBlockFile
== 0)
2657 ULARGE_INTEGER size
;
2658 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2661 * Initialize all header variables:
2662 * - The big block depot consists of one block and it is at block 0
2663 * - The directory table starts at block 1
2664 * - There is no small block depot
2666 memset( This
->bigBlockDepotStart
,
2668 sizeof(This
->bigBlockDepotStart
));
2670 This
->bigBlockDepotCount
= 1;
2671 This
->bigBlockDepotStart
[0] = 0;
2672 This
->rootStartBlock
= 1;
2673 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2674 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2675 if (sector_size
== 4096)
2676 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2678 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2679 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2680 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2681 This
->extBigBlockDepotCount
= 0;
2683 StorageImpl_SaveFileHeader(This
);
2686 * Add one block for the big block depot and one block for the directory table
2688 size
.u
.HighPart
= 0;
2689 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2690 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2693 * Initialize the big block depot
2695 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2696 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2697 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2698 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2703 * Load the header for the file.
2705 hr
= StorageImpl_LoadFileHeader(This
);
2714 * There is no block depot cached yet.
2716 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2719 * Start searching for free blocks with block 0.
2721 This
->prevFreeBlock
= 0;
2723 This
->firstFreeSmallBlock
= 0;
2726 * Create the block chain abstractions.
2728 if(!(This
->rootBlockChain
=
2729 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2731 hr
= STG_E_READFAULT
;
2735 if(!(This
->smallBlockDepotChain
=
2736 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2739 hr
= STG_E_READFAULT
;
2744 * Write the root storage entry (memory only)
2750 * Initialize the directory table
2752 memset(&rootEntry
, 0, sizeof(rootEntry
));
2753 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2754 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2755 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2756 rootEntry
.stgType
= STGTY_ROOT
;
2757 rootEntry
.leftChild
= DIRENTRY_NULL
;
2758 rootEntry
.rightChild
= DIRENTRY_NULL
;
2759 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2760 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2761 rootEntry
.size
.u
.HighPart
= 0;
2762 rootEntry
.size
.u
.LowPart
= 0;
2764 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2768 * Find the ID of the root storage.
2770 currentEntryRef
= 0;
2774 hr
= StorageImpl_ReadDirEntry(
2781 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2782 (currentEntry
.stgType
== STGTY_ROOT
) )
2784 This
->base
.storageDirEntry
= currentEntryRef
;
2790 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2794 hr
= STG_E_READFAULT
;
2799 * Create the block chain abstraction for the small block root chain.
2801 if(!(This
->smallBlockRootChain
=
2802 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2804 hr
= STG_E_READFAULT
;
2810 IStorage_Release((IStorage
*)This
);
2819 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2821 StorageImpl
*This
= (StorageImpl
*) iface
;
2823 StorageBaseImpl_DeleteAll(&This
->base
);
2825 This
->base
.reverted
= 1;
2828 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2830 StorageImpl
*This
= (StorageImpl
*) iface
;
2832 TRACE("(%p)\n", This
);
2834 StorageImpl_Invalidate(iface
);
2836 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2838 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2839 BlockChainStream_Destroy(This
->rootBlockChain
);
2840 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2842 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2843 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2845 if (This
->bigBlockFile
)
2846 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2847 HeapFree(GetProcessHeap(), 0, This
);
2850 /******************************************************************************
2851 * Storage32Impl_GetNextFreeBigBlock
2853 * Returns the index of the next free big block.
2854 * If the big block depot is filled, this method will enlarge it.
2857 static ULONG
StorageImpl_GetNextFreeBigBlock(
2860 ULONG depotBlockIndexPos
;
2861 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2863 ULONG depotBlockOffset
;
2864 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2865 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2867 ULONG freeBlock
= BLOCK_UNUSED
;
2868 ULARGE_INTEGER neededSize
;
2870 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2871 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2874 * Scan the entire big block depot until we find a block marked free
2876 while (nextBlockIndex
!= BLOCK_UNUSED
)
2878 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2880 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2883 * Grow the primary depot.
2885 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2887 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2890 * Add a block depot.
2892 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2893 This
->bigBlockDepotCount
++;
2894 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2897 * Flag it as a block depot.
2899 StorageImpl_SetNextBlockInChain(This
,
2903 /* Save new header information.
2905 StorageImpl_SaveFileHeader(This
);
2910 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2912 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2915 * Grow the extended depot.
2917 ULONG extIndex
= BLOCK_UNUSED
;
2918 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2919 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2921 if (extBlockOffset
== 0)
2923 /* We need an extended block.
2925 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2926 This
->extBigBlockDepotCount
++;
2927 depotBlockIndexPos
= extIndex
+ 1;
2930 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2933 * Add a block depot and mark it in the extended block.
2935 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2936 This
->bigBlockDepotCount
++;
2937 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2939 /* Flag the block depot.
2941 StorageImpl_SetNextBlockInChain(This
,
2945 /* If necessary, flag the extended depot block.
2947 if (extIndex
!= BLOCK_UNUSED
)
2948 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2950 /* Save header information.
2952 StorageImpl_SaveFileHeader(This
);
2956 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2960 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2961 ( nextBlockIndex
!= BLOCK_UNUSED
))
2963 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2965 if (nextBlockIndex
== BLOCK_UNUSED
)
2967 freeBlock
= (depotIndex
* blocksPerDepot
) +
2968 (depotBlockOffset
/sizeof(ULONG
));
2971 depotBlockOffset
+= sizeof(ULONG
);
2976 depotBlockOffset
= 0;
2980 * make sure that the block physically exists before using it
2982 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
2983 BIGBLOCKFILE_Expand(This
->bigBlockFile
, neededSize
);
2985 This
->prevFreeBlock
= freeBlock
;
2990 /******************************************************************************
2991 * Storage32Impl_AddBlockDepot
2993 * This will create a depot block, essentially it is a block initialized
2996 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2998 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3001 * Initialize blocks as free
3003 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3004 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3007 /******************************************************************************
3008 * Storage32Impl_GetExtDepotBlock
3010 * Returns the index of the block that corresponds to the specified depot
3011 * index. This method is only for depot indexes equal or greater than
3012 * COUNT_BBDEPOTINHEADER.
3014 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3016 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3017 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3018 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3019 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3020 ULONG blockIndex
= BLOCK_UNUSED
;
3021 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3023 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3025 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3026 return BLOCK_UNUSED
;
3028 while (extBlockCount
> 0)
3030 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3034 if (extBlockIndex
!= BLOCK_UNUSED
)
3035 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3036 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3041 /******************************************************************************
3042 * Storage32Impl_SetExtDepotBlock
3044 * Associates the specified block index to the specified depot index.
3045 * This method is only for depot indexes equal or greater than
3046 * COUNT_BBDEPOTINHEADER.
3048 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3050 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3051 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3052 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3053 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3054 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3056 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3058 while (extBlockCount
> 0)
3060 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3064 if (extBlockIndex
!= BLOCK_UNUSED
)
3066 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3067 extBlockOffset
* sizeof(ULONG
),
3072 /******************************************************************************
3073 * Storage32Impl_AddExtBlockDepot
3075 * Creates an extended depot block.
3077 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3079 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3080 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3081 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3082 ULONG index
= BLOCK_UNUSED
;
3083 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3084 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3085 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3087 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3088 blocksPerDepotBlock
;
3090 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3093 * The first extended block.
3095 This
->extBigBlockDepotStart
= index
;
3101 * Follow the chain to the last one.
3103 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3105 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3109 * Add the new extended block to the chain.
3111 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3116 * Initialize this block.
3118 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3119 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3124 /******************************************************************************
3125 * Storage32Impl_FreeBigBlock
3127 * This method will flag the specified block as free in the big block depot.
3129 static void StorageImpl_FreeBigBlock(
3133 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3135 if (blockIndex
< This
->prevFreeBlock
)
3136 This
->prevFreeBlock
= blockIndex
;
3139 /************************************************************************
3140 * Storage32Impl_GetNextBlockInChain
3142 * This method will retrieve the block index of the next big block in
3145 * Params: This - Pointer to the Storage object.
3146 * blockIndex - Index of the block to retrieve the chain
3148 * nextBlockIndex - receives the return value.
3150 * Returns: This method returns the index of the next block in the chain.
3151 * It will return the constants:
3152 * BLOCK_SPECIAL - If the block given was not part of a
3154 * BLOCK_END_OF_CHAIN - If the block given was the last in
3156 * BLOCK_UNUSED - If the block given was not past of a chain
3158 * BLOCK_EXTBBDEPOT - This block is part of the extended
3161 * See Windows documentation for more details on IStorage methods.
3163 static HRESULT
StorageImpl_GetNextBlockInChain(
3166 ULONG
* nextBlockIndex
)
3168 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3169 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3170 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3171 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3173 ULONG depotBlockIndexPos
;
3174 int index
, num_blocks
;
3176 *nextBlockIndex
= BLOCK_SPECIAL
;
3178 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3180 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3181 This
->bigBlockDepotCount
);
3182 return STG_E_READFAULT
;
3186 * Cache the currently accessed depot block.
3188 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3190 This
->indexBlockDepotCached
= depotBlockCount
;
3192 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3194 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3199 * We have to look in the extended depot.
3201 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3204 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3207 return STG_E_READFAULT
;
3209 num_blocks
= This
->bigBlockSize
/ 4;
3211 for (index
= 0; index
< num_blocks
; index
++)
3213 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3214 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3218 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3223 /******************************************************************************
3224 * Storage32Impl_GetNextExtendedBlock
3226 * Given an extended block this method will return the next extended block.
3229 * The last ULONG of an extended block is the block index of the next
3230 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3234 * - The index of the next extended block
3235 * - BLOCK_UNUSED: there is no next extended block.
3236 * - Any other return values denotes failure.
3238 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3240 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3241 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3243 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3246 return nextBlockIndex
;
3249 /******************************************************************************
3250 * Storage32Impl_SetNextBlockInChain
3252 * This method will write the index of the specified block's next block
3253 * in the big block depot.
3255 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3258 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3259 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3260 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3263 static void StorageImpl_SetNextBlockInChain(
3268 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3269 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3270 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3271 ULONG depotBlockIndexPos
;
3273 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3274 assert(blockIndex
!= nextBlock
);
3276 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3278 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3283 * We have to look in the extended depot.
3285 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3288 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3291 * Update the cached block depot, if necessary.
3293 if (depotBlockCount
== This
->indexBlockDepotCached
)
3295 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3299 /******************************************************************************
3300 * Storage32Impl_LoadFileHeader
3302 * This method will read in the file header
3304 static HRESULT
StorageImpl_LoadFileHeader(
3308 BYTE headerBigBlock
[HEADER_SIZE
];
3310 ULARGE_INTEGER offset
;
3315 * Get a pointer to the big block of data containing the header.
3317 offset
.u
.HighPart
= 0;
3318 offset
.u
.LowPart
= 0;
3319 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3320 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3321 hr
= STG_E_FILENOTFOUND
;
3324 * Extract the information from the header.
3329 * Check for the "magic number" signature and return an error if it is not
3332 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3334 return STG_E_OLDFORMAT
;
3337 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3339 return STG_E_INVALIDHEADER
;
3342 StorageUtl_ReadWord(
3344 OFFSET_BIGBLOCKSIZEBITS
,
3345 &This
->bigBlockSizeBits
);
3347 StorageUtl_ReadWord(
3349 OFFSET_SMALLBLOCKSIZEBITS
,
3350 &This
->smallBlockSizeBits
);
3352 StorageUtl_ReadDWord(
3354 OFFSET_BBDEPOTCOUNT
,
3355 &This
->bigBlockDepotCount
);
3357 StorageUtl_ReadDWord(
3359 OFFSET_ROOTSTARTBLOCK
,
3360 &This
->rootStartBlock
);
3362 StorageUtl_ReadDWord(
3364 OFFSET_SMALLBLOCKLIMIT
,
3365 &This
->smallBlockLimit
);
3367 StorageUtl_ReadDWord(
3369 OFFSET_SBDEPOTSTART
,
3370 &This
->smallBlockDepotStart
);
3372 StorageUtl_ReadDWord(
3374 OFFSET_EXTBBDEPOTSTART
,
3375 &This
->extBigBlockDepotStart
);
3377 StorageUtl_ReadDWord(
3379 OFFSET_EXTBBDEPOTCOUNT
,
3380 &This
->extBigBlockDepotCount
);
3382 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3384 StorageUtl_ReadDWord(
3386 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3387 &(This
->bigBlockDepotStart
[index
]));
3391 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3393 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3394 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3397 * Right now, the code is making some assumptions about the size of the
3398 * blocks, just make sure they are what we're expecting.
3400 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3401 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3402 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3404 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3405 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3406 hr
= STG_E_INVALIDHEADER
;
3415 /******************************************************************************
3416 * Storage32Impl_SaveFileHeader
3418 * This method will save to the file the header
3420 static void StorageImpl_SaveFileHeader(
3423 BYTE headerBigBlock
[HEADER_SIZE
];
3426 ULARGE_INTEGER offset
;
3427 DWORD bytes_read
, bytes_written
;
3430 * Get a pointer to the big block of data containing the header.
3432 offset
.u
.HighPart
= 0;
3433 offset
.u
.LowPart
= 0;
3434 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3435 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3436 hr
= STG_E_FILENOTFOUND
;
3439 * If the block read failed, the file is probably new.
3444 * Initialize for all unknown fields.
3446 memset(headerBigBlock
, 0, HEADER_SIZE
);
3449 * Initialize the magic number.
3451 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3454 * And a bunch of things we don't know what they mean
3456 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3457 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3458 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3462 * Write the information to the header.
3464 StorageUtl_WriteWord(
3466 OFFSET_BIGBLOCKSIZEBITS
,
3467 This
->bigBlockSizeBits
);
3469 StorageUtl_WriteWord(
3471 OFFSET_SMALLBLOCKSIZEBITS
,
3472 This
->smallBlockSizeBits
);
3474 StorageUtl_WriteDWord(
3476 OFFSET_BBDEPOTCOUNT
,
3477 This
->bigBlockDepotCount
);
3479 StorageUtl_WriteDWord(
3481 OFFSET_ROOTSTARTBLOCK
,
3482 This
->rootStartBlock
);
3484 StorageUtl_WriteDWord(
3486 OFFSET_SMALLBLOCKLIMIT
,
3487 This
->smallBlockLimit
);
3489 StorageUtl_WriteDWord(
3491 OFFSET_SBDEPOTSTART
,
3492 This
->smallBlockDepotStart
);
3494 StorageUtl_WriteDWord(
3496 OFFSET_SBDEPOTCOUNT
,
3497 This
->smallBlockDepotChain
?
3498 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3500 StorageUtl_WriteDWord(
3502 OFFSET_EXTBBDEPOTSTART
,
3503 This
->extBigBlockDepotStart
);
3505 StorageUtl_WriteDWord(
3507 OFFSET_EXTBBDEPOTCOUNT
,
3508 This
->extBigBlockDepotCount
);
3510 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3512 StorageUtl_WriteDWord(
3514 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3515 (This
->bigBlockDepotStart
[index
]));
3519 * Write the big block back to the file.
3521 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3524 /******************************************************************************
3525 * StorageImpl_ReadRawDirEntry
3527 * This method will read the raw data from a directory entry in the file.
3529 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3531 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3533 ULARGE_INTEGER offset
;
3537 offset
.u
.HighPart
= 0;
3538 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3540 hr
= BlockChainStream_ReadAt(
3541 This
->rootBlockChain
,
3550 /******************************************************************************
3551 * StorageImpl_WriteRawDirEntry
3553 * This method will write the raw data from a directory entry in the file.
3555 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3557 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3559 ULARGE_INTEGER offset
;
3563 offset
.u
.HighPart
= 0;
3564 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3566 hr
= BlockChainStream_WriteAt(
3567 This
->rootBlockChain
,
3576 /******************************************************************************
3579 * Update raw directory entry data from the fields in newData.
3581 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3583 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3585 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3588 buffer
+ OFFSET_PS_NAME
,
3590 DIRENTRY_NAME_BUFFER_LEN
);
3592 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3594 StorageUtl_WriteWord(
3596 OFFSET_PS_NAMELENGTH
,
3597 newData
->sizeOfNameString
);
3599 StorageUtl_WriteDWord(
3601 OFFSET_PS_LEFTCHILD
,
3602 newData
->leftChild
);
3604 StorageUtl_WriteDWord(
3606 OFFSET_PS_RIGHTCHILD
,
3607 newData
->rightChild
);
3609 StorageUtl_WriteDWord(
3612 newData
->dirRootEntry
);
3614 StorageUtl_WriteGUID(
3619 StorageUtl_WriteDWord(
3622 newData
->ctime
.dwLowDateTime
);
3624 StorageUtl_WriteDWord(
3626 OFFSET_PS_CTIMEHIGH
,
3627 newData
->ctime
.dwHighDateTime
);
3629 StorageUtl_WriteDWord(
3632 newData
->mtime
.dwLowDateTime
);
3634 StorageUtl_WriteDWord(
3636 OFFSET_PS_MTIMEHIGH
,
3637 newData
->ctime
.dwHighDateTime
);
3639 StorageUtl_WriteDWord(
3641 OFFSET_PS_STARTBLOCK
,
3642 newData
->startingBlock
);
3644 StorageUtl_WriteDWord(
3647 newData
->size
.u
.LowPart
);
3650 /******************************************************************************
3651 * Storage32Impl_ReadDirEntry
3653 * This method will read the specified directory entry.
3655 HRESULT
StorageImpl_ReadDirEntry(
3660 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3663 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3665 if (SUCCEEDED(readRes
))
3667 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3670 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3671 DIRENTRY_NAME_BUFFER_LEN
);
3672 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3674 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3676 StorageUtl_ReadWord(
3678 OFFSET_PS_NAMELENGTH
,
3679 &buffer
->sizeOfNameString
);
3681 StorageUtl_ReadDWord(
3683 OFFSET_PS_LEFTCHILD
,
3684 &buffer
->leftChild
);
3686 StorageUtl_ReadDWord(
3688 OFFSET_PS_RIGHTCHILD
,
3689 &buffer
->rightChild
);
3691 StorageUtl_ReadDWord(
3694 &buffer
->dirRootEntry
);
3696 StorageUtl_ReadGUID(
3701 StorageUtl_ReadDWord(
3704 &buffer
->ctime
.dwLowDateTime
);
3706 StorageUtl_ReadDWord(
3708 OFFSET_PS_CTIMEHIGH
,
3709 &buffer
->ctime
.dwHighDateTime
);
3711 StorageUtl_ReadDWord(
3714 &buffer
->mtime
.dwLowDateTime
);
3716 StorageUtl_ReadDWord(
3718 OFFSET_PS_MTIMEHIGH
,
3719 &buffer
->mtime
.dwHighDateTime
);
3721 StorageUtl_ReadDWord(
3723 OFFSET_PS_STARTBLOCK
,
3724 &buffer
->startingBlock
);
3726 StorageUtl_ReadDWord(
3729 &buffer
->size
.u
.LowPart
);
3731 buffer
->size
.u
.HighPart
= 0;
3737 /*********************************************************************
3738 * Write the specified directory entry to the file
3740 HRESULT
StorageImpl_WriteDirEntry(
3743 const DirEntry
* buffer
)
3745 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3748 UpdateRawDirEntry(currentEntry
, buffer
);
3750 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3754 static BOOL
StorageImpl_ReadBigBlock(
3759 ULARGE_INTEGER ulOffset
;
3762 ulOffset
.u
.HighPart
= 0;
3763 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3765 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3766 return (read
== This
->bigBlockSize
);
3769 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3775 ULARGE_INTEGER ulOffset
;
3779 ulOffset
.u
.HighPart
= 0;
3780 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3781 ulOffset
.u
.LowPart
+= offset
;
3783 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3784 *value
= lendian32toh(tmp
);
3785 return (read
== sizeof(DWORD
));
3788 static BOOL
StorageImpl_WriteBigBlock(
3793 ULARGE_INTEGER ulOffset
;
3796 ulOffset
.u
.HighPart
= 0;
3797 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3799 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3800 return (wrote
== This
->bigBlockSize
);
3803 static BOOL
StorageImpl_WriteDWordToBigBlock(
3809 ULARGE_INTEGER ulOffset
;
3812 ulOffset
.u
.HighPart
= 0;
3813 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3814 ulOffset
.u
.LowPart
+= offset
;
3816 value
= htole32(value
);
3817 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3818 return (wrote
== sizeof(DWORD
));
3821 /******************************************************************************
3822 * Storage32Impl_SmallBlocksToBigBlocks
3824 * This method will convert a small block chain to a big block chain.
3825 * The small block chain will be destroyed.
3827 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3829 SmallBlockChainStream
** ppsbChain
)
3831 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3832 ULARGE_INTEGER size
, offset
;
3833 ULONG cbRead
, cbWritten
;
3834 ULARGE_INTEGER cbTotalRead
;
3835 DirRef streamEntryRef
;
3836 HRESULT resWrite
= S_OK
;
3838 DirEntry streamEntry
;
3840 BlockChainStream
*bbTempChain
= NULL
;
3841 BlockChainStream
*bigBlockChain
= NULL
;
3844 * Create a temporary big block chain that doesn't have
3845 * an associated directory entry. This temporary chain will be
3846 * used to copy data from small blocks to big blocks.
3848 bbTempChain
= BlockChainStream_Construct(This
,
3851 if(!bbTempChain
) return NULL
;
3853 * Grow the big block chain.
3855 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3856 BlockChainStream_SetSize(bbTempChain
, size
);
3859 * Copy the contents of the small block chain to the big block chain
3860 * by small block size increments.
3862 offset
.u
.LowPart
= 0;
3863 offset
.u
.HighPart
= 0;
3864 cbTotalRead
.QuadPart
= 0;
3866 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3869 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3871 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3874 if (FAILED(resRead
))
3879 cbTotalRead
.QuadPart
+= cbRead
;
3881 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3887 if (FAILED(resWrite
))
3890 offset
.u
.LowPart
+= cbRead
;
3892 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3893 HeapFree(GetProcessHeap(),0,buffer
);
3895 size
.u
.HighPart
= 0;
3898 if (FAILED(resRead
) || FAILED(resWrite
))
3900 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3901 BlockChainStream_SetSize(bbTempChain
, size
);
3902 BlockChainStream_Destroy(bbTempChain
);
3907 * Destroy the small block chain.
3909 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3910 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3911 SmallBlockChainStream_Destroy(*ppsbChain
);
3915 * Change the directory entry. This chain is now a big block chain
3916 * and it doesn't reside in the small blocks chain anymore.
3918 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3920 streamEntry
.startingBlock
= bbHeadOfChain
;
3922 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3925 * Destroy the temporary entryless big block chain.
3926 * Create a new big block chain associated with this entry.
3928 BlockChainStream_Destroy(bbTempChain
);
3929 bigBlockChain
= BlockChainStream_Construct(This
,
3933 return bigBlockChain
;
3936 /******************************************************************************
3937 * Storage32Impl_BigBlocksToSmallBlocks
3939 * This method will convert a big block chain to a small block chain.
3940 * The big block chain will be destroyed on success.
3942 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3944 BlockChainStream
** ppbbChain
)
3946 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3947 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3948 DirRef streamEntryRef
;
3949 HRESULT resWrite
= S_OK
, resRead
;
3950 DirEntry streamEntry
;
3952 SmallBlockChainStream
* sbTempChain
;
3954 TRACE("%p %p\n", This
, ppbbChain
);
3956 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3962 size
= BlockChainStream_GetSize(*ppbbChain
);
3963 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3965 offset
.u
.HighPart
= 0;
3966 offset
.u
.LowPart
= 0;
3967 cbTotalRead
.QuadPart
= 0;
3968 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3971 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3972 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3980 cbTotalRead
.QuadPart
+= cbRead
;
3982 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3983 cbRead
, buffer
, &cbWritten
);
3985 if(FAILED(resWrite
))
3988 offset
.u
.LowPart
+= cbRead
;
3990 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3991 HeapFree(GetProcessHeap(), 0, buffer
);
3993 size
.u
.HighPart
= 0;
3996 if(FAILED(resRead
) || FAILED(resWrite
))
3998 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3999 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4000 SmallBlockChainStream_Destroy(sbTempChain
);
4004 /* destroy the original big block chain */
4005 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4006 BlockChainStream_SetSize(*ppbbChain
, size
);
4007 BlockChainStream_Destroy(*ppbbChain
);
4010 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4011 streamEntry
.startingBlock
= sbHeadOfChain
;
4012 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4014 SmallBlockChainStream_Destroy(sbTempChain
);
4015 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4018 static HRESULT
StorageBaseImpl_CopyStream(
4019 StorageBaseImpl
*dst
, DirRef dst_entry
,
4020 StorageBaseImpl
*src
, DirRef src_entry
)
4025 ULARGE_INTEGER bytes_copied
;
4026 ULONG bytestocopy
, bytesread
, byteswritten
;
4028 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4032 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4034 bytes_copied
.QuadPart
= 0;
4035 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4037 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4039 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4041 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4044 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4045 data
, &byteswritten
);
4046 if (SUCCEEDED(hr
) && byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4048 bytes_copied
.QuadPart
+= byteswritten
;
4055 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4057 DirRef result
=This
->firstFreeEntry
;
4059 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4062 if (result
== This
->entries_size
)
4064 ULONG new_size
= This
->entries_size
* 2;
4065 TransactedDirEntry
*new_entries
;
4067 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4068 if (!new_entries
) return DIRENTRY_NULL
;
4070 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4071 HeapFree(GetProcessHeap(), 0, This
->entries
);
4073 This
->entries
= new_entries
;
4074 This
->entries_size
= new_size
;
4077 This
->entries
[result
].inuse
= 1;
4079 This
->firstFreeEntry
= result
+1;
4084 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4085 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4087 DirRef stubEntryRef
;
4088 TransactedDirEntry
*entry
;
4090 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4092 if (stubEntryRef
!= DIRENTRY_NULL
)
4094 entry
= &This
->entries
[stubEntryRef
];
4096 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4101 return stubEntryRef
;
4104 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4105 TransactedSnapshotImpl
*This
, DirRef entry
)
4109 if (!This
->entries
[entry
].read
)
4111 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4112 This
->entries
[entry
].transactedParentEntry
,
4113 &This
->entries
[entry
].data
);
4115 if (SUCCEEDED(hr
) && This
->entries
[entry
].data
.leftChild
!= DIRENTRY_NULL
)
4117 This
->entries
[entry
].data
.leftChild
=
4118 TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].data
.leftChild
);
4120 if (This
->entries
[entry
].data
.leftChild
== DIRENTRY_NULL
)
4124 if (SUCCEEDED(hr
) && This
->entries
[entry
].data
.rightChild
!= DIRENTRY_NULL
)
4126 This
->entries
[entry
].data
.rightChild
=
4127 TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].data
.rightChild
);
4129 if (This
->entries
[entry
].data
.rightChild
== DIRENTRY_NULL
)
4133 if (SUCCEEDED(hr
) && This
->entries
[entry
].data
.dirRootEntry
!= DIRENTRY_NULL
)
4135 This
->entries
[entry
].data
.dirRootEntry
=
4136 TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].data
.dirRootEntry
);
4138 if (This
->entries
[entry
].data
.dirRootEntry
== DIRENTRY_NULL
)
4143 This
->entries
[entry
].read
= 1;
4149 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4150 TransactedSnapshotImpl
*This
, DirRef entry
)
4154 if (!This
->entries
[entry
].stream_dirty
)
4156 DirEntry new_entrydata
;
4158 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4159 new_entrydata
.name
[0] = 'S';
4160 new_entrydata
.sizeOfNameString
= 1;
4161 new_entrydata
.stgType
= STGTY_STREAM
;
4162 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4163 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4164 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4165 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4167 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4168 &This
->entries
[entry
].stream_entry
);
4170 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4172 hr
= StorageBaseImpl_CopyStream(
4173 This
->scratch
, This
->entries
[entry
].stream_entry
,
4174 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4177 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4181 This
->entries
[entry
].stream_dirty
= 1;
4183 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4185 /* Since this entry is modified, and we aren't using its stream data, we
4186 * no longer care about the original entry. */
4188 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4190 if (delete_ref
!= DIRENTRY_NULL
)
4191 This
->entries
[delete_ref
].deleted
= 1;
4193 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4200 /* Find the first entry in a depth-first traversal. */
4201 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4202 TransactedSnapshotImpl
* This
, DirRef parent
)
4204 DirRef cursor
, prev
;
4205 TransactedDirEntry
*entry
;
4208 entry
= &This
->entries
[cursor
];
4211 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4214 cursor
= entry
->data
.leftChild
;
4215 entry
= &This
->entries
[cursor
];
4216 entry
->parent
= prev
;
4218 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4221 cursor
= entry
->data
.rightChild
;
4222 entry
= &This
->entries
[cursor
];
4223 entry
->parent
= prev
;
4225 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4228 cursor
= entry
->data
.dirRootEntry
;
4229 entry
= &This
->entries
[cursor
];
4230 entry
->parent
= prev
;
4239 /* Find the next entry in a depth-first traversal. */
4240 static DirRef
TransactedSnapshotImpl_FindNextChild(
4241 TransactedSnapshotImpl
* This
, DirRef current
)
4244 TransactedDirEntry
*parent_entry
;
4246 parent
= This
->entries
[current
].parent
;
4247 parent_entry
= &This
->entries
[parent
];
4249 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4251 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4253 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4254 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4257 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4259 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4260 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4267 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4268 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4269 TransactedSnapshotImpl
* This
, DirRef entry
)
4271 return entry
!= DIRENTRY_NULL
&&
4272 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4275 /* Destroy the entries created by CopyTree. */
4276 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4277 TransactedSnapshotImpl
* This
, DirRef stop
)
4280 TransactedDirEntry
*entry
;
4281 ULARGE_INTEGER zero
;
4285 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4288 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4290 if (cursor
== DIRENTRY_NULL
)
4293 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4295 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4297 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4299 entry
= &This
->entries
[cursor
];
4301 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4302 entry
->newTransactedParentEntry
, zero
);
4304 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4305 entry
->newTransactedParentEntry
);
4307 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4310 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4314 /* Make a copy of our edited tree that we can use in the parent. */
4315 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4318 TransactedDirEntry
*entry
;
4321 cursor
= This
->base
.storageDirEntry
;
4322 entry
= &This
->entries
[cursor
];
4323 entry
->parent
= DIRENTRY_NULL
;
4324 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4326 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4329 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4331 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4332 entry
= &This
->entries
[cursor
];
4334 while (cursor
!= DIRENTRY_NULL
)
4336 /* Make a copy of this entry in the transacted parent. */
4338 (!entry
->dirty
&& !entry
->stream_dirty
&&
4339 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4340 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4341 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4342 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4347 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4349 newData
.size
.QuadPart
= 0;
4350 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4352 if (newData
.leftChild
!= DIRENTRY_NULL
)
4353 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4355 if (newData
.rightChild
!= DIRENTRY_NULL
)
4356 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4358 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4359 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4361 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4362 &entry
->newTransactedParentEntry
);
4365 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4369 hr
= StorageBaseImpl_CopyStream(
4370 This
->transactedParent
, entry
->newTransactedParentEntry
,
4371 (StorageBaseImpl
*)This
, cursor
);
4374 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4375 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4380 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4381 entry
= &This
->entries
[cursor
];
4387 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4389 DWORD grfCommitFlags
) /* [in] */
4391 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4392 TransactedDirEntry
*root_entry
;
4393 DirRef i
, dir_root_ref
;
4395 ULARGE_INTEGER zero
;
4400 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4402 /* Cannot commit a read-only transacted storage */
4403 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4404 return STG_E_ACCESSDENIED
;
4406 /* To prevent data loss, we create the new structure in the file before we
4407 * delete the old one, so that in case of errors the old data is intact. We
4408 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4409 * needed in the rare situation where we have just enough free disk space to
4410 * overwrite the existing data. */
4412 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4414 if (!root_entry
->read
)
4417 hr
= TransactedSnapshotImpl_CopyTree(This
);
4418 if (FAILED(hr
)) return hr
;
4420 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4421 dir_root_ref
= DIRENTRY_NULL
;
4423 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4425 /* Update the storage to use the new data in one step. */
4426 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4427 root_entry
->transactedParentEntry
, &data
);
4431 data
.dirRootEntry
= dir_root_ref
;
4432 data
.clsid
= root_entry
->data
.clsid
;
4433 data
.ctime
= root_entry
->data
.ctime
;
4434 data
.mtime
= root_entry
->data
.mtime
;
4436 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4437 root_entry
->transactedParentEntry
, &data
);
4442 /* Destroy the old now-orphaned data. */
4443 for (i
=0; i
<This
->entries_size
; i
++)
4445 TransactedDirEntry
*entry
= &This
->entries
[i
];
4450 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4451 entry
->transactedParentEntry
, zero
);
4452 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4453 entry
->transactedParentEntry
);
4454 memset(entry
, 0, sizeof(TransactedDirEntry
));
4455 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4457 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4459 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4461 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4462 entry
->transactedParentEntry
, zero
);
4463 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4464 entry
->transactedParentEntry
);
4466 if (entry
->stream_dirty
)
4468 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4469 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4470 entry
->stream_dirty
= 0;
4473 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4480 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4486 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4489 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4490 ULARGE_INTEGER zero
;
4493 TRACE("(%p)\n", iface
);
4495 /* Destroy the open objects. */
4496 StorageBaseImpl_DeleteAll(&This
->base
);
4498 /* Clear out the scratch file. */
4500 for (i
=0; i
<This
->entries_size
; i
++)
4502 if (This
->entries
[i
].stream_dirty
)
4504 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4507 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4511 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4513 This
->firstFreeEntry
= 0;
4514 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4519 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4521 if (!This
->reverted
)
4523 TRACE("Storage invalidated (stg=%p)\n", This
);
4527 StorageBaseImpl_DeleteAll(This
);
4531 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4533 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4535 TransactedSnapshotImpl_Revert((IStorage
*)iface
);
4537 IStorage_Release((IStorage
*)This
->transactedParent
);
4539 IStorage_Release((IStorage
*)This
->scratch
);
4541 HeapFree(GetProcessHeap(), 0, This
->entries
);
4543 HeapFree(GetProcessHeap(), 0, This
);
4546 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4547 const DirEntry
*newData
, DirRef
*index
)
4549 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4551 TransactedDirEntry
*new_entry
;
4553 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4554 if (new_ref
== DIRENTRY_NULL
)
4555 return E_OUTOFMEMORY
;
4557 new_entry
= &This
->entries
[new_ref
];
4559 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4560 new_entry
->read
= 1;
4561 new_entry
->dirty
= 1;
4562 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4566 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4571 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4572 DirRef index
, const DirEntry
*data
)
4574 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4577 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4579 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4580 if (FAILED(hr
)) return hr
;
4582 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4584 if (index
!= This
->base
.storageDirEntry
)
4586 This
->entries
[index
].dirty
= 1;
4588 if (data
->size
.QuadPart
== 0 &&
4589 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4591 /* Since this entry is modified, and we aren't using its stream data, we
4592 * no longer care about the original entry. */
4594 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4596 if (delete_ref
!= DIRENTRY_NULL
)
4597 This
->entries
[delete_ref
].deleted
= 1;
4599 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4606 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4607 DirRef index
, DirEntry
*data
)
4609 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4612 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4613 if (FAILED(hr
)) return hr
;
4615 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4617 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4622 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4625 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4627 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4628 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4630 /* If we deleted this entry while it has stream data. We must have left the
4631 * data because some other entry is using it, and we need to leave the
4632 * original entry alone. */
4633 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4634 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4638 This
->entries
[index
].deleted
= 1;
4644 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4645 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4647 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4649 if (This
->entries
[index
].stream_dirty
)
4651 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4652 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4654 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4656 /* This stream doesn't live in the parent, and we haven't allocated storage
4663 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4664 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4668 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4669 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4671 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4674 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4675 if (FAILED(hr
)) return hr
;
4677 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4678 if (FAILED(hr
)) return hr
;
4680 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4681 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4683 if (SUCCEEDED(hr
) && size
!= 0)
4684 This
->entries
[index
].data
.size
.QuadPart
= max(
4685 This
->entries
[index
].data
.size
.QuadPart
,
4686 offset
.QuadPart
+ size
);
4691 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4692 DirRef index
, ULARGE_INTEGER newsize
)
4694 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4697 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4698 if (FAILED(hr
)) return hr
;
4700 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4703 if (newsize
.QuadPart
== 0)
4705 /* Destroy any parent references or entries in the scratch file. */
4706 if (This
->entries
[index
].stream_dirty
)
4708 ULARGE_INTEGER zero
;
4710 StorageBaseImpl_StreamSetSize(This
->scratch
,
4711 This
->entries
[index
].stream_entry
, zero
);
4712 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4713 This
->entries
[index
].stream_entry
);
4714 This
->entries
[index
].stream_dirty
= 0;
4716 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4719 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4721 if (delete_ref
!= DIRENTRY_NULL
)
4722 This
->entries
[delete_ref
].deleted
= 1;
4724 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4729 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4730 if (FAILED(hr
)) return hr
;
4732 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
4733 This
->entries
[index
].stream_entry
, newsize
);
4737 This
->entries
[index
].data
.size
= newsize
;
4742 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4744 StorageBaseImpl_QueryInterface
,
4745 StorageBaseImpl_AddRef
,
4746 StorageBaseImpl_Release
,
4747 StorageBaseImpl_CreateStream
,
4748 StorageBaseImpl_OpenStream
,
4749 StorageBaseImpl_CreateStorage
,
4750 StorageBaseImpl_OpenStorage
,
4751 StorageBaseImpl_CopyTo
,
4752 StorageBaseImpl_MoveElementTo
,
4753 TransactedSnapshotImpl_Commit
,
4754 TransactedSnapshotImpl_Revert
,
4755 StorageBaseImpl_EnumElements
,
4756 StorageBaseImpl_DestroyElement
,
4757 StorageBaseImpl_RenameElement
,
4758 StorageBaseImpl_SetElementTimes
,
4759 StorageBaseImpl_SetClass
,
4760 StorageBaseImpl_SetStateBits
,
4761 StorageBaseImpl_Stat
4764 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4766 TransactedSnapshotImpl_Destroy
,
4767 TransactedSnapshotImpl_Invalidate
,
4768 TransactedSnapshotImpl_CreateDirEntry
,
4769 TransactedSnapshotImpl_WriteDirEntry
,
4770 TransactedSnapshotImpl_ReadDirEntry
,
4771 TransactedSnapshotImpl_DestroyDirEntry
,
4772 TransactedSnapshotImpl_StreamReadAt
,
4773 TransactedSnapshotImpl_StreamWriteAt
,
4774 TransactedSnapshotImpl_StreamSetSize
4777 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4778 TransactedSnapshotImpl
** result
)
4782 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4785 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4787 /* This is OK because the property set storage functions use the IStorage functions. */
4788 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4790 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4792 list_init(&(*result
)->base
.strmHead
);
4794 list_init(&(*result
)->base
.storageHead
);
4796 (*result
)->base
.ref
= 1;
4798 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4800 (*result
)->base
.filename
= parentStorage
->filename
;
4802 /* Create a new temporary storage to act as the scratch file. */
4803 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
,
4804 0, (IStorage
**)&(*result
)->scratch
);
4808 ULONG num_entries
= 20;
4810 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
4812 (*result
)->entries_size
= num_entries
;
4814 (*result
)->firstFreeEntry
= 0;
4816 if ((*result
)->entries
)
4818 /* parentStorage already has 1 reference, which we take over here. */
4819 (*result
)->transactedParent
= parentStorage
;
4821 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4823 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
4827 IStorage_Release((IStorage
*)(*result
)->scratch
);
4833 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4838 return E_OUTOFMEMORY
;
4841 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4842 StorageBaseImpl
** result
)
4846 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4848 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4851 return TransactedSnapshotImpl_Construct(parentStorage
,
4852 (TransactedSnapshotImpl
**)result
);
4855 static HRESULT
Storage_Construct(
4863 StorageBaseImpl
** result
)
4865 StorageImpl
*newStorage
;
4866 StorageBaseImpl
*newTransactedStorage
;
4869 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
4870 if (FAILED(hr
)) goto end
;
4872 if (openFlags
& STGM_TRANSACTED
)
4874 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4876 IStorage_Release((IStorage
*)newStorage
);
4878 *result
= newTransactedStorage
;
4881 *result
= &newStorage
->base
;
4887 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4889 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4891 if (!This
->base
.reverted
)
4893 TRACE("Storage invalidated (stg=%p)\n", This
);
4895 This
->base
.reverted
= 1;
4897 This
->parentStorage
= NULL
;
4899 StorageBaseImpl_DeleteAll(&This
->base
);
4901 list_remove(&This
->ParentListEntry
);
4905 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4907 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4909 StorageInternalImpl_Invalidate(&This
->base
);
4911 HeapFree(GetProcessHeap(), 0, This
);
4914 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4915 const DirEntry
*newData
, DirRef
*index
)
4917 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4919 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4923 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4924 DirRef index
, const DirEntry
*data
)
4926 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4928 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4932 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4933 DirRef index
, DirEntry
*data
)
4935 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4937 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4941 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4944 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4946 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4950 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4951 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4953 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4955 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4956 index
, offset
, size
, buffer
, bytesRead
);
4959 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4960 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4962 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4964 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4965 index
, offset
, size
, buffer
, bytesWritten
);
4968 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4969 DirRef index
, ULARGE_INTEGER newsize
)
4971 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4973 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4977 /******************************************************************************
4979 ** Storage32InternalImpl_Commit
4982 static HRESULT WINAPI
StorageInternalImpl_Commit(
4984 DWORD grfCommitFlags
) /* [in] */
4986 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4990 /******************************************************************************
4992 ** Storage32InternalImpl_Revert
4995 static HRESULT WINAPI
StorageInternalImpl_Revert(
4998 FIXME("(%p): stub\n", iface
);
5002 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5004 IStorage_Release((IStorage
*)This
->parentStorage
);
5005 HeapFree(GetProcessHeap(), 0, This
);
5008 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5009 IEnumSTATSTG
* iface
,
5013 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5016 return E_INVALIDARG
;
5020 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5021 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5024 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
5028 return E_NOINTERFACE
;
5031 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5032 IEnumSTATSTG
* iface
)
5034 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5035 return InterlockedIncrement(&This
->ref
);
5038 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5039 IEnumSTATSTG
* iface
)
5041 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5045 newRef
= InterlockedDecrement(&This
->ref
);
5049 IEnumSTATSTGImpl_Destroy(This
);
5055 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5056 IEnumSTATSTGImpl
* This
,
5059 DirRef result
= DIRENTRY_NULL
;
5063 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5065 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5066 This
->parentStorage
->storageDirEntry
, &entry
);
5067 searchNode
= entry
.dirRootEntry
;
5069 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5071 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5075 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5079 searchNode
= entry
.rightChild
;
5083 result
= searchNode
;
5084 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5085 searchNode
= entry
.leftChild
;
5093 if (result
!= DIRENTRY_NULL
)
5094 memcpy(This
->name
, result_name
, sizeof(result_name
));
5100 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5101 IEnumSTATSTG
* iface
,
5104 ULONG
* pceltFetched
)
5106 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5108 DirEntry currentEntry
;
5109 STATSTG
* currentReturnStruct
= rgelt
;
5110 ULONG objectFetched
= 0;
5111 DirRef currentSearchNode
;
5114 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5115 return E_INVALIDARG
;
5117 if (This
->parentStorage
->reverted
)
5118 return STG_E_REVERTED
;
5121 * To avoid the special case, get another pointer to a ULONG value if
5122 * the caller didn't supply one.
5124 if (pceltFetched
==0)
5125 pceltFetched
= &objectFetched
;
5128 * Start the iteration, we will iterate until we hit the end of the
5129 * linked list or until we hit the number of items to iterate through
5133 while ( *pceltFetched
< celt
)
5135 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5137 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5141 * Read the entry from the storage.
5143 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5148 * Copy the information to the return buffer.
5150 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5151 currentReturnStruct
,
5156 * Step to the next item in the iteration
5159 currentReturnStruct
++;
5162 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5169 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5170 IEnumSTATSTG
* iface
,
5173 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5175 ULONG objectFetched
= 0;
5176 DirRef currentSearchNode
;
5179 if (This
->parentStorage
->reverted
)
5180 return STG_E_REVERTED
;
5182 while ( (objectFetched
< celt
) )
5184 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5186 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5192 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5198 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5199 IEnumSTATSTG
* iface
)
5201 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5203 if (This
->parentStorage
->reverted
)
5204 return STG_E_REVERTED
;
5211 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5212 IEnumSTATSTG
* iface
,
5213 IEnumSTATSTG
** ppenum
)
5215 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5217 IEnumSTATSTGImpl
* newClone
;
5219 if (This
->parentStorage
->reverted
)
5220 return STG_E_REVERTED
;
5223 * Perform a sanity check on the parameters.
5226 return E_INVALIDARG
;
5228 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5229 This
->storageDirEntry
);
5233 * The new clone enumeration must point to the same current node as
5236 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5238 *ppenum
= (IEnumSTATSTG
*)newClone
;
5241 * Don't forget to nail down a reference to the clone before
5244 IEnumSTATSTGImpl_AddRef(*ppenum
);
5250 * Virtual function table for the IEnumSTATSTGImpl class.
5252 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5254 IEnumSTATSTGImpl_QueryInterface
,
5255 IEnumSTATSTGImpl_AddRef
,
5256 IEnumSTATSTGImpl_Release
,
5257 IEnumSTATSTGImpl_Next
,
5258 IEnumSTATSTGImpl_Skip
,
5259 IEnumSTATSTGImpl_Reset
,
5260 IEnumSTATSTGImpl_Clone
5263 /******************************************************************************
5264 ** IEnumSTATSTGImpl implementation
5267 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5268 StorageBaseImpl
* parentStorage
,
5269 DirRef storageDirEntry
)
5271 IEnumSTATSTGImpl
* newEnumeration
;
5273 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5275 if (newEnumeration
!=0)
5278 * Set-up the virtual function table and reference count.
5280 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5281 newEnumeration
->ref
= 0;
5284 * We want to nail-down the reference to the storage in case the
5285 * enumeration out-lives the storage in the client application.
5287 newEnumeration
->parentStorage
= parentStorage
;
5288 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
5290 newEnumeration
->storageDirEntry
= storageDirEntry
;
5293 * Make sure the current node of the iterator is the first one.
5295 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
5298 return newEnumeration
;
5302 * Virtual function table for the Storage32InternalImpl class.
5304 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5306 StorageBaseImpl_QueryInterface
,
5307 StorageBaseImpl_AddRef
,
5308 StorageBaseImpl_Release
,
5309 StorageBaseImpl_CreateStream
,
5310 StorageBaseImpl_OpenStream
,
5311 StorageBaseImpl_CreateStorage
,
5312 StorageBaseImpl_OpenStorage
,
5313 StorageBaseImpl_CopyTo
,
5314 StorageBaseImpl_MoveElementTo
,
5315 StorageInternalImpl_Commit
,
5316 StorageInternalImpl_Revert
,
5317 StorageBaseImpl_EnumElements
,
5318 StorageBaseImpl_DestroyElement
,
5319 StorageBaseImpl_RenameElement
,
5320 StorageBaseImpl_SetElementTimes
,
5321 StorageBaseImpl_SetClass
,
5322 StorageBaseImpl_SetStateBits
,
5323 StorageBaseImpl_Stat
5326 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5328 StorageInternalImpl_Destroy
,
5329 StorageInternalImpl_Invalidate
,
5330 StorageInternalImpl_CreateDirEntry
,
5331 StorageInternalImpl_WriteDirEntry
,
5332 StorageInternalImpl_ReadDirEntry
,
5333 StorageInternalImpl_DestroyDirEntry
,
5334 StorageInternalImpl_StreamReadAt
,
5335 StorageInternalImpl_StreamWriteAt
,
5336 StorageInternalImpl_StreamSetSize
5339 /******************************************************************************
5340 ** Storage32InternalImpl implementation
5343 static StorageInternalImpl
* StorageInternalImpl_Construct(
5344 StorageBaseImpl
* parentStorage
,
5346 DirRef storageDirEntry
)
5348 StorageInternalImpl
* newStorage
;
5350 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5354 list_init(&newStorage
->base
.strmHead
);
5356 list_init(&newStorage
->base
.storageHead
);
5359 * Initialize the virtual function table.
5361 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5362 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5363 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5364 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5366 newStorage
->base
.reverted
= 0;
5368 newStorage
->base
.ref
= 1;
5370 newStorage
->parentStorage
= parentStorage
;
5373 * Keep a reference to the directory entry of this storage
5375 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5377 newStorage
->base
.create
= 0;
5385 /******************************************************************************
5386 ** StorageUtl implementation
5389 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5393 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5394 *value
= lendian16toh(tmp
);
5397 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5399 value
= htole16(value
);
5400 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5403 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5407 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5408 *value
= lendian32toh(tmp
);
5411 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5413 value
= htole32(value
);
5414 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5417 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5418 ULARGE_INTEGER
* value
)
5420 #ifdef WORDS_BIGENDIAN
5423 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5424 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5425 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5427 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5431 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5432 const ULARGE_INTEGER
*value
)
5434 #ifdef WORDS_BIGENDIAN
5437 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5438 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5439 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5441 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5445 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5447 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5448 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5449 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5451 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5454 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5456 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5457 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5458 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5460 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5463 void StorageUtl_CopyDirEntryToSTATSTG(
5464 StorageBaseImpl
* storage
,
5465 STATSTG
* destination
,
5466 const DirEntry
* source
,
5471 if (source
->stgType
== STGTY_ROOT
)
5473 /* replace the name of root entry (often "Root Entry") by the file name */
5474 entryName
= storage
->filename
;
5478 entryName
= source
->name
;
5482 * The copy of the string occurs only when the flag is not set
5484 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5485 (entryName
== NULL
) ||
5486 (entryName
[0] == 0) )
5488 destination
->pwcsName
= 0;
5492 destination
->pwcsName
=
5493 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
5495 strcpyW(destination
->pwcsName
, entryName
);
5498 switch (source
->stgType
)
5502 destination
->type
= STGTY_STORAGE
;
5505 destination
->type
= STGTY_STREAM
;
5508 destination
->type
= STGTY_STREAM
;
5512 destination
->cbSize
= source
->size
;
5514 currentReturnStruct->mtime = {0}; TODO
5515 currentReturnStruct->ctime = {0};
5516 currentReturnStruct->atime = {0};
5518 destination
->grfMode
= 0;
5519 destination
->grfLocksSupported
= 0;
5520 destination
->clsid
= source
->clsid
;
5521 destination
->grfStateBits
= 0;
5522 destination
->reserved
= 0;
5525 /******************************************************************************
5526 ** BlockChainStream implementation
5529 /* Read and save the index of all blocks in this stream. */
5530 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5532 ULONG next_sector
, next_offset
;
5534 struct BlockChainRun
*last_run
;
5536 if (This
->indexCacheLen
== 0)
5540 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5544 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5545 next_offset
= last_run
->lastOffset
+1;
5546 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5547 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5549 if (FAILED(hr
)) return hr
;
5552 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5554 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5556 /* Add the current block to the cache. */
5557 if (This
->indexCacheSize
== 0)
5559 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5560 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5561 This
->indexCacheSize
= 16;
5563 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5565 struct BlockChainRun
*new_cache
;
5568 new_size
= This
->indexCacheSize
* 2;
5569 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5570 if (!new_cache
) return E_OUTOFMEMORY
;
5571 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5573 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5574 This
->indexCache
= new_cache
;
5575 This
->indexCacheSize
= new_size
;
5578 This
->indexCacheLen
++;
5579 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5580 last_run
->firstSector
= next_sector
;
5581 last_run
->firstOffset
= next_offset
;
5584 last_run
->lastOffset
= next_offset
;
5586 /* Find the next block. */
5588 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5589 if (FAILED(hr
)) return hr
;
5592 if (This
->indexCacheLen
)
5594 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5595 This
->numBlocks
= last_run
->lastOffset
+1;
5599 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5600 This
->numBlocks
= 0;
5606 /* Locate the nth block in this stream. */
5607 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5609 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5610 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5612 if (offset
>= This
->numBlocks
)
5613 return BLOCK_END_OF_CHAIN
;
5615 while (min_run
< max_run
)
5617 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5618 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5620 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5621 max_run
= run_to_check
-1;
5623 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5625 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5626 min_run
= run_to_check
+1;
5629 /* Block is in this run. */
5630 min_run
= max_run
= run_to_check
;
5633 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5636 BlockChainStream
* BlockChainStream_Construct(
5637 StorageImpl
* parentStorage
,
5638 ULONG
* headOfStreamPlaceHolder
,
5641 BlockChainStream
* newStream
;
5643 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5645 newStream
->parentStorage
= parentStorage
;
5646 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5647 newStream
->ownerDirEntry
= dirEntry
;
5648 newStream
->indexCache
= NULL
;
5649 newStream
->indexCacheLen
= 0;
5650 newStream
->indexCacheSize
= 0;
5652 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
5654 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
5655 HeapFree(GetProcessHeap(), 0, newStream
);
5662 void BlockChainStream_Destroy(BlockChainStream
* This
)
5665 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5666 HeapFree(GetProcessHeap(), 0, This
);
5669 /******************************************************************************
5670 * BlockChainStream_GetHeadOfChain
5672 * Returns the head of this stream chain.
5673 * Some special chains don't have directory entries, their heads are kept in
5674 * This->headOfStreamPlaceHolder.
5677 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5679 DirEntry chainEntry
;
5682 if (This
->headOfStreamPlaceHolder
!= 0)
5683 return *(This
->headOfStreamPlaceHolder
);
5685 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5687 hr
= StorageImpl_ReadDirEntry(
5688 This
->parentStorage
,
5689 This
->ownerDirEntry
,
5694 return chainEntry
.startingBlock
;
5698 return BLOCK_END_OF_CHAIN
;
5701 /******************************************************************************
5702 * BlockChainStream_GetCount
5704 * Returns the number of blocks that comprises this chain.
5705 * This is not the size of the stream as the last block may not be full!
5707 * FIXME: Use the cache to get this information.
5709 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5714 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5716 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5720 if(FAILED(StorageImpl_GetNextBlockInChain(
5721 This
->parentStorage
,
5730 /******************************************************************************
5731 * BlockChainStream_ReadAt
5733 * Reads a specified number of bytes from this chain at the specified offset.
5734 * bytesRead may be NULL.
5735 * Failure will be returned if the specified number of bytes has not been read.
5737 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5738 ULARGE_INTEGER offset
,
5743 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5744 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5745 ULONG bytesToReadInBuffer
;
5749 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5752 * Find the first block in the stream that contains part of the buffer.
5754 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5756 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5757 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5760 * Start reading the buffer.
5763 bufferWalker
= buffer
;
5765 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5767 ULARGE_INTEGER ulOffset
;
5770 * Calculate how many bytes we can copy from this big block.
5772 bytesToReadInBuffer
=
5773 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5775 TRACE("block %i\n",blockIndex
);
5776 ulOffset
.u
.HighPart
= 0;
5777 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5780 StorageImpl_ReadAt(This
->parentStorage
,
5783 bytesToReadInBuffer
,
5786 * Step to the next big block.
5788 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5789 return STG_E_DOCFILECORRUPT
;
5791 bufferWalker
+= bytesReadAt
;
5792 size
-= bytesReadAt
;
5793 *bytesRead
+= bytesReadAt
;
5794 offsetInBlock
= 0; /* There is no offset on the next block */
5796 if (bytesToReadInBuffer
!= bytesReadAt
)
5800 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5803 /******************************************************************************
5804 * BlockChainStream_WriteAt
5806 * Writes the specified number of bytes to this chain at the specified offset.
5807 * Will fail if not all specified number of bytes have been written.
5809 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5810 ULARGE_INTEGER offset
,
5813 ULONG
* bytesWritten
)
5815 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5816 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5819 const BYTE
* bufferWalker
;
5822 * Find the first block in the stream that contains part of the buffer.
5824 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5826 /* BlockChainStream_SetSize should have already been called to ensure we have
5827 * enough blocks in the chain to write into */
5828 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5830 ERR("not enough blocks in chain to write data\n");
5831 return STG_E_DOCFILECORRUPT
;
5835 bufferWalker
= buffer
;
5837 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5839 ULARGE_INTEGER ulOffset
;
5840 DWORD bytesWrittenAt
;
5842 * Calculate how many bytes we can copy from this big block.
5845 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5847 TRACE("block %i\n",blockIndex
);
5848 ulOffset
.u
.HighPart
= 0;
5849 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5852 StorageImpl_WriteAt(This
->parentStorage
,
5859 * Step to the next big block.
5861 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5863 return STG_E_DOCFILECORRUPT
;
5865 bufferWalker
+= bytesWrittenAt
;
5866 size
-= bytesWrittenAt
;
5867 *bytesWritten
+= bytesWrittenAt
;
5868 offsetInBlock
= 0; /* There is no offset on the next block */
5870 if (bytesWrittenAt
!= bytesToWrite
)
5874 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5877 /******************************************************************************
5878 * BlockChainStream_Shrink
5880 * Shrinks this chain in the big block depot.
5882 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5883 ULARGE_INTEGER newSize
)
5889 * Figure out how many blocks are needed to contain the new size
5891 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5893 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5899 * Go to the new end of chain
5901 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
5903 /* Mark the new end of chain */
5904 StorageImpl_SetNextBlockInChain(
5905 This
->parentStorage
,
5907 BLOCK_END_OF_CHAIN
);
5909 This
->tailIndex
= blockIndex
;
5913 if (This
->headOfStreamPlaceHolder
!= 0)
5915 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
5919 DirEntry chainEntry
;
5920 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5922 StorageImpl_ReadDirEntry(
5923 This
->parentStorage
,
5924 This
->ownerDirEntry
,
5927 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
5929 StorageImpl_WriteDirEntry(
5930 This
->parentStorage
,
5931 This
->ownerDirEntry
,
5935 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5938 This
->numBlocks
= numBlocks
;
5941 * Mark the extra blocks as free
5943 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
5945 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5946 StorageImpl_FreeBigBlock(This
->parentStorage
,
5947 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
5948 if (last_run
->lastOffset
== last_run
->firstOffset
)
5949 This
->indexCacheLen
--;
5951 last_run
->lastOffset
--;
5957 /******************************************************************************
5958 * BlockChainStream_Enlarge
5960 * Grows this chain in the big block depot.
5962 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5963 ULARGE_INTEGER newSize
)
5965 ULONG blockIndex
, currentBlock
;
5967 ULONG oldNumBlocks
= 0;
5969 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5972 * Empty chain. Create the head.
5974 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5976 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5977 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5979 BLOCK_END_OF_CHAIN
);
5981 if (This
->headOfStreamPlaceHolder
!= 0)
5983 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5987 DirEntry chainEntry
;
5988 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5990 StorageImpl_ReadDirEntry(
5991 This
->parentStorage
,
5992 This
->ownerDirEntry
,
5995 chainEntry
.startingBlock
= blockIndex
;
5997 StorageImpl_WriteDirEntry(
5998 This
->parentStorage
,
5999 This
->ownerDirEntry
,
6003 This
->tailIndex
= blockIndex
;
6004 This
->numBlocks
= 1;
6008 * Figure out how many blocks are needed to contain this stream
6010 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6012 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6016 * Go to the current end of chain
6018 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6020 currentBlock
= blockIndex
;
6022 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6025 currentBlock
= blockIndex
;
6027 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6032 This
->tailIndex
= currentBlock
;
6035 currentBlock
= This
->tailIndex
;
6036 oldNumBlocks
= This
->numBlocks
;
6039 * Add new blocks to the chain
6041 if (oldNumBlocks
< newNumBlocks
)
6043 while (oldNumBlocks
< newNumBlocks
)
6045 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6047 StorageImpl_SetNextBlockInChain(
6048 This
->parentStorage
,
6052 StorageImpl_SetNextBlockInChain(
6053 This
->parentStorage
,
6055 BLOCK_END_OF_CHAIN
);
6057 currentBlock
= blockIndex
;
6061 This
->tailIndex
= blockIndex
;
6062 This
->numBlocks
= newNumBlocks
;
6065 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6071 /******************************************************************************
6072 * BlockChainStream_SetSize
6074 * Sets the size of this stream. The big block depot will be updated.
6075 * The file will grow if we grow the chain.
6077 * TODO: Free the actual blocks in the file when we shrink the chain.
6078 * Currently, the blocks are still in the file. So the file size
6079 * doesn't shrink even if we shrink streams.
6081 BOOL
BlockChainStream_SetSize(
6082 BlockChainStream
* This
,
6083 ULARGE_INTEGER newSize
)
6085 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6087 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6090 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6092 BlockChainStream_Shrink(This
, newSize
);
6096 BlockChainStream_Enlarge(This
, newSize
);
6102 /******************************************************************************
6103 * BlockChainStream_GetSize
6105 * Returns the size of this chain.
6106 * Will return the block count if this chain doesn't have a directory entry.
6108 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6110 DirEntry chainEntry
;
6112 if(This
->headOfStreamPlaceHolder
== NULL
)
6115 * This chain has a directory entry so use the size value from there.
6117 StorageImpl_ReadDirEntry(
6118 This
->parentStorage
,
6119 This
->ownerDirEntry
,
6122 return chainEntry
.size
;
6127 * this chain is a chain that does not have a directory entry, figure out the
6128 * size by making the product number of used blocks times the
6131 ULARGE_INTEGER result
;
6132 result
.u
.HighPart
= 0;
6135 BlockChainStream_GetCount(This
) *
6136 This
->parentStorage
->bigBlockSize
;
6142 /******************************************************************************
6143 ** SmallBlockChainStream implementation
6146 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6147 StorageImpl
* parentStorage
,
6148 ULONG
* headOfStreamPlaceHolder
,
6151 SmallBlockChainStream
* newStream
;
6153 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6155 newStream
->parentStorage
= parentStorage
;
6156 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6157 newStream
->ownerDirEntry
= dirEntry
;
6162 void SmallBlockChainStream_Destroy(
6163 SmallBlockChainStream
* This
)
6165 HeapFree(GetProcessHeap(), 0, This
);
6168 /******************************************************************************
6169 * SmallBlockChainStream_GetHeadOfChain
6171 * Returns the head of this chain of small blocks.
6173 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6174 SmallBlockChainStream
* This
)
6176 DirEntry chainEntry
;
6179 if (This
->headOfStreamPlaceHolder
!= NULL
)
6180 return *(This
->headOfStreamPlaceHolder
);
6182 if (This
->ownerDirEntry
)
6184 hr
= StorageImpl_ReadDirEntry(
6185 This
->parentStorage
,
6186 This
->ownerDirEntry
,
6191 return chainEntry
.startingBlock
;
6196 return BLOCK_END_OF_CHAIN
;
6199 /******************************************************************************
6200 * SmallBlockChainStream_GetNextBlockInChain
6202 * Returns the index of the next small block in this chain.
6205 * - BLOCK_END_OF_CHAIN: end of this chain
6206 * - BLOCK_UNUSED: small block 'blockIndex' is free
6208 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6209 SmallBlockChainStream
* This
,
6211 ULONG
* nextBlockInChain
)
6213 ULARGE_INTEGER offsetOfBlockInDepot
;
6218 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6220 offsetOfBlockInDepot
.u
.HighPart
= 0;
6221 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6224 * Read those bytes in the buffer from the small block file.
6226 res
= BlockChainStream_ReadAt(
6227 This
->parentStorage
->smallBlockDepotChain
,
6228 offsetOfBlockInDepot
,
6235 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6242 /******************************************************************************
6243 * SmallBlockChainStream_SetNextBlockInChain
6245 * Writes the index of the next block of the specified block in the small
6247 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6248 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6250 static void SmallBlockChainStream_SetNextBlockInChain(
6251 SmallBlockChainStream
* This
,
6255 ULARGE_INTEGER offsetOfBlockInDepot
;
6259 offsetOfBlockInDepot
.u
.HighPart
= 0;
6260 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6262 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6265 * Read those bytes in the buffer from the small block file.
6267 BlockChainStream_WriteAt(
6268 This
->parentStorage
->smallBlockDepotChain
,
6269 offsetOfBlockInDepot
,
6275 /******************************************************************************
6276 * SmallBlockChainStream_FreeBlock
6278 * Flag small block 'blockIndex' as free in the small block depot.
6280 static void SmallBlockChainStream_FreeBlock(
6281 SmallBlockChainStream
* This
,
6284 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6287 /******************************************************************************
6288 * SmallBlockChainStream_GetNextFreeBlock
6290 * Returns the index of a free small block. The small block depot will be
6291 * enlarged if necessary. The small block chain will also be enlarged if
6294 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6295 SmallBlockChainStream
* This
)
6297 ULARGE_INTEGER offsetOfBlockInDepot
;
6300 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6301 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6303 ULONG smallBlocksPerBigBlock
;
6305 offsetOfBlockInDepot
.u
.HighPart
= 0;
6308 * Scan the small block depot for a free block
6310 while (nextBlockIndex
!= BLOCK_UNUSED
)
6312 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6314 res
= BlockChainStream_ReadAt(
6315 This
->parentStorage
->smallBlockDepotChain
,
6316 offsetOfBlockInDepot
,
6322 * If we run out of space for the small block depot, enlarge it
6326 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6328 if (nextBlockIndex
!= BLOCK_UNUSED
)
6334 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6336 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6337 ULARGE_INTEGER newSize
, offset
;
6340 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6341 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6344 * Initialize all the small blocks to free
6346 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6347 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6348 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6349 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6351 StorageImpl_SaveFileHeader(This
->parentStorage
);
6355 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6357 smallBlocksPerBigBlock
=
6358 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6361 * Verify if we have to allocate big blocks to contain small blocks
6363 if (blockIndex
% smallBlocksPerBigBlock
== 0)
6366 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6367 ULARGE_INTEGER old_size
, size_required
;
6369 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6371 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6373 if (size_required
.QuadPart
> old_size
.QuadPart
)
6375 BlockChainStream_SetSize(
6376 This
->parentStorage
->smallBlockRootChain
,
6379 StorageImpl_ReadDirEntry(
6380 This
->parentStorage
,
6381 This
->parentStorage
->base
.storageDirEntry
,
6384 rootEntry
.size
= size_required
;
6386 StorageImpl_WriteDirEntry(
6387 This
->parentStorage
,
6388 This
->parentStorage
->base
.storageDirEntry
,
6396 /******************************************************************************
6397 * SmallBlockChainStream_ReadAt
6399 * Reads a specified number of bytes from this chain at the specified offset.
6400 * bytesRead may be NULL.
6401 * Failure will be returned if the specified number of bytes has not been read.
6403 HRESULT
SmallBlockChainStream_ReadAt(
6404 SmallBlockChainStream
* This
,
6405 ULARGE_INTEGER offset
,
6411 ULARGE_INTEGER offsetInBigBlockFile
;
6412 ULONG blockNoInSequence
=
6413 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6415 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6416 ULONG bytesToReadInBuffer
;
6418 ULONG bytesReadFromBigBlockFile
;
6422 * This should never happen on a small block file.
6424 assert(offset
.u
.HighPart
==0);
6427 * Find the first block in the stream that contains part of the buffer.
6429 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6431 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6433 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6436 blockNoInSequence
--;
6440 * Start reading the buffer.
6443 bufferWalker
= buffer
;
6445 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6448 * Calculate how many bytes we can copy from this small block.
6450 bytesToReadInBuffer
=
6451 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6454 * Calculate the offset of the small block in the small block file.
6456 offsetInBigBlockFile
.u
.HighPart
= 0;
6457 offsetInBigBlockFile
.u
.LowPart
=
6458 blockIndex
* This
->parentStorage
->smallBlockSize
;
6460 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6463 * Read those bytes in the buffer from the small block file.
6464 * The small block has already been identified so it shouldn't fail
6465 * unless the file is corrupt.
6467 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6468 offsetInBigBlockFile
,
6469 bytesToReadInBuffer
,
6471 &bytesReadFromBigBlockFile
);
6477 * Step to the next big block.
6479 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6481 return STG_E_DOCFILECORRUPT
;
6483 bufferWalker
+= bytesReadFromBigBlockFile
;
6484 size
-= bytesReadFromBigBlockFile
;
6485 *bytesRead
+= bytesReadFromBigBlockFile
;
6486 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6489 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
6492 /******************************************************************************
6493 * SmallBlockChainStream_WriteAt
6495 * Writes the specified number of bytes to this chain at the specified offset.
6496 * Will fail if not all specified number of bytes have been written.
6498 HRESULT
SmallBlockChainStream_WriteAt(
6499 SmallBlockChainStream
* This
,
6500 ULARGE_INTEGER offset
,
6503 ULONG
* bytesWritten
)
6505 ULARGE_INTEGER offsetInBigBlockFile
;
6506 ULONG blockNoInSequence
=
6507 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6509 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6510 ULONG bytesToWriteInBuffer
;
6512 ULONG bytesWrittenToBigBlockFile
;
6513 const BYTE
* bufferWalker
;
6517 * This should never happen on a small block file.
6519 assert(offset
.u
.HighPart
==0);
6522 * Find the first block in the stream that contains part of the buffer.
6524 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6526 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6528 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6529 return STG_E_DOCFILECORRUPT
;
6530 blockNoInSequence
--;
6534 * Start writing the buffer.
6537 bufferWalker
= buffer
;
6538 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6541 * Calculate how many bytes we can copy to this small block.
6543 bytesToWriteInBuffer
=
6544 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6547 * Calculate the offset of the small block in the small block file.
6549 offsetInBigBlockFile
.u
.HighPart
= 0;
6550 offsetInBigBlockFile
.u
.LowPart
=
6551 blockIndex
* This
->parentStorage
->smallBlockSize
;
6553 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6556 * Write those bytes in the buffer to the small block file.
6558 res
= BlockChainStream_WriteAt(
6559 This
->parentStorage
->smallBlockRootChain
,
6560 offsetInBigBlockFile
,
6561 bytesToWriteInBuffer
,
6563 &bytesWrittenToBigBlockFile
);
6568 * Step to the next big block.
6570 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6573 bufferWalker
+= bytesWrittenToBigBlockFile
;
6574 size
-= bytesWrittenToBigBlockFile
;
6575 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6576 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6579 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6582 /******************************************************************************
6583 * SmallBlockChainStream_Shrink
6585 * Shrinks this chain in the small block depot.
6587 static BOOL
SmallBlockChainStream_Shrink(
6588 SmallBlockChainStream
* This
,
6589 ULARGE_INTEGER newSize
)
6591 ULONG blockIndex
, extraBlock
;
6595 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6597 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6600 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6603 * Go to the new end of chain
6605 while (count
< numBlocks
)
6607 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6614 * If the count is 0, we have a special case, the head of the chain was
6619 DirEntry chainEntry
;
6621 StorageImpl_ReadDirEntry(This
->parentStorage
,
6622 This
->ownerDirEntry
,
6625 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6627 StorageImpl_WriteDirEntry(This
->parentStorage
,
6628 This
->ownerDirEntry
,
6632 * We start freeing the chain at the head block.
6634 extraBlock
= blockIndex
;
6638 /* Get the next block before marking the new end */
6639 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6643 /* Mark the new end of chain */
6644 SmallBlockChainStream_SetNextBlockInChain(
6647 BLOCK_END_OF_CHAIN
);
6651 * Mark the extra blocks as free
6653 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6655 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6658 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6659 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
6660 extraBlock
= blockIndex
;
6666 /******************************************************************************
6667 * SmallBlockChainStream_Enlarge
6669 * Grows this chain in the small block depot.
6671 static BOOL
SmallBlockChainStream_Enlarge(
6672 SmallBlockChainStream
* This
,
6673 ULARGE_INTEGER newSize
)
6675 ULONG blockIndex
, currentBlock
;
6677 ULONG oldNumBlocks
= 0;
6679 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6682 * Empty chain. Create the head.
6684 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6686 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6687 SmallBlockChainStream_SetNextBlockInChain(
6690 BLOCK_END_OF_CHAIN
);
6692 if (This
->headOfStreamPlaceHolder
!= NULL
)
6694 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6698 DirEntry chainEntry
;
6700 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6703 chainEntry
.startingBlock
= blockIndex
;
6705 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6710 currentBlock
= blockIndex
;
6713 * Figure out how many blocks are needed to contain this stream
6715 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6717 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6721 * Go to the current end of chain
6723 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6726 currentBlock
= blockIndex
;
6727 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6732 * Add new blocks to the chain
6734 while (oldNumBlocks
< newNumBlocks
)
6736 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6737 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6739 SmallBlockChainStream_SetNextBlockInChain(
6742 BLOCK_END_OF_CHAIN
);
6744 currentBlock
= blockIndex
;
6751 /******************************************************************************
6752 * SmallBlockChainStream_SetSize
6754 * Sets the size of this stream.
6755 * The file will grow if we grow the chain.
6757 * TODO: Free the actual blocks in the file when we shrink the chain.
6758 * Currently, the blocks are still in the file. So the file size
6759 * doesn't shrink even if we shrink streams.
6761 BOOL
SmallBlockChainStream_SetSize(
6762 SmallBlockChainStream
* This
,
6763 ULARGE_INTEGER newSize
)
6765 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6767 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6770 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6772 SmallBlockChainStream_Shrink(This
, newSize
);
6776 SmallBlockChainStream_Enlarge(This
, newSize
);
6782 /******************************************************************************
6783 * SmallBlockChainStream_GetCount
6785 * Returns the number of small blocks that comprises this chain.
6786 * This is not the size of the stream as the last block may not be full!
6789 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6794 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6796 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6800 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6801 blockIndex
, &blockIndex
)))
6808 /******************************************************************************
6809 * SmallBlockChainStream_GetSize
6811 * Returns the size of this chain.
6813 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6815 DirEntry chainEntry
;
6817 if(This
->headOfStreamPlaceHolder
!= NULL
)
6819 ULARGE_INTEGER result
;
6820 result
.u
.HighPart
= 0;
6822 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6823 This
->parentStorage
->smallBlockSize
;
6828 StorageImpl_ReadDirEntry(
6829 This
->parentStorage
,
6830 This
->ownerDirEntry
,
6833 return chainEntry
.size
;
6836 static HRESULT
create_storagefile(
6840 STGOPTIONS
* pStgOptions
,
6844 StorageBaseImpl
* newStorage
= 0;
6845 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6846 HRESULT hr
= STG_E_INVALIDFLAG
;
6850 DWORD fileAttributes
;
6851 WCHAR tempFileName
[MAX_PATH
];
6854 return STG_E_INVALIDPOINTER
;
6856 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
6857 return STG_E_INVALIDPARAMETER
;
6859 /* if no share mode given then DENY_NONE is the default */
6860 if (STGM_SHARE_MODE(grfMode
) == 0)
6861 grfMode
|= STGM_SHARE_DENY_NONE
;
6863 if ( FAILED( validateSTGM(grfMode
) ))
6866 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6867 switch(STGM_ACCESS_MODE(grfMode
))
6870 case STGM_READWRITE
:
6876 /* in direct mode, can only use SHARE_EXCLUSIVE */
6877 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6880 /* but in transacted mode, any share mode is valid */
6883 * Generate a unique name.
6887 WCHAR tempPath
[MAX_PATH
];
6888 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6890 memset(tempPath
, 0, sizeof(tempPath
));
6891 memset(tempFileName
, 0, sizeof(tempFileName
));
6893 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6896 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6897 pwcsName
= tempFileName
;
6900 hr
= STG_E_INSUFFICIENTMEMORY
;
6904 creationMode
= TRUNCATE_EXISTING
;
6908 creationMode
= GetCreationModeFromSTGM(grfMode
);
6912 * Interpret the STGM value grfMode
6914 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6915 accessMode
= GetAccessModeFromSTGM(grfMode
);
6917 if (grfMode
& STGM_DELETEONRELEASE
)
6918 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6920 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6922 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6926 FIXME("Storage share mode not implemented.\n");
6931 hFile
= CreateFileW(pwcsName
,
6939 if (hFile
== INVALID_HANDLE_VALUE
)
6941 if(GetLastError() == ERROR_FILE_EXISTS
)
6942 hr
= STG_E_FILEALREADYEXISTS
;
6949 * Allocate and initialize the new IStorage32object.
6951 hr
= Storage_Construct(
6958 pStgOptions
->ulSectorSize
,
6966 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
6968 IStorage_Release((IStorage
*)newStorage
);
6971 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6976 /******************************************************************************
6977 * StgCreateDocfile [OLE32.@]
6978 * Creates a new compound file storage object
6981 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6982 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6983 * reserved [ ?] unused?, usually 0
6984 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6987 * S_OK if the file was successfully created
6988 * some STG_E_ value if error
6990 * if pwcsName is NULL, create file with new unique name
6991 * the function can returns
6992 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6995 HRESULT WINAPI
StgCreateDocfile(
6999 IStorage
**ppstgOpen
)
7001 STGOPTIONS stgoptions
= {1, 0, 512};
7003 TRACE("(%s, %x, %d, %p)\n",
7004 debugstr_w(pwcsName
), grfMode
,
7005 reserved
, ppstgOpen
);
7008 return STG_E_INVALIDPOINTER
;
7010 return STG_E_INVALIDPARAMETER
;
7012 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7015 /******************************************************************************
7016 * StgCreateStorageEx [OLE32.@]
7018 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7020 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7021 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7023 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7025 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7026 return STG_E_INVALIDPARAMETER
;
7029 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7031 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7032 return STG_E_INVALIDPARAMETER
;
7035 if (stgfmt
== STGFMT_FILE
)
7037 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7038 return STG_E_INVALIDPARAMETER
;
7041 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7043 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7047 ERR("Invalid stgfmt argument\n");
7048 return STG_E_INVALIDPARAMETER
;
7051 /******************************************************************************
7052 * StgCreatePropSetStg [OLE32.@]
7054 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7055 IPropertySetStorage
**ppPropSetStg
)
7059 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
7061 hr
= STG_E_INVALIDPARAMETER
;
7063 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
7064 (void**)ppPropSetStg
);
7068 /******************************************************************************
7069 * StgOpenStorageEx [OLE32.@]
7071 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7073 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7074 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7076 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7078 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7079 return STG_E_INVALIDPARAMETER
;
7085 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7086 return STG_E_INVALIDPARAMETER
;
7088 case STGFMT_STORAGE
:
7091 case STGFMT_DOCFILE
:
7092 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7094 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7095 return STG_E_INVALIDPARAMETER
;
7097 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7101 WARN("STGFMT_ANY assuming storage\n");
7105 return STG_E_INVALIDPARAMETER
;
7108 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7112 /******************************************************************************
7113 * StgOpenStorage [OLE32.@]
7115 HRESULT WINAPI
StgOpenStorage(
7116 const OLECHAR
*pwcsName
,
7117 IStorage
*pstgPriority
,
7121 IStorage
**ppstgOpen
)
7123 StorageBaseImpl
* newStorage
= 0;
7129 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7130 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7131 snbExclude
, reserved
, ppstgOpen
);
7135 hr
= STG_E_INVALIDNAME
;
7141 hr
= STG_E_INVALIDPOINTER
;
7147 hr
= STG_E_INVALIDPARAMETER
;
7151 if (grfMode
& STGM_PRIORITY
)
7153 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7154 return STG_E_INVALIDFLAG
;
7155 if (grfMode
& STGM_DELETEONRELEASE
)
7156 return STG_E_INVALIDFUNCTION
;
7157 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7158 return STG_E_INVALIDFLAG
;
7159 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7160 grfMode
|= STGM_SHARE_DENY_NONE
;
7162 /* STGM_PRIORITY stops other IStorage objects on the same file from
7163 * committing until the STGM_PRIORITY IStorage is closed. it also
7164 * stops non-transacted mode StgOpenStorage calls with write access from
7165 * succeeding. obviously, both of these cannot be achieved through just
7166 * file share flags */
7167 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7171 * Validate the sharing mode
7173 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7174 switch(STGM_SHARE_MODE(grfMode
))
7176 case STGM_SHARE_EXCLUSIVE
:
7177 case STGM_SHARE_DENY_WRITE
:
7180 hr
= STG_E_INVALIDFLAG
;
7184 if ( FAILED( validateSTGM(grfMode
) ) ||
7185 (grfMode
&STGM_CREATE
))
7187 hr
= STG_E_INVALIDFLAG
;
7191 /* shared reading requires transacted mode */
7192 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7193 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7194 !(grfMode
&STGM_TRANSACTED
) )
7196 hr
= STG_E_INVALIDFLAG
;
7201 * Interpret the STGM value grfMode
7203 shareMode
= GetShareModeFromSTGM(grfMode
);
7204 accessMode
= GetAccessModeFromSTGM(grfMode
);
7208 hFile
= CreateFileW( pwcsName
,
7213 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7216 if (hFile
==INVALID_HANDLE_VALUE
)
7218 DWORD last_error
= GetLastError();
7224 case ERROR_FILE_NOT_FOUND
:
7225 hr
= STG_E_FILENOTFOUND
;
7228 case ERROR_PATH_NOT_FOUND
:
7229 hr
= STG_E_PATHNOTFOUND
;
7232 case ERROR_ACCESS_DENIED
:
7233 case ERROR_WRITE_PROTECT
:
7234 hr
= STG_E_ACCESSDENIED
;
7237 case ERROR_SHARING_VIOLATION
:
7238 hr
= STG_E_SHAREVIOLATION
;
7249 * Refuse to open the file if it's too small to be a structured storage file
7250 * FIXME: verify the file when reading instead of here
7252 if (GetFileSize(hFile
, NULL
) < 0x100)
7255 hr
= STG_E_FILEALREADYEXISTS
;
7260 * Allocate and initialize the new IStorage32object.
7262 hr
= Storage_Construct(
7275 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7277 if(hr
== STG_E_INVALIDHEADER
)
7278 hr
= STG_E_FILEALREADYEXISTS
;
7283 * Get an "out" pointer for the caller.
7285 *ppstgOpen
= (IStorage
*)newStorage
;
7288 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7292 /******************************************************************************
7293 * StgCreateDocfileOnILockBytes [OLE32.@]
7295 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7299 IStorage
** ppstgOpen
)
7301 StorageBaseImpl
* newStorage
= 0;
7304 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7305 return STG_E_INVALIDPOINTER
;
7308 * Allocate and initialize the new IStorage object.
7310 hr
= Storage_Construct(
7326 * Get an "out" pointer for the caller.
7328 *ppstgOpen
= (IStorage
*)newStorage
;
7333 /******************************************************************************
7334 * StgOpenStorageOnILockBytes [OLE32.@]
7336 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7338 IStorage
*pstgPriority
,
7342 IStorage
**ppstgOpen
)
7344 StorageBaseImpl
* newStorage
= 0;
7347 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7348 return STG_E_INVALIDPOINTER
;
7350 if ( FAILED( validateSTGM(grfMode
) ))
7351 return STG_E_INVALIDFLAG
;
7356 * Allocate and initialize the new IStorage object.
7358 hr
= Storage_Construct(
7374 * Get an "out" pointer for the caller.
7376 *ppstgOpen
= (IStorage
*)newStorage
;
7381 /******************************************************************************
7382 * StgSetTimes [ole32.@]
7383 * StgSetTimes [OLE32.@]
7387 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7388 FILETIME
const *patime
, FILETIME
const *pmtime
)
7390 IStorage
*stg
= NULL
;
7393 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7395 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7399 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7400 IStorage_Release(stg
);
7406 /******************************************************************************
7407 * StgIsStorageILockBytes [OLE32.@]
7409 * Determines if the ILockBytes contains a storage object.
7411 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7414 ULARGE_INTEGER offset
;
7416 offset
.u
.HighPart
= 0;
7417 offset
.u
.LowPart
= 0;
7419 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
7421 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
7427 /******************************************************************************
7428 * WriteClassStg [OLE32.@]
7430 * This method will store the specified CLSID in the specified storage object
7432 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7437 return E_INVALIDARG
;
7440 return STG_E_INVALIDPOINTER
;
7442 hRes
= IStorage_SetClass(pStg
, rclsid
);
7447 /***********************************************************************
7448 * ReadClassStg (OLE32.@)
7450 * This method reads the CLSID previously written to a storage object with
7451 * the WriteClassStg.
7454 * pstg [I] IStorage pointer
7455 * pclsid [O] Pointer to where the CLSID is written
7459 * Failure: HRESULT code.
7461 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7466 TRACE("(%p, %p)\n", pstg
, pclsid
);
7468 if(!pstg
|| !pclsid
)
7469 return E_INVALIDARG
;
7472 * read a STATSTG structure (contains the clsid) from the storage
7474 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7477 *pclsid
=pstatstg
.clsid
;
7482 /***********************************************************************
7483 * OleLoadFromStream (OLE32.@)
7485 * This function loads an object from stream
7487 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7491 LPPERSISTSTREAM xstm
;
7493 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7495 res
=ReadClassStm(pStm
,&clsid
);
7498 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7501 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7503 IUnknown_Release((IUnknown
*)*ppvObj
);
7506 res
=IPersistStream_Load(xstm
,pStm
);
7507 IPersistStream_Release(xstm
);
7508 /* FIXME: all refcounts ok at this point? I think they should be:
7511 * xstm : 0 (released)
7516 /***********************************************************************
7517 * OleSaveToStream (OLE32.@)
7519 * This function saves an object with the IPersistStream interface on it
7520 * to the specified stream.
7522 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7528 TRACE("(%p,%p)\n",pPStm
,pStm
);
7530 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7532 if (SUCCEEDED(res
)){
7534 res
=WriteClassStm(pStm
,&clsid
);
7538 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7541 TRACE("Finished Save\n");
7545 /****************************************************************************
7546 * This method validate a STGM parameter that can contain the values below
7548 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7549 * The stgm values contained in 0xffff0000 are bitmasks.
7551 * STGM_DIRECT 0x00000000
7552 * STGM_TRANSACTED 0x00010000
7553 * STGM_SIMPLE 0x08000000
7555 * STGM_READ 0x00000000
7556 * STGM_WRITE 0x00000001
7557 * STGM_READWRITE 0x00000002
7559 * STGM_SHARE_DENY_NONE 0x00000040
7560 * STGM_SHARE_DENY_READ 0x00000030
7561 * STGM_SHARE_DENY_WRITE 0x00000020
7562 * STGM_SHARE_EXCLUSIVE 0x00000010
7564 * STGM_PRIORITY 0x00040000
7565 * STGM_DELETEONRELEASE 0x04000000
7567 * STGM_CREATE 0x00001000
7568 * STGM_CONVERT 0x00020000
7569 * STGM_FAILIFTHERE 0x00000000
7571 * STGM_NOSCRATCH 0x00100000
7572 * STGM_NOSNAPSHOT 0x00200000
7574 static HRESULT
validateSTGM(DWORD stgm
)
7576 DWORD access
= STGM_ACCESS_MODE(stgm
);
7577 DWORD share
= STGM_SHARE_MODE(stgm
);
7578 DWORD create
= STGM_CREATE_MODE(stgm
);
7580 if (stgm
&~STGM_KNOWN_FLAGS
)
7582 ERR("unknown flags %08x\n", stgm
);
7590 case STGM_READWRITE
:
7598 case STGM_SHARE_DENY_NONE
:
7599 case STGM_SHARE_DENY_READ
:
7600 case STGM_SHARE_DENY_WRITE
:
7601 case STGM_SHARE_EXCLUSIVE
:
7610 case STGM_FAILIFTHERE
:
7617 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7619 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7623 * STGM_CREATE | STGM_CONVERT
7624 * if both are false, STGM_FAILIFTHERE is set to TRUE
7626 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7630 * STGM_NOSCRATCH requires STGM_TRANSACTED
7632 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7636 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7637 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7639 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7640 (!(stgm
& STGM_TRANSACTED
) ||
7641 share
== STGM_SHARE_EXCLUSIVE
||
7642 share
== STGM_SHARE_DENY_WRITE
) )
7648 /****************************************************************************
7649 * GetShareModeFromSTGM
7651 * This method will return a share mode flag from a STGM value.
7652 * The STGM value is assumed valid.
7654 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7656 switch (STGM_SHARE_MODE(stgm
))
7658 case STGM_SHARE_DENY_NONE
:
7659 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7660 case STGM_SHARE_DENY_READ
:
7661 return FILE_SHARE_WRITE
;
7662 case STGM_SHARE_DENY_WRITE
:
7663 return FILE_SHARE_READ
;
7664 case STGM_SHARE_EXCLUSIVE
:
7667 ERR("Invalid share mode!\n");
7672 /****************************************************************************
7673 * GetAccessModeFromSTGM
7675 * This method will return an access mode flag from a STGM value.
7676 * The STGM value is assumed valid.
7678 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7680 switch (STGM_ACCESS_MODE(stgm
))
7683 return GENERIC_READ
;
7685 case STGM_READWRITE
:
7686 return GENERIC_READ
| GENERIC_WRITE
;
7688 ERR("Invalid access mode!\n");
7693 /****************************************************************************
7694 * GetCreationModeFromSTGM
7696 * This method will return a creation mode flag from a STGM value.
7697 * The STGM value is assumed valid.
7699 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7701 switch(STGM_CREATE_MODE(stgm
))
7704 return CREATE_ALWAYS
;
7706 FIXME("STGM_CONVERT not implemented!\n");
7708 case STGM_FAILIFTHERE
:
7711 ERR("Invalid create mode!\n");
7717 /*************************************************************************
7718 * OLECONVERT_LoadOLE10 [Internal]
7720 * Loads the OLE10 STREAM to memory
7723 * pOleStream [I] The OLESTREAM
7724 * pData [I] Data Structure for the OLESTREAM Data
7728 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7729 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7732 * This function is used by OleConvertOLESTREAMToIStorage only.
7734 * Memory allocated for pData must be freed by the caller
7736 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7739 HRESULT hRes
= S_OK
;
7743 pData
->pData
= NULL
;
7744 pData
->pstrOleObjFileName
= NULL
;
7746 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7749 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7750 if(dwSize
!= sizeof(pData
->dwOleID
))
7752 hRes
= CONVERT10_E_OLESTREAM_GET
;
7754 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7756 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7767 /* Get the TypeID... more info needed for this field */
7768 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7769 if(dwSize
!= sizeof(pData
->dwTypeID
))
7771 hRes
= CONVERT10_E_OLESTREAM_GET
;
7776 if(pData
->dwTypeID
!= 0)
7778 /* Get the length of the OleTypeName */
7779 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7780 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7782 hRes
= CONVERT10_E_OLESTREAM_GET
;
7787 if(pData
->dwOleTypeNameLength
> 0)
7789 /* Get the OleTypeName */
7790 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7791 if(dwSize
!= pData
->dwOleTypeNameLength
)
7793 hRes
= CONVERT10_E_OLESTREAM_GET
;
7799 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7800 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7802 hRes
= CONVERT10_E_OLESTREAM_GET
;
7806 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7807 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7808 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7809 if(pData
->pstrOleObjFileName
)
7811 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7812 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7814 hRes
= CONVERT10_E_OLESTREAM_GET
;
7818 hRes
= CONVERT10_E_OLESTREAM_GET
;
7823 /* Get the Width of the Metafile */
7824 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7825 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7827 hRes
= CONVERT10_E_OLESTREAM_GET
;
7831 /* Get the Height of the Metafile */
7832 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7833 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7835 hRes
= CONVERT10_E_OLESTREAM_GET
;
7841 /* Get the Length of the Data */
7842 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7843 if(dwSize
!= sizeof(pData
->dwDataLength
))
7845 hRes
= CONVERT10_E_OLESTREAM_GET
;
7849 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7851 if(!bStrem1
) /* if it is a second OLE stream data */
7853 pData
->dwDataLength
-= 8;
7854 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7855 if(dwSize
!= sizeof(pData
->strUnknown
))
7857 hRes
= CONVERT10_E_OLESTREAM_GET
;
7863 if(pData
->dwDataLength
> 0)
7865 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7867 /* Get Data (ex. IStorage, Metafile, or BMP) */
7870 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7871 if(dwSize
!= pData
->dwDataLength
)
7873 hRes
= CONVERT10_E_OLESTREAM_GET
;
7878 hRes
= CONVERT10_E_OLESTREAM_GET
;
7887 /*************************************************************************
7888 * OLECONVERT_SaveOLE10 [Internal]
7890 * Saves the OLE10 STREAM From memory
7893 * pData [I] Data Structure for the OLESTREAM Data
7894 * pOleStream [I] The OLESTREAM to save
7898 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7901 * This function is used by OleConvertIStorageToOLESTREAM only.
7904 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7907 HRESULT hRes
= S_OK
;
7911 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7912 if(dwSize
!= sizeof(pData
->dwOleID
))
7914 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7919 /* Set the TypeID */
7920 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7921 if(dwSize
!= sizeof(pData
->dwTypeID
))
7923 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7927 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7929 /* Set the Length of the OleTypeName */
7930 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7931 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7933 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7938 if(pData
->dwOleTypeNameLength
> 0)
7940 /* Set the OleTypeName */
7941 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7942 if(dwSize
!= pData
->dwOleTypeNameLength
)
7944 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7951 /* Set the width of the Metafile */
7952 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7953 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7955 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7961 /* Set the height of the Metafile */
7962 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7963 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7965 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7971 /* Set the length of the Data */
7972 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7973 if(dwSize
!= sizeof(pData
->dwDataLength
))
7975 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7981 if(pData
->dwDataLength
> 0)
7983 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7984 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7985 if(dwSize
!= pData
->dwDataLength
)
7987 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7995 /*************************************************************************
7996 * OLECONVERT_GetOLE20FromOLE10[Internal]
7998 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7999 * opens it, and copies the content to the dest IStorage for
8000 * OleConvertOLESTREAMToIStorage
8004 * pDestStorage [I] The IStorage to copy the data to
8005 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8006 * nBufferLength [I] The size of the buffer
8015 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8019 IStorage
*pTempStorage
;
8020 DWORD dwNumOfBytesWritten
;
8021 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8022 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8024 /* Create a temp File */
8025 GetTempPathW(MAX_PATH
, wstrTempDir
);
8026 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8027 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8029 if(hFile
!= INVALID_HANDLE_VALUE
)
8031 /* Write IStorage Data to File */
8032 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8035 /* Open and copy temp storage to the Dest Storage */
8036 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8039 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8040 IStorage_Release(pTempStorage
);
8042 DeleteFileW(wstrTempFile
);
8047 /*************************************************************************
8048 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8050 * Saves the OLE10 STREAM From memory
8053 * pStorage [I] The Src IStorage to copy
8054 * pData [I] The Dest Memory to write to.
8057 * The size in bytes allocated for pData
8060 * Memory allocated for pData must be freed by the caller
8062 * Used by OleConvertIStorageToOLESTREAM only.
8065 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8069 DWORD nDataLength
= 0;
8070 IStorage
*pTempStorage
;
8071 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8072 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8076 /* Create temp Storage */
8077 GetTempPathW(MAX_PATH
, wstrTempDir
);
8078 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8079 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8083 /* Copy Src Storage to the Temp Storage */
8084 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8085 IStorage_Release(pTempStorage
);
8087 /* Open Temp Storage as a file and copy to memory */
8088 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8089 if(hFile
!= INVALID_HANDLE_VALUE
)
8091 nDataLength
= GetFileSize(hFile
, NULL
);
8092 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8093 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8096 DeleteFileW(wstrTempFile
);
8101 /*************************************************************************
8102 * OLECONVERT_CreateOleStream [Internal]
8104 * Creates the "\001OLE" stream in the IStorage if necessary.
8107 * pStorage [I] Dest storage to create the stream in
8113 * This function is used by OleConvertOLESTREAMToIStorage only.
8115 * This stream is still unknown, MS Word seems to have extra data
8116 * but since the data is stored in the OLESTREAM there should be
8117 * no need to recreate the stream. If the stream is manually
8118 * deleted it will create it with this default data.
8121 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8125 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8126 BYTE pOleStreamHeader
[] =
8128 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8130 0x00, 0x00, 0x00, 0x00
8133 /* Create stream if not present */
8134 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8135 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8139 /* Write default Data */
8140 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8141 IStream_Release(pStream
);
8145 /* write a string to a stream, preceded by its length */
8146 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8153 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8154 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8159 str
= CoTaskMemAlloc( len
);
8160 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8161 r
= IStream_Write( stm
, str
, len
, NULL
);
8162 CoTaskMemFree( str
);
8166 /* read a string preceded by its length from a stream */
8167 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8170 DWORD len
, count
= 0;
8174 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8177 if( count
!= sizeof(len
) )
8178 return E_OUTOFMEMORY
;
8180 TRACE("%d bytes\n",len
);
8182 str
= CoTaskMemAlloc( len
);
8184 return E_OUTOFMEMORY
;
8186 r
= IStream_Read( stm
, str
, len
, &count
);
8191 CoTaskMemFree( str
);
8192 return E_OUTOFMEMORY
;
8195 TRACE("Read string %s\n",debugstr_an(str
,len
));
8197 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8198 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8200 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8201 CoTaskMemFree( str
);
8209 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8210 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8214 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8216 static const BYTE unknown1
[12] =
8217 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8218 0xFF, 0xFF, 0xFF, 0xFF};
8219 static const BYTE unknown2
[16] =
8220 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8223 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8224 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8225 debugstr_w(szProgIDName
));
8227 /* Create a CompObj stream */
8228 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8229 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8233 /* Write CompObj Structure to stream */
8234 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8236 if( SUCCEEDED( r
) )
8237 r
= WriteClassStm( pstm
, clsid
);
8239 if( SUCCEEDED( r
) )
8240 r
= STREAM_WriteString( pstm
, lpszUserType
);
8241 if( SUCCEEDED( r
) )
8242 r
= STREAM_WriteString( pstm
, szClipName
);
8243 if( SUCCEEDED( r
) )
8244 r
= STREAM_WriteString( pstm
, szProgIDName
);
8245 if( SUCCEEDED( r
) )
8246 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8248 IStream_Release( pstm
);
8253 /***********************************************************************
8254 * WriteFmtUserTypeStg (OLE32.@)
8256 HRESULT WINAPI
WriteFmtUserTypeStg(
8257 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8260 WCHAR szwClipName
[0x40];
8261 CLSID clsid
= CLSID_NULL
;
8262 LPWSTR wstrProgID
= NULL
;
8265 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8267 /* get the clipboard format name */
8268 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8271 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8273 /* FIXME: There's room to save a CLSID and its ProgID, but
8274 the CLSID is not looked up in the registry and in all the
8275 tests I wrote it was CLSID_NULL. Where does it come from?
8278 /* get the real program ID. This may fail, but that's fine */
8279 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8281 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8283 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8284 lpszUserType
, szwClipName
, wstrProgID
);
8286 CoTaskMemFree(wstrProgID
);
8292 /******************************************************************************
8293 * ReadFmtUserTypeStg [OLE32.@]
8295 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8299 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8300 unsigned char unknown1
[12];
8301 unsigned char unknown2
[16];
8303 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8306 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8308 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8309 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8312 WARN("Failed to open stream r = %08x\n", r
);
8316 /* read the various parts of the structure */
8317 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8318 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8320 r
= ReadClassStm( stm
, &clsid
);
8324 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8328 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8332 r
= STREAM_ReadString( stm
, &szProgIDName
);
8336 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8337 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8340 /* ok, success... now we just need to store what we found */
8342 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8343 CoTaskMemFree( szOleTypeName
);
8345 if( lplpszUserType
)
8346 *lplpszUserType
= szCLSIDName
;
8347 CoTaskMemFree( szProgIDName
);
8350 IStream_Release( stm
);
8356 /*************************************************************************
8357 * OLECONVERT_CreateCompObjStream [Internal]
8359 * Creates a "\001CompObj" is the destination IStorage if necessary.
8362 * pStorage [I] The dest IStorage to create the CompObj Stream
8364 * strOleTypeName [I] The ProgID
8368 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8371 * This function is used by OleConvertOLESTREAMToIStorage only.
8373 * The stream data is stored in the OLESTREAM and there should be
8374 * no need to recreate the stream. If the stream is manually
8375 * deleted it will attempt to create it by querying the registry.
8379 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8382 HRESULT hStorageRes
, hRes
= S_OK
;
8383 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8384 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8385 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8387 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8388 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8390 /* Initialize the CompObj structure */
8391 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8392 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8393 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8396 /* Create a CompObj stream if it doesn't exist */
8397 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8398 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8399 if(hStorageRes
== S_OK
)
8401 /* copy the OleTypeName to the compobj struct */
8402 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8403 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8405 /* copy the OleTypeName to the compobj struct */
8406 /* Note: in the test made, these were Identical */
8407 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8408 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8411 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8412 bufferW
, OLESTREAM_MAX_STR_LEN
);
8413 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8419 /* Get the CLSID Default Name from the Registry */
8420 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8421 if(hErr
== ERROR_SUCCESS
)
8423 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8424 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8425 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8426 if(hErr
== ERROR_SUCCESS
)
8428 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8434 /* Write CompObj Structure to stream */
8435 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8437 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8439 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8440 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8442 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8444 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8445 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8447 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8449 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8450 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8452 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8454 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8455 IStream_Release(pStream
);
8461 /*************************************************************************
8462 * OLECONVERT_CreateOlePresStream[Internal]
8464 * Creates the "\002OlePres000" Stream with the Metafile data
8467 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8468 * dwExtentX [I] Width of the Metafile
8469 * dwExtentY [I] Height of the Metafile
8470 * pData [I] Metafile data
8471 * dwDataLength [I] Size of the Metafile data
8475 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8478 * This function is used by OleConvertOLESTREAMToIStorage only.
8481 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8485 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8486 BYTE pOlePresStreamHeader
[] =
8488 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8489 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8490 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8491 0x00, 0x00, 0x00, 0x00
8494 BYTE pOlePresStreamHeaderEmpty
[] =
8496 0x00, 0x00, 0x00, 0x00,
8497 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8498 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8499 0x00, 0x00, 0x00, 0x00
8502 /* Create the OlePres000 Stream */
8503 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8504 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8509 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8511 memset(&OlePres
, 0, sizeof(OlePres
));
8512 /* Do we have any metafile data to save */
8513 if(dwDataLength
> 0)
8515 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8516 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8520 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8521 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8523 /* Set width and height of the metafile */
8524 OlePres
.dwExtentX
= dwExtentX
;
8525 OlePres
.dwExtentY
= -dwExtentY
;
8527 /* Set Data and Length */
8528 if(dwDataLength
> sizeof(METAFILEPICT16
))
8530 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8531 OlePres
.pData
= &(pData
[8]);
8533 /* Save OlePres000 Data to Stream */
8534 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8535 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8536 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8537 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8538 if(OlePres
.dwSize
> 0)
8540 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8542 IStream_Release(pStream
);
8546 /*************************************************************************
8547 * OLECONVERT_CreateOle10NativeStream [Internal]
8549 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8552 * pStorage [I] Dest storage to create the stream in
8553 * pData [I] Ole10 Native Data (ex. bmp)
8554 * dwDataLength [I] Size of the Ole10 Native Data
8560 * This function is used by OleConvertOLESTREAMToIStorage only.
8562 * Might need to verify the data and return appropriate error message
8565 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8569 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8571 /* Create the Ole10Native Stream */
8572 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8573 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8577 /* Write info to stream */
8578 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8579 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8580 IStream_Release(pStream
);
8585 /*************************************************************************
8586 * OLECONVERT_GetOLE10ProgID [Internal]
8588 * Finds the ProgID (or OleTypeID) from the IStorage
8591 * pStorage [I] The Src IStorage to get the ProgID
8592 * strProgID [I] the ProgID string to get
8593 * dwSize [I] the size of the string
8597 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8600 * This function is used by OleConvertIStorageToOLESTREAM only.
8604 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8608 LARGE_INTEGER iSeekPos
;
8609 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8610 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8612 /* Open the CompObj Stream */
8613 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8614 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8618 /*Get the OleType from the CompObj Stream */
8619 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8620 iSeekPos
.u
.HighPart
= 0;
8622 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8623 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8624 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8625 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8626 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8627 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8628 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8630 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8633 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8635 IStream_Release(pStream
);
8640 LPOLESTR wstrProgID
;
8642 /* Get the OleType from the registry */
8643 REFCLSID clsid
= &(stat
.clsid
);
8644 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8645 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8648 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8655 /*************************************************************************
8656 * OLECONVERT_GetOle10PresData [Internal]
8658 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8661 * pStorage [I] Src IStroage
8662 * pOleStream [I] Dest OleStream Mem Struct
8668 * This function is used by OleConvertIStorageToOLESTREAM only.
8670 * Memory allocated for pData must be freed by the caller
8674 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8679 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8681 /* Initialize Default data for OLESTREAM */
8682 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8683 pOleStreamData
[0].dwTypeID
= 2;
8684 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8685 pOleStreamData
[1].dwTypeID
= 0;
8686 pOleStreamData
[0].dwMetaFileWidth
= 0;
8687 pOleStreamData
[0].dwMetaFileHeight
= 0;
8688 pOleStreamData
[0].pData
= NULL
;
8689 pOleStreamData
[1].pData
= NULL
;
8691 /* Open Ole10Native Stream */
8692 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8693 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8697 /* Read Size and Data */
8698 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8699 if(pOleStreamData
->dwDataLength
> 0)
8701 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8702 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8704 IStream_Release(pStream
);
8710 /*************************************************************************
8711 * OLECONVERT_GetOle20PresData[Internal]
8713 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8716 * pStorage [I] Src IStroage
8717 * pOleStreamData [I] Dest OleStream Mem Struct
8723 * This function is used by OleConvertIStorageToOLESTREAM only.
8725 * Memory allocated for pData must be freed by the caller
8727 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8731 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8732 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8734 /* Initialize Default data for OLESTREAM */
8735 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8736 pOleStreamData
[0].dwTypeID
= 2;
8737 pOleStreamData
[0].dwMetaFileWidth
= 0;
8738 pOleStreamData
[0].dwMetaFileHeight
= 0;
8739 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8740 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8741 pOleStreamData
[1].dwTypeID
= 0;
8742 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8743 pOleStreamData
[1].strOleTypeName
[0] = 0;
8744 pOleStreamData
[1].dwMetaFileWidth
= 0;
8745 pOleStreamData
[1].dwMetaFileHeight
= 0;
8746 pOleStreamData
[1].pData
= NULL
;
8747 pOleStreamData
[1].dwDataLength
= 0;
8750 /* Open OlePress000 stream */
8751 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8752 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8755 LARGE_INTEGER iSeekPos
;
8756 METAFILEPICT16 MetaFilePict
;
8757 static const char strMetafilePictName
[] = "METAFILEPICT";
8759 /* Set the TypeID for a Metafile */
8760 pOleStreamData
[1].dwTypeID
= 5;
8762 /* Set the OleTypeName to Metafile */
8763 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8764 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8766 iSeekPos
.u
.HighPart
= 0;
8767 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8769 /* Get Presentation Data */
8770 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8771 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8772 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8773 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8775 /*Set width and Height */
8776 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8777 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8778 if(olePress
.dwSize
> 0)
8781 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8783 /* Set MetaFilePict struct */
8784 MetaFilePict
.mm
= 8;
8785 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8786 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8787 MetaFilePict
.hMF
= 0;
8789 /* Get Metafile Data */
8790 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8791 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8792 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8794 IStream_Release(pStream
);
8798 /*************************************************************************
8799 * OleConvertOLESTREAMToIStorage [OLE32.@]
8804 * DVTARGETDEVICE parameter is not handled
8805 * Still unsure of some mem fields for OLE 10 Stream
8806 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8807 * and "\001OLE" streams
8810 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8811 LPOLESTREAM pOleStream
,
8813 const DVTARGETDEVICE
* ptd
)
8817 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8819 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8821 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8825 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8828 if(pstg
== NULL
|| pOleStream
== NULL
)
8830 hRes
= E_INVALIDARG
;
8835 /* Load the OLESTREAM to Memory */
8836 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8841 /* Load the OLESTREAM to Memory (part 2)*/
8842 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8848 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8850 /* Do we have the IStorage Data in the OLESTREAM */
8851 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8853 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8854 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8858 /* It must be an original OLE 1.0 source */
8859 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8864 /* It must be an original OLE 1.0 source */
8865 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8868 /* Create CompObj Stream if necessary */
8869 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8872 /*Create the Ole Stream if necessary */
8873 OLECONVERT_CreateOleStream(pstg
);
8878 /* Free allocated memory */
8879 for(i
=0; i
< 2; i
++)
8881 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8882 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8883 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8888 /*************************************************************************
8889 * OleConvertIStorageToOLESTREAM [OLE32.@]
8896 * Still unsure of some mem fields for OLE 10 Stream
8897 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8898 * and "\001OLE" streams.
8901 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8903 LPOLESTREAM pOleStream
)
8906 HRESULT hRes
= S_OK
;
8908 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8909 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8911 TRACE("%p %p\n", pstg
, pOleStream
);
8913 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8915 if(pstg
== NULL
|| pOleStream
== NULL
)
8917 hRes
= E_INVALIDARG
;
8921 /* Get the ProgID */
8922 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8923 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8927 /* Was it originally Ole10 */
8928 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8931 IStream_Release(pStream
);
8932 /* Get Presentation Data for Ole10Native */
8933 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8937 /* Get Presentation Data (OLE20) */
8938 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8941 /* Save OLESTREAM */
8942 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8945 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8950 /* Free allocated memory */
8951 for(i
=0; i
< 2; i
++)
8953 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8959 /***********************************************************************
8960 * GetConvertStg (OLE32.@)
8962 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8963 FIXME("unimplemented stub!\n");
8967 /******************************************************************************
8968 * StgIsStorageFile [OLE32.@]
8969 * Verify if the file contains a storage object
8975 * S_OK if file has magic bytes as a storage object
8976 * S_FALSE if file is not storage
8979 StgIsStorageFile(LPCOLESTR fn
)
8985 TRACE("%s\n", debugstr_w(fn
));
8986 hf
= CreateFileW(fn
, GENERIC_READ
,
8987 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8988 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8990 if (hf
== INVALID_HANDLE_VALUE
)
8991 return STG_E_FILENOTFOUND
;
8993 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8995 WARN(" unable to read file\n");
9002 if (bytes_read
!= 8) {
9003 TRACE(" too short\n");
9007 if (!memcmp(magic
,STORAGE_magic
,8)) {
9012 TRACE(" -> Invalid header.\n");
9016 /***********************************************************************
9017 * WriteClassStm (OLE32.@)
9019 * Writes a CLSID to a stream.
9022 * pStm [I] Stream to write to.
9023 * rclsid [I] CLSID to write.
9027 * Failure: HRESULT code.
9029 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9031 TRACE("(%p,%p)\n",pStm
,rclsid
);
9033 if (!pStm
|| !rclsid
)
9034 return E_INVALIDARG
;
9036 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9039 /***********************************************************************
9040 * ReadClassStm (OLE32.@)
9042 * Reads a CLSID from a stream.
9045 * pStm [I] Stream to read from.
9046 * rclsid [O] CLSID to read.
9050 * Failure: HRESULT code.
9052 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9057 TRACE("(%p,%p)\n",pStm
,pclsid
);
9059 if (!pStm
|| !pclsid
)
9060 return E_INVALIDARG
;
9062 /* clear the output args */
9063 *pclsid
= CLSID_NULL
;
9065 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9070 if (nbByte
!= sizeof(CLSID
))
9071 return STG_E_READFAULT
;