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 void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2358 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2360 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2362 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2363 This
->blockChainCache
[i
] = NULL
;
2369 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2370 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2372 StorageImpl
*This
= (StorageImpl
*)base
;
2377 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2378 if (FAILED(hr
)) return hr
;
2380 if (data
.size
.QuadPart
== 0)
2386 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2388 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2395 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2397 SmallBlockChainStream
*stream
;
2399 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2400 if (!stream
) return E_OUTOFMEMORY
;
2402 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2404 SmallBlockChainStream_Destroy(stream
);
2410 BlockChainStream
*stream
= NULL
;
2412 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2413 if (!stream
) return E_OUTOFMEMORY
;
2415 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2421 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2422 ULARGE_INTEGER newsize
)
2424 StorageImpl
*This
= (StorageImpl
*)base
;
2427 SmallBlockChainStream
*smallblock
=NULL
;
2428 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2430 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2431 if (FAILED(hr
)) return hr
;
2433 /* In simple mode keep the stream size above the small block limit */
2434 if (This
->base
.openFlags
& STGM_SIMPLE
)
2435 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2437 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2440 /* Create a block chain object of the appropriate type */
2441 if (data
.size
.QuadPart
== 0)
2443 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2445 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2446 if (!smallblock
) return E_OUTOFMEMORY
;
2450 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2451 bigblock
= *pbigblock
;
2452 if (!bigblock
) return E_OUTOFMEMORY
;
2455 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2457 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2458 if (!smallblock
) return E_OUTOFMEMORY
;
2462 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2463 bigblock
= *pbigblock
;
2464 if (!bigblock
) return E_OUTOFMEMORY
;
2467 /* Change the block chain type if necessary. */
2468 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2470 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2473 SmallBlockChainStream_Destroy(smallblock
);
2477 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2478 *pbigblock
= bigblock
;
2480 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2482 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2487 /* Set the size of the block chain. */
2490 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2491 SmallBlockChainStream_Destroy(smallblock
);
2495 BlockChainStream_SetSize(bigblock
, newsize
);
2498 /* Set the size in the directory entry. */
2499 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2502 data
.size
= newsize
;
2504 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2509 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2510 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2512 StorageImpl
*This
= (StorageImpl
*)base
;
2515 ULARGE_INTEGER newSize
;
2517 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2518 if (FAILED(hr
)) return hr
;
2520 /* Grow the stream if necessary */
2521 newSize
.QuadPart
= 0;
2522 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2524 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2526 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2530 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2531 if (FAILED(hr
)) return hr
;
2534 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2536 SmallBlockChainStream
*stream
;
2538 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2539 if (!stream
) return E_OUTOFMEMORY
;
2541 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2543 SmallBlockChainStream_Destroy(stream
);
2549 BlockChainStream
*stream
;
2551 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2552 if (!stream
) return E_OUTOFMEMORY
;
2554 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2560 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2563 StorageImpl
*This
= (StorageImpl
*)base
;
2564 DirEntry dst_data
, src_data
;
2567 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2570 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2574 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2575 dst_data
.startingBlock
= src_data
.startingBlock
;
2576 dst_data
.size
= src_data
.size
;
2578 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2585 * Virtual function table for the IStorage32Impl class.
2587 static const IStorageVtbl Storage32Impl_Vtbl
=
2589 StorageBaseImpl_QueryInterface
,
2590 StorageBaseImpl_AddRef
,
2591 StorageBaseImpl_Release
,
2592 StorageBaseImpl_CreateStream
,
2593 StorageBaseImpl_OpenStream
,
2594 StorageBaseImpl_CreateStorage
,
2595 StorageBaseImpl_OpenStorage
,
2596 StorageBaseImpl_CopyTo
,
2597 StorageBaseImpl_MoveElementTo
,
2600 StorageBaseImpl_EnumElements
,
2601 StorageBaseImpl_DestroyElement
,
2602 StorageBaseImpl_RenameElement
,
2603 StorageBaseImpl_SetElementTimes
,
2604 StorageBaseImpl_SetClass
,
2605 StorageBaseImpl_SetStateBits
,
2606 StorageBaseImpl_Stat
2609 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2611 StorageImpl_Destroy
,
2612 StorageImpl_Invalidate
,
2613 StorageImpl_CreateDirEntry
,
2614 StorageImpl_BaseWriteDirEntry
,
2615 StorageImpl_BaseReadDirEntry
,
2616 StorageImpl_DestroyDirEntry
,
2617 StorageImpl_StreamReadAt
,
2618 StorageImpl_StreamWriteAt
,
2619 StorageImpl_StreamSetSize
,
2620 StorageImpl_StreamLink
2623 static HRESULT
StorageImpl_Construct(
2631 StorageImpl
** result
)
2635 DirEntry currentEntry
;
2636 DirRef currentEntryRef
;
2637 WCHAR fullpath
[MAX_PATH
];
2639 if ( FAILED( validateSTGM(openFlags
) ))
2640 return STG_E_INVALIDFLAG
;
2642 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2644 return E_OUTOFMEMORY
;
2646 memset(This
, 0, sizeof(StorageImpl
));
2648 list_init(&This
->base
.strmHead
);
2650 list_init(&This
->base
.storageHead
);
2652 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2653 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2654 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2655 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2657 This
->base
.create
= create
;
2659 This
->base
.reverted
= 0;
2661 This
->hFile
= hFile
;
2664 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2666 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2668 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2669 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2670 if (!This
->pwcsName
)
2672 hr
= STG_E_INSUFFICIENTMEMORY
;
2675 strcpyW(This
->pwcsName
, fullpath
);
2676 This
->base
.filename
= This
->pwcsName
;
2680 * Initialize the big block cache.
2682 This
->bigBlockSize
= sector_size
;
2683 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2684 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2689 if (This
->bigBlockFile
== 0)
2697 ULARGE_INTEGER size
;
2698 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2701 * Initialize all header variables:
2702 * - The big block depot consists of one block and it is at block 0
2703 * - The directory table starts at block 1
2704 * - There is no small block depot
2706 memset( This
->bigBlockDepotStart
,
2708 sizeof(This
->bigBlockDepotStart
));
2710 This
->bigBlockDepotCount
= 1;
2711 This
->bigBlockDepotStart
[0] = 0;
2712 This
->rootStartBlock
= 1;
2713 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2714 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2715 if (sector_size
== 4096)
2716 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2718 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2719 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2720 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2721 This
->extBigBlockDepotCount
= 0;
2723 StorageImpl_SaveFileHeader(This
);
2726 * Add one block for the big block depot and one block for the directory table
2728 size
.u
.HighPart
= 0;
2729 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2730 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2733 * Initialize the big block depot
2735 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2736 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2737 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2738 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2743 * Load the header for the file.
2745 hr
= StorageImpl_LoadFileHeader(This
);
2754 * There is no block depot cached yet.
2756 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2759 * Start searching for free blocks with block 0.
2761 This
->prevFreeBlock
= 0;
2763 This
->firstFreeSmallBlock
= 0;
2766 * Create the block chain abstractions.
2768 if(!(This
->rootBlockChain
=
2769 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2771 hr
= STG_E_READFAULT
;
2775 if(!(This
->smallBlockDepotChain
=
2776 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2779 hr
= STG_E_READFAULT
;
2784 * Write the root storage entry (memory only)
2790 * Initialize the directory table
2792 memset(&rootEntry
, 0, sizeof(rootEntry
));
2793 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2794 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2795 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2796 rootEntry
.stgType
= STGTY_ROOT
;
2797 rootEntry
.leftChild
= DIRENTRY_NULL
;
2798 rootEntry
.rightChild
= DIRENTRY_NULL
;
2799 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2800 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2801 rootEntry
.size
.u
.HighPart
= 0;
2802 rootEntry
.size
.u
.LowPart
= 0;
2804 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2808 * Find the ID of the root storage.
2810 currentEntryRef
= 0;
2814 hr
= StorageImpl_ReadDirEntry(
2821 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2822 (currentEntry
.stgType
== STGTY_ROOT
) )
2824 This
->base
.storageDirEntry
= currentEntryRef
;
2830 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2834 hr
= STG_E_READFAULT
;
2839 * Create the block chain abstraction for the small block root chain.
2841 if(!(This
->smallBlockRootChain
=
2842 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2844 hr
= STG_E_READFAULT
;
2850 IStorage_Release((IStorage
*)This
);
2859 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2861 StorageImpl
*This
= (StorageImpl
*) iface
;
2863 StorageBaseImpl_DeleteAll(&This
->base
);
2865 This
->base
.reverted
= 1;
2868 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2870 StorageImpl
*This
= (StorageImpl
*) iface
;
2872 TRACE("(%p)\n", This
);
2874 StorageImpl_Invalidate(iface
);
2876 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2878 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2879 BlockChainStream_Destroy(This
->rootBlockChain
);
2880 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2882 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2883 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2885 if (This
->bigBlockFile
)
2886 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2887 HeapFree(GetProcessHeap(), 0, This
);
2890 /******************************************************************************
2891 * Storage32Impl_GetNextFreeBigBlock
2893 * Returns the index of the next free big block.
2894 * If the big block depot is filled, this method will enlarge it.
2897 static ULONG
StorageImpl_GetNextFreeBigBlock(
2900 ULONG depotBlockIndexPos
;
2901 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2903 ULONG depotBlockOffset
;
2904 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2905 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2907 ULONG freeBlock
= BLOCK_UNUSED
;
2908 ULARGE_INTEGER neededSize
;
2910 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2911 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2914 * Scan the entire big block depot until we find a block marked free
2916 while (nextBlockIndex
!= BLOCK_UNUSED
)
2918 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2920 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2923 * Grow the primary depot.
2925 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2927 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2930 * Add a block depot.
2932 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2933 This
->bigBlockDepotCount
++;
2934 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2937 * Flag it as a block depot.
2939 StorageImpl_SetNextBlockInChain(This
,
2943 /* Save new header information.
2945 StorageImpl_SaveFileHeader(This
);
2950 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2952 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2955 * Grow the extended depot.
2957 ULONG extIndex
= BLOCK_UNUSED
;
2958 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2959 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2961 if (extBlockOffset
== 0)
2963 /* We need an extended block.
2965 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2966 This
->extBigBlockDepotCount
++;
2967 depotBlockIndexPos
= extIndex
+ 1;
2970 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2973 * Add a block depot and mark it in the extended block.
2975 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2976 This
->bigBlockDepotCount
++;
2977 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2979 /* Flag the block depot.
2981 StorageImpl_SetNextBlockInChain(This
,
2985 /* If necessary, flag the extended depot block.
2987 if (extIndex
!= BLOCK_UNUSED
)
2988 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2990 /* Save header information.
2992 StorageImpl_SaveFileHeader(This
);
2996 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3000 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3001 ( nextBlockIndex
!= BLOCK_UNUSED
))
3003 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3005 if (nextBlockIndex
== BLOCK_UNUSED
)
3007 freeBlock
= (depotIndex
* blocksPerDepot
) +
3008 (depotBlockOffset
/sizeof(ULONG
));
3011 depotBlockOffset
+= sizeof(ULONG
);
3016 depotBlockOffset
= 0;
3020 * make sure that the block physically exists before using it
3022 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3023 BIGBLOCKFILE_Expand(This
->bigBlockFile
, neededSize
);
3025 This
->prevFreeBlock
= freeBlock
;
3030 /******************************************************************************
3031 * Storage32Impl_AddBlockDepot
3033 * This will create a depot block, essentially it is a block initialized
3036 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3038 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3041 * Initialize blocks as free
3043 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3044 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3047 /******************************************************************************
3048 * Storage32Impl_GetExtDepotBlock
3050 * Returns the index of the block that corresponds to the specified depot
3051 * index. This method is only for depot indexes equal or greater than
3052 * COUNT_BBDEPOTINHEADER.
3054 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3056 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3057 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3058 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3059 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3060 ULONG blockIndex
= BLOCK_UNUSED
;
3061 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3063 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3065 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3066 return BLOCK_UNUSED
;
3068 while (extBlockCount
> 0)
3070 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3074 if (extBlockIndex
!= BLOCK_UNUSED
)
3075 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3076 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3081 /******************************************************************************
3082 * Storage32Impl_SetExtDepotBlock
3084 * Associates the specified block index to the specified depot index.
3085 * This method is only for depot indexes equal or greater than
3086 * COUNT_BBDEPOTINHEADER.
3088 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3090 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3091 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3092 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3093 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3094 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3096 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3098 while (extBlockCount
> 0)
3100 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3104 if (extBlockIndex
!= BLOCK_UNUSED
)
3106 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3107 extBlockOffset
* sizeof(ULONG
),
3112 /******************************************************************************
3113 * Storage32Impl_AddExtBlockDepot
3115 * Creates an extended depot block.
3117 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3119 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3120 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3121 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3122 ULONG index
= BLOCK_UNUSED
;
3123 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3124 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3125 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3127 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3128 blocksPerDepotBlock
;
3130 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3133 * The first extended block.
3135 This
->extBigBlockDepotStart
= index
;
3141 * Follow the chain to the last one.
3143 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3145 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3149 * Add the new extended block to the chain.
3151 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3156 * Initialize this block.
3158 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3159 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3164 /******************************************************************************
3165 * Storage32Impl_FreeBigBlock
3167 * This method will flag the specified block as free in the big block depot.
3169 static void StorageImpl_FreeBigBlock(
3173 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3175 if (blockIndex
< This
->prevFreeBlock
)
3176 This
->prevFreeBlock
= blockIndex
;
3179 /************************************************************************
3180 * Storage32Impl_GetNextBlockInChain
3182 * This method will retrieve the block index of the next big block in
3185 * Params: This - Pointer to the Storage object.
3186 * blockIndex - Index of the block to retrieve the chain
3188 * nextBlockIndex - receives the return value.
3190 * Returns: This method returns the index of the next block in the chain.
3191 * It will return the constants:
3192 * BLOCK_SPECIAL - If the block given was not part of a
3194 * BLOCK_END_OF_CHAIN - If the block given was the last in
3196 * BLOCK_UNUSED - If the block given was not past of a chain
3198 * BLOCK_EXTBBDEPOT - This block is part of the extended
3201 * See Windows documentation for more details on IStorage methods.
3203 static HRESULT
StorageImpl_GetNextBlockInChain(
3206 ULONG
* nextBlockIndex
)
3208 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3209 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3210 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3211 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3213 ULONG depotBlockIndexPos
;
3214 int index
, num_blocks
;
3216 *nextBlockIndex
= BLOCK_SPECIAL
;
3218 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3220 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3221 This
->bigBlockDepotCount
);
3222 return STG_E_READFAULT
;
3226 * Cache the currently accessed depot block.
3228 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3230 This
->indexBlockDepotCached
= depotBlockCount
;
3232 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3234 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3239 * We have to look in the extended depot.
3241 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3244 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3247 return STG_E_READFAULT
;
3249 num_blocks
= This
->bigBlockSize
/ 4;
3251 for (index
= 0; index
< num_blocks
; index
++)
3253 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3254 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3258 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3263 /******************************************************************************
3264 * Storage32Impl_GetNextExtendedBlock
3266 * Given an extended block this method will return the next extended block.
3269 * The last ULONG of an extended block is the block index of the next
3270 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3274 * - The index of the next extended block
3275 * - BLOCK_UNUSED: there is no next extended block.
3276 * - Any other return values denotes failure.
3278 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3280 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3281 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3283 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3286 return nextBlockIndex
;
3289 /******************************************************************************
3290 * Storage32Impl_SetNextBlockInChain
3292 * This method will write the index of the specified block's next block
3293 * in the big block depot.
3295 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3298 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3299 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3300 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3303 static void StorageImpl_SetNextBlockInChain(
3308 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3309 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3310 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3311 ULONG depotBlockIndexPos
;
3313 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3314 assert(blockIndex
!= nextBlock
);
3316 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3318 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3323 * We have to look in the extended depot.
3325 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3328 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3331 * Update the cached block depot, if necessary.
3333 if (depotBlockCount
== This
->indexBlockDepotCached
)
3335 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3339 /******************************************************************************
3340 * Storage32Impl_LoadFileHeader
3342 * This method will read in the file header
3344 static HRESULT
StorageImpl_LoadFileHeader(
3348 BYTE headerBigBlock
[HEADER_SIZE
];
3350 ULARGE_INTEGER offset
;
3355 * Get a pointer to the big block of data containing the header.
3357 offset
.u
.HighPart
= 0;
3358 offset
.u
.LowPart
= 0;
3359 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3360 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3361 hr
= STG_E_FILENOTFOUND
;
3364 * Extract the information from the header.
3369 * Check for the "magic number" signature and return an error if it is not
3372 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3374 return STG_E_OLDFORMAT
;
3377 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3379 return STG_E_INVALIDHEADER
;
3382 StorageUtl_ReadWord(
3384 OFFSET_BIGBLOCKSIZEBITS
,
3385 &This
->bigBlockSizeBits
);
3387 StorageUtl_ReadWord(
3389 OFFSET_SMALLBLOCKSIZEBITS
,
3390 &This
->smallBlockSizeBits
);
3392 StorageUtl_ReadDWord(
3394 OFFSET_BBDEPOTCOUNT
,
3395 &This
->bigBlockDepotCount
);
3397 StorageUtl_ReadDWord(
3399 OFFSET_ROOTSTARTBLOCK
,
3400 &This
->rootStartBlock
);
3402 StorageUtl_ReadDWord(
3404 OFFSET_SMALLBLOCKLIMIT
,
3405 &This
->smallBlockLimit
);
3407 StorageUtl_ReadDWord(
3409 OFFSET_SBDEPOTSTART
,
3410 &This
->smallBlockDepotStart
);
3412 StorageUtl_ReadDWord(
3414 OFFSET_EXTBBDEPOTSTART
,
3415 &This
->extBigBlockDepotStart
);
3417 StorageUtl_ReadDWord(
3419 OFFSET_EXTBBDEPOTCOUNT
,
3420 &This
->extBigBlockDepotCount
);
3422 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3424 StorageUtl_ReadDWord(
3426 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3427 &(This
->bigBlockDepotStart
[index
]));
3431 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3433 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3434 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3437 * Right now, the code is making some assumptions about the size of the
3438 * blocks, just make sure they are what we're expecting.
3440 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3441 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3442 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3444 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3445 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3446 hr
= STG_E_INVALIDHEADER
;
3455 /******************************************************************************
3456 * Storage32Impl_SaveFileHeader
3458 * This method will save to the file the header
3460 static void StorageImpl_SaveFileHeader(
3463 BYTE headerBigBlock
[HEADER_SIZE
];
3466 ULARGE_INTEGER offset
;
3467 DWORD bytes_read
, bytes_written
;
3470 * Get a pointer to the big block of data containing the header.
3472 offset
.u
.HighPart
= 0;
3473 offset
.u
.LowPart
= 0;
3474 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3475 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3476 hr
= STG_E_FILENOTFOUND
;
3479 * If the block read failed, the file is probably new.
3484 * Initialize for all unknown fields.
3486 memset(headerBigBlock
, 0, HEADER_SIZE
);
3489 * Initialize the magic number.
3491 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3494 * And a bunch of things we don't know what they mean
3496 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3497 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3498 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3502 * Write the information to the header.
3504 StorageUtl_WriteWord(
3506 OFFSET_BIGBLOCKSIZEBITS
,
3507 This
->bigBlockSizeBits
);
3509 StorageUtl_WriteWord(
3511 OFFSET_SMALLBLOCKSIZEBITS
,
3512 This
->smallBlockSizeBits
);
3514 StorageUtl_WriteDWord(
3516 OFFSET_BBDEPOTCOUNT
,
3517 This
->bigBlockDepotCount
);
3519 StorageUtl_WriteDWord(
3521 OFFSET_ROOTSTARTBLOCK
,
3522 This
->rootStartBlock
);
3524 StorageUtl_WriteDWord(
3526 OFFSET_SMALLBLOCKLIMIT
,
3527 This
->smallBlockLimit
);
3529 StorageUtl_WriteDWord(
3531 OFFSET_SBDEPOTSTART
,
3532 This
->smallBlockDepotStart
);
3534 StorageUtl_WriteDWord(
3536 OFFSET_SBDEPOTCOUNT
,
3537 This
->smallBlockDepotChain
?
3538 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3540 StorageUtl_WriteDWord(
3542 OFFSET_EXTBBDEPOTSTART
,
3543 This
->extBigBlockDepotStart
);
3545 StorageUtl_WriteDWord(
3547 OFFSET_EXTBBDEPOTCOUNT
,
3548 This
->extBigBlockDepotCount
);
3550 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3552 StorageUtl_WriteDWord(
3554 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3555 (This
->bigBlockDepotStart
[index
]));
3559 * Write the big block back to the file.
3561 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3564 /******************************************************************************
3565 * StorageImpl_ReadRawDirEntry
3567 * This method will read the raw data from a directory entry in the file.
3569 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3571 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3573 ULARGE_INTEGER offset
;
3577 offset
.u
.HighPart
= 0;
3578 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3580 hr
= BlockChainStream_ReadAt(
3581 This
->rootBlockChain
,
3587 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3588 return STG_E_READFAULT
;
3593 /******************************************************************************
3594 * StorageImpl_WriteRawDirEntry
3596 * This method will write the raw data from a directory entry in the file.
3598 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3600 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3602 ULARGE_INTEGER offset
;
3606 offset
.u
.HighPart
= 0;
3607 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3609 hr
= BlockChainStream_WriteAt(
3610 This
->rootBlockChain
,
3619 /******************************************************************************
3622 * Update raw directory entry data from the fields in newData.
3624 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3626 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3628 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3631 buffer
+ OFFSET_PS_NAME
,
3633 DIRENTRY_NAME_BUFFER_LEN
);
3635 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3637 StorageUtl_WriteWord(
3639 OFFSET_PS_NAMELENGTH
,
3640 newData
->sizeOfNameString
);
3642 StorageUtl_WriteDWord(
3644 OFFSET_PS_LEFTCHILD
,
3645 newData
->leftChild
);
3647 StorageUtl_WriteDWord(
3649 OFFSET_PS_RIGHTCHILD
,
3650 newData
->rightChild
);
3652 StorageUtl_WriteDWord(
3655 newData
->dirRootEntry
);
3657 StorageUtl_WriteGUID(
3662 StorageUtl_WriteDWord(
3665 newData
->ctime
.dwLowDateTime
);
3667 StorageUtl_WriteDWord(
3669 OFFSET_PS_CTIMEHIGH
,
3670 newData
->ctime
.dwHighDateTime
);
3672 StorageUtl_WriteDWord(
3675 newData
->mtime
.dwLowDateTime
);
3677 StorageUtl_WriteDWord(
3679 OFFSET_PS_MTIMEHIGH
,
3680 newData
->ctime
.dwHighDateTime
);
3682 StorageUtl_WriteDWord(
3684 OFFSET_PS_STARTBLOCK
,
3685 newData
->startingBlock
);
3687 StorageUtl_WriteDWord(
3690 newData
->size
.u
.LowPart
);
3693 /******************************************************************************
3694 * Storage32Impl_ReadDirEntry
3696 * This method will read the specified directory entry.
3698 HRESULT
StorageImpl_ReadDirEntry(
3703 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3706 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3708 if (SUCCEEDED(readRes
))
3710 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3713 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3714 DIRENTRY_NAME_BUFFER_LEN
);
3715 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3717 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3719 StorageUtl_ReadWord(
3721 OFFSET_PS_NAMELENGTH
,
3722 &buffer
->sizeOfNameString
);
3724 StorageUtl_ReadDWord(
3726 OFFSET_PS_LEFTCHILD
,
3727 &buffer
->leftChild
);
3729 StorageUtl_ReadDWord(
3731 OFFSET_PS_RIGHTCHILD
,
3732 &buffer
->rightChild
);
3734 StorageUtl_ReadDWord(
3737 &buffer
->dirRootEntry
);
3739 StorageUtl_ReadGUID(
3744 StorageUtl_ReadDWord(
3747 &buffer
->ctime
.dwLowDateTime
);
3749 StorageUtl_ReadDWord(
3751 OFFSET_PS_CTIMEHIGH
,
3752 &buffer
->ctime
.dwHighDateTime
);
3754 StorageUtl_ReadDWord(
3757 &buffer
->mtime
.dwLowDateTime
);
3759 StorageUtl_ReadDWord(
3761 OFFSET_PS_MTIMEHIGH
,
3762 &buffer
->mtime
.dwHighDateTime
);
3764 StorageUtl_ReadDWord(
3766 OFFSET_PS_STARTBLOCK
,
3767 &buffer
->startingBlock
);
3769 StorageUtl_ReadDWord(
3772 &buffer
->size
.u
.LowPart
);
3774 buffer
->size
.u
.HighPart
= 0;
3780 /*********************************************************************
3781 * Write the specified directory entry to the file
3783 HRESULT
StorageImpl_WriteDirEntry(
3786 const DirEntry
* buffer
)
3788 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3791 UpdateRawDirEntry(currentEntry
, buffer
);
3793 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3797 static BOOL
StorageImpl_ReadBigBlock(
3802 ULARGE_INTEGER ulOffset
;
3805 ulOffset
.u
.HighPart
= 0;
3806 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3808 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3809 return (read
== This
->bigBlockSize
);
3812 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3818 ULARGE_INTEGER ulOffset
;
3822 ulOffset
.u
.HighPart
= 0;
3823 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3824 ulOffset
.u
.LowPart
+= offset
;
3826 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3827 *value
= lendian32toh(tmp
);
3828 return (read
== sizeof(DWORD
));
3831 static BOOL
StorageImpl_WriteBigBlock(
3836 ULARGE_INTEGER ulOffset
;
3839 ulOffset
.u
.HighPart
= 0;
3840 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3842 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3843 return (wrote
== This
->bigBlockSize
);
3846 static BOOL
StorageImpl_WriteDWordToBigBlock(
3852 ULARGE_INTEGER ulOffset
;
3855 ulOffset
.u
.HighPart
= 0;
3856 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3857 ulOffset
.u
.LowPart
+= offset
;
3859 value
= htole32(value
);
3860 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3861 return (wrote
== sizeof(DWORD
));
3864 /******************************************************************************
3865 * Storage32Impl_SmallBlocksToBigBlocks
3867 * This method will convert a small block chain to a big block chain.
3868 * The small block chain will be destroyed.
3870 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3872 SmallBlockChainStream
** ppsbChain
)
3874 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3875 ULARGE_INTEGER size
, offset
;
3876 ULONG cbRead
, cbWritten
;
3877 ULARGE_INTEGER cbTotalRead
;
3878 DirRef streamEntryRef
;
3879 HRESULT resWrite
= S_OK
;
3881 DirEntry streamEntry
;
3883 BlockChainStream
*bbTempChain
= NULL
;
3884 BlockChainStream
*bigBlockChain
= NULL
;
3887 * Create a temporary big block chain that doesn't have
3888 * an associated directory entry. This temporary chain will be
3889 * used to copy data from small blocks to big blocks.
3891 bbTempChain
= BlockChainStream_Construct(This
,
3894 if(!bbTempChain
) return NULL
;
3896 * Grow the big block chain.
3898 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3899 BlockChainStream_SetSize(bbTempChain
, size
);
3902 * Copy the contents of the small block chain to the big block chain
3903 * by small block size increments.
3905 offset
.u
.LowPart
= 0;
3906 offset
.u
.HighPart
= 0;
3907 cbTotalRead
.QuadPart
= 0;
3909 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3912 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3914 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3917 if (FAILED(resRead
))
3922 cbTotalRead
.QuadPart
+= cbRead
;
3924 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3930 if (FAILED(resWrite
))
3933 offset
.u
.LowPart
+= cbRead
;
3937 resRead
= STG_E_READFAULT
;
3940 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3941 HeapFree(GetProcessHeap(),0,buffer
);
3943 size
.u
.HighPart
= 0;
3946 if (FAILED(resRead
) || FAILED(resWrite
))
3948 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3949 BlockChainStream_SetSize(bbTempChain
, size
);
3950 BlockChainStream_Destroy(bbTempChain
);
3955 * Destroy the small block chain.
3957 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3958 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3959 SmallBlockChainStream_Destroy(*ppsbChain
);
3963 * Change the directory entry. This chain is now a big block chain
3964 * and it doesn't reside in the small blocks chain anymore.
3966 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3968 streamEntry
.startingBlock
= bbHeadOfChain
;
3970 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3973 * Destroy the temporary entryless big block chain.
3974 * Create a new big block chain associated with this entry.
3976 BlockChainStream_Destroy(bbTempChain
);
3977 bigBlockChain
= BlockChainStream_Construct(This
,
3981 return bigBlockChain
;
3984 /******************************************************************************
3985 * Storage32Impl_BigBlocksToSmallBlocks
3987 * This method will convert a big block chain to a small block chain.
3988 * The big block chain will be destroyed on success.
3990 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3992 BlockChainStream
** ppbbChain
)
3994 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3995 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3996 DirRef streamEntryRef
;
3997 HRESULT resWrite
= S_OK
, resRead
;
3998 DirEntry streamEntry
;
4000 SmallBlockChainStream
* sbTempChain
;
4002 TRACE("%p %p\n", This
, ppbbChain
);
4004 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4010 size
= BlockChainStream_GetSize(*ppbbChain
);
4011 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4013 offset
.u
.HighPart
= 0;
4014 offset
.u
.LowPart
= 0;
4015 cbTotalRead
.QuadPart
= 0;
4016 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4019 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4020 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4028 cbTotalRead
.QuadPart
+= cbRead
;
4030 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4031 cbRead
, buffer
, &cbWritten
);
4033 if(FAILED(resWrite
))
4036 offset
.u
.LowPart
+= cbRead
;
4040 resRead
= STG_E_READFAULT
;
4043 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
4044 HeapFree(GetProcessHeap(), 0, buffer
);
4046 size
.u
.HighPart
= 0;
4049 if(FAILED(resRead
) || FAILED(resWrite
))
4051 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4052 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4053 SmallBlockChainStream_Destroy(sbTempChain
);
4057 /* destroy the original big block chain */
4058 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4059 BlockChainStream_SetSize(*ppbbChain
, size
);
4060 BlockChainStream_Destroy(*ppbbChain
);
4063 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4064 streamEntry
.startingBlock
= sbHeadOfChain
;
4065 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4067 SmallBlockChainStream_Destroy(sbTempChain
);
4068 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4071 static HRESULT
StorageBaseImpl_CopyStream(
4072 StorageBaseImpl
*dst
, DirRef dst_entry
,
4073 StorageBaseImpl
*src
, DirRef src_entry
)
4078 ULARGE_INTEGER bytes_copied
;
4079 ULONG bytestocopy
, bytesread
, byteswritten
;
4081 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4085 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4087 bytes_copied
.QuadPart
= 0;
4088 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4090 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4092 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4094 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4097 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4098 data
, &byteswritten
);
4101 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4102 bytes_copied
.QuadPart
+= byteswritten
;
4110 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4112 DirRef result
=This
->firstFreeEntry
;
4114 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4117 if (result
== This
->entries_size
)
4119 ULONG new_size
= This
->entries_size
* 2;
4120 TransactedDirEntry
*new_entries
;
4122 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4123 if (!new_entries
) return DIRENTRY_NULL
;
4125 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4126 HeapFree(GetProcessHeap(), 0, This
->entries
);
4128 This
->entries
= new_entries
;
4129 This
->entries_size
= new_size
;
4132 This
->entries
[result
].inuse
= 1;
4134 This
->firstFreeEntry
= result
+1;
4139 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4140 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4142 DirRef stubEntryRef
;
4143 TransactedDirEntry
*entry
;
4145 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4147 if (stubEntryRef
!= DIRENTRY_NULL
)
4149 entry
= &This
->entries
[stubEntryRef
];
4151 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4156 return stubEntryRef
;
4159 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4160 TransactedSnapshotImpl
*This
, DirRef entry
)
4165 if (!This
->entries
[entry
].read
)
4167 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4168 This
->entries
[entry
].transactedParentEntry
,
4171 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4173 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4175 if (data
.leftChild
== DIRENTRY_NULL
)
4179 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4181 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4183 if (data
.rightChild
== DIRENTRY_NULL
)
4187 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4189 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4191 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4197 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4198 This
->entries
[entry
].read
= 1;
4205 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4206 TransactedSnapshotImpl
*This
, DirRef entry
)
4210 if (!This
->entries
[entry
].stream_dirty
)
4212 DirEntry new_entrydata
;
4214 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4215 new_entrydata
.name
[0] = 'S';
4216 new_entrydata
.sizeOfNameString
= 1;
4217 new_entrydata
.stgType
= STGTY_STREAM
;
4218 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4219 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4220 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4221 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4223 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4224 &This
->entries
[entry
].stream_entry
);
4226 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4228 hr
= StorageBaseImpl_CopyStream(
4229 This
->scratch
, This
->entries
[entry
].stream_entry
,
4230 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4233 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4237 This
->entries
[entry
].stream_dirty
= 1;
4239 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4241 /* Since this entry is modified, and we aren't using its stream data, we
4242 * no longer care about the original entry. */
4244 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4246 if (delete_ref
!= DIRENTRY_NULL
)
4247 This
->entries
[delete_ref
].deleted
= 1;
4249 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4256 /* Find the first entry in a depth-first traversal. */
4257 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4258 TransactedSnapshotImpl
* This
, DirRef parent
)
4260 DirRef cursor
, prev
;
4261 TransactedDirEntry
*entry
;
4264 entry
= &This
->entries
[cursor
];
4267 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4270 cursor
= entry
->data
.leftChild
;
4271 entry
= &This
->entries
[cursor
];
4272 entry
->parent
= prev
;
4274 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4277 cursor
= entry
->data
.rightChild
;
4278 entry
= &This
->entries
[cursor
];
4279 entry
->parent
= prev
;
4281 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4284 cursor
= entry
->data
.dirRootEntry
;
4285 entry
= &This
->entries
[cursor
];
4286 entry
->parent
= prev
;
4295 /* Find the next entry in a depth-first traversal. */
4296 static DirRef
TransactedSnapshotImpl_FindNextChild(
4297 TransactedSnapshotImpl
* This
, DirRef current
)
4300 TransactedDirEntry
*parent_entry
;
4302 parent
= This
->entries
[current
].parent
;
4303 parent_entry
= &This
->entries
[parent
];
4305 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4307 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4309 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4310 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4313 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4315 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4316 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4323 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4324 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4325 TransactedSnapshotImpl
* This
, DirRef entry
)
4327 return entry
!= DIRENTRY_NULL
&&
4328 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4331 /* Destroy the entries created by CopyTree. */
4332 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4333 TransactedSnapshotImpl
* This
, DirRef stop
)
4336 TransactedDirEntry
*entry
;
4337 ULARGE_INTEGER zero
;
4341 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4344 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4346 if (cursor
== DIRENTRY_NULL
)
4349 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4351 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4353 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4355 entry
= &This
->entries
[cursor
];
4357 if (entry
->stream_dirty
)
4358 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4359 entry
->newTransactedParentEntry
, zero
);
4361 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4362 entry
->newTransactedParentEntry
);
4364 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4367 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4371 /* Make a copy of our edited tree that we can use in the parent. */
4372 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4375 TransactedDirEntry
*entry
;
4378 cursor
= This
->base
.storageDirEntry
;
4379 entry
= &This
->entries
[cursor
];
4380 entry
->parent
= DIRENTRY_NULL
;
4381 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4383 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4386 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4388 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4389 entry
= &This
->entries
[cursor
];
4391 while (cursor
!= DIRENTRY_NULL
)
4393 /* Make a copy of this entry in the transacted parent. */
4395 (!entry
->dirty
&& !entry
->stream_dirty
&&
4396 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4397 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4398 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4399 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4404 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4406 newData
.size
.QuadPart
= 0;
4407 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4409 if (newData
.leftChild
!= DIRENTRY_NULL
)
4410 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4412 if (newData
.rightChild
!= DIRENTRY_NULL
)
4413 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4415 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4416 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4418 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4419 &entry
->newTransactedParentEntry
);
4422 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4426 if (entry
->stream_dirty
)
4428 hr
= StorageBaseImpl_CopyStream(
4429 This
->transactedParent
, entry
->newTransactedParentEntry
,
4430 This
->scratch
, entry
->stream_entry
);
4432 else if (entry
->data
.size
.QuadPart
)
4434 hr
= StorageBaseImpl_StreamLink(
4435 This
->transactedParent
, entry
->newTransactedParentEntry
,
4436 entry
->transactedParentEntry
);
4441 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4442 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4447 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4448 entry
= &This
->entries
[cursor
];
4454 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4456 DWORD grfCommitFlags
) /* [in] */
4458 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4459 TransactedDirEntry
*root_entry
;
4460 DirRef i
, dir_root_ref
;
4462 ULARGE_INTEGER zero
;
4467 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4469 /* Cannot commit a read-only transacted storage */
4470 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4471 return STG_E_ACCESSDENIED
;
4473 /* To prevent data loss, we create the new structure in the file before we
4474 * delete the old one, so that in case of errors the old data is intact. We
4475 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4476 * needed in the rare situation where we have just enough free disk space to
4477 * overwrite the existing data. */
4479 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4481 if (!root_entry
->read
)
4484 hr
= TransactedSnapshotImpl_CopyTree(This
);
4485 if (FAILED(hr
)) return hr
;
4487 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4488 dir_root_ref
= DIRENTRY_NULL
;
4490 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4492 /* Update the storage to use the new data in one step. */
4493 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4494 root_entry
->transactedParentEntry
, &data
);
4498 data
.dirRootEntry
= dir_root_ref
;
4499 data
.clsid
= root_entry
->data
.clsid
;
4500 data
.ctime
= root_entry
->data
.ctime
;
4501 data
.mtime
= root_entry
->data
.mtime
;
4503 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4504 root_entry
->transactedParentEntry
, &data
);
4509 /* Destroy the old now-orphaned data. */
4510 for (i
=0; i
<This
->entries_size
; i
++)
4512 TransactedDirEntry
*entry
= &This
->entries
[i
];
4517 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4518 entry
->transactedParentEntry
, zero
);
4519 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4520 entry
->transactedParentEntry
);
4521 memset(entry
, 0, sizeof(TransactedDirEntry
));
4522 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4524 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4526 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4527 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4528 entry
->transactedParentEntry
);
4529 if (entry
->stream_dirty
)
4531 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4532 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4533 entry
->stream_dirty
= 0;
4536 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4543 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4549 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4552 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4553 ULARGE_INTEGER zero
;
4556 TRACE("(%p)\n", iface
);
4558 /* Destroy the open objects. */
4559 StorageBaseImpl_DeleteAll(&This
->base
);
4561 /* Clear out the scratch file. */
4563 for (i
=0; i
<This
->entries_size
; i
++)
4565 if (This
->entries
[i
].stream_dirty
)
4567 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4570 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4574 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4576 This
->firstFreeEntry
= 0;
4577 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4582 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4584 if (!This
->reverted
)
4586 TRACE("Storage invalidated (stg=%p)\n", This
);
4590 StorageBaseImpl_DeleteAll(This
);
4594 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4596 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4598 TransactedSnapshotImpl_Revert((IStorage
*)iface
);
4600 IStorage_Release((IStorage
*)This
->transactedParent
);
4602 IStorage_Release((IStorage
*)This
->scratch
);
4604 HeapFree(GetProcessHeap(), 0, This
->entries
);
4606 HeapFree(GetProcessHeap(), 0, This
);
4609 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4610 const DirEntry
*newData
, DirRef
*index
)
4612 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4614 TransactedDirEntry
*new_entry
;
4616 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4617 if (new_ref
== DIRENTRY_NULL
)
4618 return E_OUTOFMEMORY
;
4620 new_entry
= &This
->entries
[new_ref
];
4622 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4623 new_entry
->read
= 1;
4624 new_entry
->dirty
= 1;
4625 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4629 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4634 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4635 DirRef index
, const DirEntry
*data
)
4637 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4640 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4642 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4643 if (FAILED(hr
)) return hr
;
4645 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4647 if (index
!= This
->base
.storageDirEntry
)
4649 This
->entries
[index
].dirty
= 1;
4651 if (data
->size
.QuadPart
== 0 &&
4652 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4654 /* Since this entry is modified, and we aren't using its stream data, we
4655 * no longer care about the original entry. */
4657 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4659 if (delete_ref
!= DIRENTRY_NULL
)
4660 This
->entries
[delete_ref
].deleted
= 1;
4662 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4669 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4670 DirRef index
, DirEntry
*data
)
4672 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4675 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4676 if (FAILED(hr
)) return hr
;
4678 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4680 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4685 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4688 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4690 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4691 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4693 /* If we deleted this entry while it has stream data. We must have left the
4694 * data because some other entry is using it, and we need to leave the
4695 * original entry alone. */
4696 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4697 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4701 This
->entries
[index
].deleted
= 1;
4707 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4708 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4710 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4712 if (This
->entries
[index
].stream_dirty
)
4714 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4715 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4717 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4719 /* This stream doesn't live in the parent, and we haven't allocated storage
4726 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4727 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4731 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4732 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4734 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4737 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4738 if (FAILED(hr
)) return hr
;
4740 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4741 if (FAILED(hr
)) return hr
;
4743 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4744 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4746 if (SUCCEEDED(hr
) && size
!= 0)
4747 This
->entries
[index
].data
.size
.QuadPart
= max(
4748 This
->entries
[index
].data
.size
.QuadPart
,
4749 offset
.QuadPart
+ size
);
4754 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4755 DirRef index
, ULARGE_INTEGER newsize
)
4757 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4760 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4761 if (FAILED(hr
)) return hr
;
4763 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4766 if (newsize
.QuadPart
== 0)
4768 /* Destroy any parent references or entries in the scratch file. */
4769 if (This
->entries
[index
].stream_dirty
)
4771 ULARGE_INTEGER zero
;
4773 StorageBaseImpl_StreamSetSize(This
->scratch
,
4774 This
->entries
[index
].stream_entry
, zero
);
4775 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4776 This
->entries
[index
].stream_entry
);
4777 This
->entries
[index
].stream_dirty
= 0;
4779 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4782 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4784 if (delete_ref
!= DIRENTRY_NULL
)
4785 This
->entries
[delete_ref
].deleted
= 1;
4787 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4792 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4793 if (FAILED(hr
)) return hr
;
4795 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
4796 This
->entries
[index
].stream_entry
, newsize
);
4800 This
->entries
[index
].data
.size
= newsize
;
4805 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
4806 DirRef dst
, DirRef src
)
4808 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4810 TransactedDirEntry
*dst_entry
, *src_entry
;
4812 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
4813 if (FAILED(hr
)) return hr
;
4815 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
4816 if (FAILED(hr
)) return hr
;
4818 dst_entry
= &This
->entries
[dst
];
4819 src_entry
= &This
->entries
[src
];
4821 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
4822 dst_entry
->stream_entry
= src_entry
->stream_entry
;
4823 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
4824 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
4825 dst_entry
->data
.size
= src_entry
->data
.size
;
4830 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4832 StorageBaseImpl_QueryInterface
,
4833 StorageBaseImpl_AddRef
,
4834 StorageBaseImpl_Release
,
4835 StorageBaseImpl_CreateStream
,
4836 StorageBaseImpl_OpenStream
,
4837 StorageBaseImpl_CreateStorage
,
4838 StorageBaseImpl_OpenStorage
,
4839 StorageBaseImpl_CopyTo
,
4840 StorageBaseImpl_MoveElementTo
,
4841 TransactedSnapshotImpl_Commit
,
4842 TransactedSnapshotImpl_Revert
,
4843 StorageBaseImpl_EnumElements
,
4844 StorageBaseImpl_DestroyElement
,
4845 StorageBaseImpl_RenameElement
,
4846 StorageBaseImpl_SetElementTimes
,
4847 StorageBaseImpl_SetClass
,
4848 StorageBaseImpl_SetStateBits
,
4849 StorageBaseImpl_Stat
4852 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4854 TransactedSnapshotImpl_Destroy
,
4855 TransactedSnapshotImpl_Invalidate
,
4856 TransactedSnapshotImpl_CreateDirEntry
,
4857 TransactedSnapshotImpl_WriteDirEntry
,
4858 TransactedSnapshotImpl_ReadDirEntry
,
4859 TransactedSnapshotImpl_DestroyDirEntry
,
4860 TransactedSnapshotImpl_StreamReadAt
,
4861 TransactedSnapshotImpl_StreamWriteAt
,
4862 TransactedSnapshotImpl_StreamSetSize
,
4863 TransactedSnapshotImpl_StreamLink
4866 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4867 TransactedSnapshotImpl
** result
)
4871 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4874 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4876 /* This is OK because the property set storage functions use the IStorage functions. */
4877 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4879 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4881 list_init(&(*result
)->base
.strmHead
);
4883 list_init(&(*result
)->base
.storageHead
);
4885 (*result
)->base
.ref
= 1;
4887 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4889 (*result
)->base
.filename
= parentStorage
->filename
;
4891 /* Create a new temporary storage to act as the scratch file. */
4892 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
,
4893 0, (IStorage
**)&(*result
)->scratch
);
4897 ULONG num_entries
= 20;
4899 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
4901 (*result
)->entries_size
= num_entries
;
4903 (*result
)->firstFreeEntry
= 0;
4905 if ((*result
)->entries
)
4907 /* parentStorage already has 1 reference, which we take over here. */
4908 (*result
)->transactedParent
= parentStorage
;
4910 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4912 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
4916 IStorage_Release((IStorage
*)(*result
)->scratch
);
4922 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4927 return E_OUTOFMEMORY
;
4930 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4931 StorageBaseImpl
** result
)
4935 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4937 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4940 return TransactedSnapshotImpl_Construct(parentStorage
,
4941 (TransactedSnapshotImpl
**)result
);
4944 static HRESULT
Storage_Construct(
4952 StorageBaseImpl
** result
)
4954 StorageImpl
*newStorage
;
4955 StorageBaseImpl
*newTransactedStorage
;
4958 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
4959 if (FAILED(hr
)) goto end
;
4961 if (openFlags
& STGM_TRANSACTED
)
4963 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4965 IStorage_Release((IStorage
*)newStorage
);
4967 *result
= newTransactedStorage
;
4970 *result
= &newStorage
->base
;
4976 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4978 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4980 if (!This
->base
.reverted
)
4982 TRACE("Storage invalidated (stg=%p)\n", This
);
4984 This
->base
.reverted
= 1;
4986 This
->parentStorage
= NULL
;
4988 StorageBaseImpl_DeleteAll(&This
->base
);
4990 list_remove(&This
->ParentListEntry
);
4994 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4996 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4998 StorageInternalImpl_Invalidate(&This
->base
);
5000 HeapFree(GetProcessHeap(), 0, This
);
5003 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5004 const DirEntry
*newData
, DirRef
*index
)
5006 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5008 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5012 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5013 DirRef index
, const DirEntry
*data
)
5015 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5017 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5021 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5022 DirRef index
, DirEntry
*data
)
5024 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5026 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5030 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5033 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5035 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5039 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5040 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5042 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5044 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5045 index
, offset
, size
, buffer
, bytesRead
);
5048 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5049 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5051 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5053 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5054 index
, offset
, size
, buffer
, bytesWritten
);
5057 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5058 DirRef index
, ULARGE_INTEGER newsize
)
5060 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5062 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5066 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5067 DirRef dst
, DirRef src
)
5069 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5071 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5075 /******************************************************************************
5077 ** Storage32InternalImpl_Commit
5080 static HRESULT WINAPI
StorageInternalImpl_Commit(
5082 DWORD grfCommitFlags
) /* [in] */
5084 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
5088 /******************************************************************************
5090 ** Storage32InternalImpl_Revert
5093 static HRESULT WINAPI
StorageInternalImpl_Revert(
5096 FIXME("(%p): stub\n", iface
);
5100 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5102 IStorage_Release((IStorage
*)This
->parentStorage
);
5103 HeapFree(GetProcessHeap(), 0, This
);
5106 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5107 IEnumSTATSTG
* iface
,
5111 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5114 return E_INVALIDARG
;
5118 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5119 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5122 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
5126 return E_NOINTERFACE
;
5129 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5130 IEnumSTATSTG
* iface
)
5132 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5133 return InterlockedIncrement(&This
->ref
);
5136 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5137 IEnumSTATSTG
* iface
)
5139 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5143 newRef
= InterlockedDecrement(&This
->ref
);
5147 IEnumSTATSTGImpl_Destroy(This
);
5153 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5154 IEnumSTATSTGImpl
* This
,
5157 DirRef result
= DIRENTRY_NULL
;
5161 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5163 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5164 This
->parentStorage
->storageDirEntry
, &entry
);
5165 searchNode
= entry
.dirRootEntry
;
5167 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5169 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5173 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5177 searchNode
= entry
.rightChild
;
5181 result
= searchNode
;
5182 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5183 searchNode
= entry
.leftChild
;
5191 if (result
!= DIRENTRY_NULL
)
5192 memcpy(This
->name
, result_name
, sizeof(result_name
));
5198 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5199 IEnumSTATSTG
* iface
,
5202 ULONG
* pceltFetched
)
5204 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5206 DirEntry currentEntry
;
5207 STATSTG
* currentReturnStruct
= rgelt
;
5208 ULONG objectFetched
= 0;
5209 DirRef currentSearchNode
;
5212 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5213 return E_INVALIDARG
;
5215 if (This
->parentStorage
->reverted
)
5216 return STG_E_REVERTED
;
5219 * To avoid the special case, get another pointer to a ULONG value if
5220 * the caller didn't supply one.
5222 if (pceltFetched
==0)
5223 pceltFetched
= &objectFetched
;
5226 * Start the iteration, we will iterate until we hit the end of the
5227 * linked list or until we hit the number of items to iterate through
5231 while ( *pceltFetched
< celt
)
5233 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5235 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5239 * Read the entry from the storage.
5241 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5246 * Copy the information to the return buffer.
5248 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5249 currentReturnStruct
,
5254 * Step to the next item in the iteration
5257 currentReturnStruct
++;
5260 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5267 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5268 IEnumSTATSTG
* iface
,
5271 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5273 ULONG objectFetched
= 0;
5274 DirRef currentSearchNode
;
5277 if (This
->parentStorage
->reverted
)
5278 return STG_E_REVERTED
;
5280 while ( (objectFetched
< celt
) )
5282 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5284 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5290 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5296 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5297 IEnumSTATSTG
* iface
)
5299 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5301 if (This
->parentStorage
->reverted
)
5302 return STG_E_REVERTED
;
5309 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5310 IEnumSTATSTG
* iface
,
5311 IEnumSTATSTG
** ppenum
)
5313 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5315 IEnumSTATSTGImpl
* newClone
;
5317 if (This
->parentStorage
->reverted
)
5318 return STG_E_REVERTED
;
5321 * Perform a sanity check on the parameters.
5324 return E_INVALIDARG
;
5326 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5327 This
->storageDirEntry
);
5331 * The new clone enumeration must point to the same current node as
5334 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5336 *ppenum
= (IEnumSTATSTG
*)newClone
;
5339 * Don't forget to nail down a reference to the clone before
5342 IEnumSTATSTGImpl_AddRef(*ppenum
);
5348 * Virtual function table for the IEnumSTATSTGImpl class.
5350 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5352 IEnumSTATSTGImpl_QueryInterface
,
5353 IEnumSTATSTGImpl_AddRef
,
5354 IEnumSTATSTGImpl_Release
,
5355 IEnumSTATSTGImpl_Next
,
5356 IEnumSTATSTGImpl_Skip
,
5357 IEnumSTATSTGImpl_Reset
,
5358 IEnumSTATSTGImpl_Clone
5361 /******************************************************************************
5362 ** IEnumSTATSTGImpl implementation
5365 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5366 StorageBaseImpl
* parentStorage
,
5367 DirRef storageDirEntry
)
5369 IEnumSTATSTGImpl
* newEnumeration
;
5371 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5373 if (newEnumeration
!=0)
5376 * Set-up the virtual function table and reference count.
5378 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5379 newEnumeration
->ref
= 0;
5382 * We want to nail-down the reference to the storage in case the
5383 * enumeration out-lives the storage in the client application.
5385 newEnumeration
->parentStorage
= parentStorage
;
5386 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
5388 newEnumeration
->storageDirEntry
= storageDirEntry
;
5391 * Make sure the current node of the iterator is the first one.
5393 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
5396 return newEnumeration
;
5400 * Virtual function table for the Storage32InternalImpl class.
5402 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5404 StorageBaseImpl_QueryInterface
,
5405 StorageBaseImpl_AddRef
,
5406 StorageBaseImpl_Release
,
5407 StorageBaseImpl_CreateStream
,
5408 StorageBaseImpl_OpenStream
,
5409 StorageBaseImpl_CreateStorage
,
5410 StorageBaseImpl_OpenStorage
,
5411 StorageBaseImpl_CopyTo
,
5412 StorageBaseImpl_MoveElementTo
,
5413 StorageInternalImpl_Commit
,
5414 StorageInternalImpl_Revert
,
5415 StorageBaseImpl_EnumElements
,
5416 StorageBaseImpl_DestroyElement
,
5417 StorageBaseImpl_RenameElement
,
5418 StorageBaseImpl_SetElementTimes
,
5419 StorageBaseImpl_SetClass
,
5420 StorageBaseImpl_SetStateBits
,
5421 StorageBaseImpl_Stat
5424 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5426 StorageInternalImpl_Destroy
,
5427 StorageInternalImpl_Invalidate
,
5428 StorageInternalImpl_CreateDirEntry
,
5429 StorageInternalImpl_WriteDirEntry
,
5430 StorageInternalImpl_ReadDirEntry
,
5431 StorageInternalImpl_DestroyDirEntry
,
5432 StorageInternalImpl_StreamReadAt
,
5433 StorageInternalImpl_StreamWriteAt
,
5434 StorageInternalImpl_StreamSetSize
,
5435 StorageInternalImpl_StreamLink
5438 /******************************************************************************
5439 ** Storage32InternalImpl implementation
5442 static StorageInternalImpl
* StorageInternalImpl_Construct(
5443 StorageBaseImpl
* parentStorage
,
5445 DirRef storageDirEntry
)
5447 StorageInternalImpl
* newStorage
;
5449 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5453 list_init(&newStorage
->base
.strmHead
);
5455 list_init(&newStorage
->base
.storageHead
);
5458 * Initialize the virtual function table.
5460 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5461 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5462 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5463 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5465 newStorage
->base
.reverted
= 0;
5467 newStorage
->base
.ref
= 1;
5469 newStorage
->parentStorage
= parentStorage
;
5472 * Keep a reference to the directory entry of this storage
5474 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5476 newStorage
->base
.create
= 0;
5484 /******************************************************************************
5485 ** StorageUtl implementation
5488 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5492 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5493 *value
= lendian16toh(tmp
);
5496 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5498 value
= htole16(value
);
5499 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5502 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5506 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5507 *value
= lendian32toh(tmp
);
5510 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5512 value
= htole32(value
);
5513 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5516 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5517 ULARGE_INTEGER
* value
)
5519 #ifdef WORDS_BIGENDIAN
5522 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5523 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5524 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5526 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5530 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5531 const ULARGE_INTEGER
*value
)
5533 #ifdef WORDS_BIGENDIAN
5536 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5537 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5538 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5540 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5544 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5546 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5547 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5548 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5550 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5553 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5555 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5556 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5557 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5559 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5562 void StorageUtl_CopyDirEntryToSTATSTG(
5563 StorageBaseImpl
* storage
,
5564 STATSTG
* destination
,
5565 const DirEntry
* source
,
5570 if (source
->stgType
== STGTY_ROOT
)
5572 /* replace the name of root entry (often "Root Entry") by the file name */
5573 entryName
= storage
->filename
;
5577 entryName
= source
->name
;
5581 * The copy of the string occurs only when the flag is not set
5583 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5584 (entryName
== NULL
) ||
5585 (entryName
[0] == 0) )
5587 destination
->pwcsName
= 0;
5591 destination
->pwcsName
=
5592 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
5594 strcpyW(destination
->pwcsName
, entryName
);
5597 switch (source
->stgType
)
5601 destination
->type
= STGTY_STORAGE
;
5604 destination
->type
= STGTY_STREAM
;
5607 destination
->type
= STGTY_STREAM
;
5611 destination
->cbSize
= source
->size
;
5613 currentReturnStruct->mtime = {0}; TODO
5614 currentReturnStruct->ctime = {0};
5615 currentReturnStruct->atime = {0};
5617 destination
->grfMode
= 0;
5618 destination
->grfLocksSupported
= 0;
5619 destination
->clsid
= source
->clsid
;
5620 destination
->grfStateBits
= 0;
5621 destination
->reserved
= 0;
5624 /******************************************************************************
5625 ** BlockChainStream implementation
5628 /* Read and save the index of all blocks in this stream. */
5629 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5631 ULONG next_sector
, next_offset
;
5633 struct BlockChainRun
*last_run
;
5635 if (This
->indexCacheLen
== 0)
5639 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5643 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5644 next_offset
= last_run
->lastOffset
+1;
5645 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5646 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5648 if (FAILED(hr
)) return hr
;
5651 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5653 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5655 /* Add the current block to the cache. */
5656 if (This
->indexCacheSize
== 0)
5658 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5659 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5660 This
->indexCacheSize
= 16;
5662 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5664 struct BlockChainRun
*new_cache
;
5667 new_size
= This
->indexCacheSize
* 2;
5668 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5669 if (!new_cache
) return E_OUTOFMEMORY
;
5670 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5672 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5673 This
->indexCache
= new_cache
;
5674 This
->indexCacheSize
= new_size
;
5677 This
->indexCacheLen
++;
5678 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5679 last_run
->firstSector
= next_sector
;
5680 last_run
->firstOffset
= next_offset
;
5683 last_run
->lastOffset
= next_offset
;
5685 /* Find the next block. */
5687 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5688 if (FAILED(hr
)) return hr
;
5691 if (This
->indexCacheLen
)
5693 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5694 This
->numBlocks
= last_run
->lastOffset
+1;
5698 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5699 This
->numBlocks
= 0;
5705 /* Locate the nth block in this stream. */
5706 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5708 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5709 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5711 if (offset
>= This
->numBlocks
)
5712 return BLOCK_END_OF_CHAIN
;
5714 while (min_run
< max_run
)
5716 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5717 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5719 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5720 max_run
= run_to_check
-1;
5722 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5724 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5725 min_run
= run_to_check
+1;
5728 /* Block is in this run. */
5729 min_run
= max_run
= run_to_check
;
5732 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5735 BlockChainStream
* BlockChainStream_Construct(
5736 StorageImpl
* parentStorage
,
5737 ULONG
* headOfStreamPlaceHolder
,
5740 BlockChainStream
* newStream
;
5742 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5744 newStream
->parentStorage
= parentStorage
;
5745 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5746 newStream
->ownerDirEntry
= dirEntry
;
5747 newStream
->indexCache
= NULL
;
5748 newStream
->indexCacheLen
= 0;
5749 newStream
->indexCacheSize
= 0;
5751 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
5753 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
5754 HeapFree(GetProcessHeap(), 0, newStream
);
5761 void BlockChainStream_Destroy(BlockChainStream
* This
)
5764 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5765 HeapFree(GetProcessHeap(), 0, This
);
5768 /******************************************************************************
5769 * BlockChainStream_GetHeadOfChain
5771 * Returns the head of this stream chain.
5772 * Some special chains don't have directory entries, their heads are kept in
5773 * This->headOfStreamPlaceHolder.
5776 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5778 DirEntry chainEntry
;
5781 if (This
->headOfStreamPlaceHolder
!= 0)
5782 return *(This
->headOfStreamPlaceHolder
);
5784 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5786 hr
= StorageImpl_ReadDirEntry(
5787 This
->parentStorage
,
5788 This
->ownerDirEntry
,
5793 return chainEntry
.startingBlock
;
5797 return BLOCK_END_OF_CHAIN
;
5800 /******************************************************************************
5801 * BlockChainStream_GetCount
5803 * Returns the number of blocks that comprises this chain.
5804 * This is not the size of the stream as the last block may not be full!
5806 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5808 return This
->numBlocks
;
5811 /******************************************************************************
5812 * BlockChainStream_ReadAt
5814 * Reads a specified number of bytes from this chain at the specified offset.
5815 * bytesRead may be NULL.
5816 * Failure will be returned if the specified number of bytes has not been read.
5818 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5819 ULARGE_INTEGER offset
,
5824 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5825 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5826 ULONG bytesToReadInBuffer
;
5829 ULARGE_INTEGER stream_size
;
5831 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5834 * Find the first block in the stream that contains part of the buffer.
5836 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5840 stream_size
= BlockChainStream_GetSize(This
);
5841 if (stream_size
.QuadPart
> offset
.QuadPart
)
5842 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
5847 * Start reading the buffer.
5849 bufferWalker
= buffer
;
5851 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5853 ULARGE_INTEGER ulOffset
;
5856 * Calculate how many bytes we can copy from this big block.
5858 bytesToReadInBuffer
=
5859 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5861 TRACE("block %i\n",blockIndex
);
5862 ulOffset
.u
.HighPart
= 0;
5863 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5866 StorageImpl_ReadAt(This
->parentStorage
,
5869 bytesToReadInBuffer
,
5872 * Step to the next big block.
5874 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5875 return STG_E_DOCFILECORRUPT
;
5877 bufferWalker
+= bytesReadAt
;
5878 size
-= bytesReadAt
;
5879 *bytesRead
+= bytesReadAt
;
5880 offsetInBlock
= 0; /* There is no offset on the next block */
5882 if (bytesToReadInBuffer
!= bytesReadAt
)
5889 /******************************************************************************
5890 * BlockChainStream_WriteAt
5892 * Writes the specified number of bytes to this chain at the specified offset.
5893 * Will fail if not all specified number of bytes have been written.
5895 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5896 ULARGE_INTEGER offset
,
5899 ULONG
* bytesWritten
)
5901 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5902 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5905 const BYTE
* bufferWalker
;
5908 * Find the first block in the stream that contains part of the buffer.
5910 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5912 /* BlockChainStream_SetSize should have already been called to ensure we have
5913 * enough blocks in the chain to write into */
5914 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5916 ERR("not enough blocks in chain to write data\n");
5917 return STG_E_DOCFILECORRUPT
;
5921 bufferWalker
= buffer
;
5923 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5925 ULARGE_INTEGER ulOffset
;
5926 DWORD bytesWrittenAt
;
5928 * Calculate how many bytes we can copy from this big block.
5931 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5933 TRACE("block %i\n",blockIndex
);
5934 ulOffset
.u
.HighPart
= 0;
5935 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5938 StorageImpl_WriteAt(This
->parentStorage
,
5945 * Step to the next big block.
5947 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5949 return STG_E_DOCFILECORRUPT
;
5951 bufferWalker
+= bytesWrittenAt
;
5952 size
-= bytesWrittenAt
;
5953 *bytesWritten
+= bytesWrittenAt
;
5954 offsetInBlock
= 0; /* There is no offset on the next block */
5956 if (bytesWrittenAt
!= bytesToWrite
)
5960 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5963 /******************************************************************************
5964 * BlockChainStream_Shrink
5966 * Shrinks this chain in the big block depot.
5968 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5969 ULARGE_INTEGER newSize
)
5975 * Figure out how many blocks are needed to contain the new size
5977 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5979 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5985 * Go to the new end of chain
5987 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
5989 /* Mark the new end of chain */
5990 StorageImpl_SetNextBlockInChain(
5991 This
->parentStorage
,
5993 BLOCK_END_OF_CHAIN
);
5995 This
->tailIndex
= blockIndex
;
5999 if (This
->headOfStreamPlaceHolder
!= 0)
6001 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6005 DirEntry chainEntry
;
6006 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6008 StorageImpl_ReadDirEntry(
6009 This
->parentStorage
,
6010 This
->ownerDirEntry
,
6013 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6015 StorageImpl_WriteDirEntry(
6016 This
->parentStorage
,
6017 This
->ownerDirEntry
,
6021 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6024 This
->numBlocks
= numBlocks
;
6027 * Mark the extra blocks as free
6029 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6031 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6032 StorageImpl_FreeBigBlock(This
->parentStorage
,
6033 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6034 if (last_run
->lastOffset
== last_run
->firstOffset
)
6035 This
->indexCacheLen
--;
6037 last_run
->lastOffset
--;
6043 /******************************************************************************
6044 * BlockChainStream_Enlarge
6046 * Grows this chain in the big block depot.
6048 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6049 ULARGE_INTEGER newSize
)
6051 ULONG blockIndex
, currentBlock
;
6053 ULONG oldNumBlocks
= 0;
6055 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6058 * Empty chain. Create the head.
6060 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6062 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6063 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6065 BLOCK_END_OF_CHAIN
);
6067 if (This
->headOfStreamPlaceHolder
!= 0)
6069 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6073 DirEntry chainEntry
;
6074 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6076 StorageImpl_ReadDirEntry(
6077 This
->parentStorage
,
6078 This
->ownerDirEntry
,
6081 chainEntry
.startingBlock
= blockIndex
;
6083 StorageImpl_WriteDirEntry(
6084 This
->parentStorage
,
6085 This
->ownerDirEntry
,
6089 This
->tailIndex
= blockIndex
;
6090 This
->numBlocks
= 1;
6094 * Figure out how many blocks are needed to contain this stream
6096 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6098 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6102 * Go to the current end of chain
6104 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6106 currentBlock
= blockIndex
;
6108 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6111 currentBlock
= blockIndex
;
6113 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6118 This
->tailIndex
= currentBlock
;
6121 currentBlock
= This
->tailIndex
;
6122 oldNumBlocks
= This
->numBlocks
;
6125 * Add new blocks to the chain
6127 if (oldNumBlocks
< newNumBlocks
)
6129 while (oldNumBlocks
< newNumBlocks
)
6131 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6133 StorageImpl_SetNextBlockInChain(
6134 This
->parentStorage
,
6138 StorageImpl_SetNextBlockInChain(
6139 This
->parentStorage
,
6141 BLOCK_END_OF_CHAIN
);
6143 currentBlock
= blockIndex
;
6147 This
->tailIndex
= blockIndex
;
6148 This
->numBlocks
= newNumBlocks
;
6151 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6157 /******************************************************************************
6158 * BlockChainStream_SetSize
6160 * Sets the size of this stream. The big block depot will be updated.
6161 * The file will grow if we grow the chain.
6163 * TODO: Free the actual blocks in the file when we shrink the chain.
6164 * Currently, the blocks are still in the file. So the file size
6165 * doesn't shrink even if we shrink streams.
6167 BOOL
BlockChainStream_SetSize(
6168 BlockChainStream
* This
,
6169 ULARGE_INTEGER newSize
)
6171 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6173 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6176 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6178 BlockChainStream_Shrink(This
, newSize
);
6182 BlockChainStream_Enlarge(This
, newSize
);
6188 /******************************************************************************
6189 * BlockChainStream_GetSize
6191 * Returns the size of this chain.
6192 * Will return the block count if this chain doesn't have a directory entry.
6194 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6196 DirEntry chainEntry
;
6198 if(This
->headOfStreamPlaceHolder
== NULL
)
6201 * This chain has a directory entry so use the size value from there.
6203 StorageImpl_ReadDirEntry(
6204 This
->parentStorage
,
6205 This
->ownerDirEntry
,
6208 return chainEntry
.size
;
6213 * this chain is a chain that does not have a directory entry, figure out the
6214 * size by making the product number of used blocks times the
6217 ULARGE_INTEGER result
;
6218 result
.u
.HighPart
= 0;
6221 BlockChainStream_GetCount(This
) *
6222 This
->parentStorage
->bigBlockSize
;
6228 /******************************************************************************
6229 ** SmallBlockChainStream implementation
6232 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6233 StorageImpl
* parentStorage
,
6234 ULONG
* headOfStreamPlaceHolder
,
6237 SmallBlockChainStream
* newStream
;
6239 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6241 newStream
->parentStorage
= parentStorage
;
6242 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6243 newStream
->ownerDirEntry
= dirEntry
;
6248 void SmallBlockChainStream_Destroy(
6249 SmallBlockChainStream
* This
)
6251 HeapFree(GetProcessHeap(), 0, This
);
6254 /******************************************************************************
6255 * SmallBlockChainStream_GetHeadOfChain
6257 * Returns the head of this chain of small blocks.
6259 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6260 SmallBlockChainStream
* This
)
6262 DirEntry chainEntry
;
6265 if (This
->headOfStreamPlaceHolder
!= NULL
)
6266 return *(This
->headOfStreamPlaceHolder
);
6268 if (This
->ownerDirEntry
)
6270 hr
= StorageImpl_ReadDirEntry(
6271 This
->parentStorage
,
6272 This
->ownerDirEntry
,
6277 return chainEntry
.startingBlock
;
6282 return BLOCK_END_OF_CHAIN
;
6285 /******************************************************************************
6286 * SmallBlockChainStream_GetNextBlockInChain
6288 * Returns the index of the next small block in this chain.
6291 * - BLOCK_END_OF_CHAIN: end of this chain
6292 * - BLOCK_UNUSED: small block 'blockIndex' is free
6294 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6295 SmallBlockChainStream
* This
,
6297 ULONG
* nextBlockInChain
)
6299 ULARGE_INTEGER offsetOfBlockInDepot
;
6304 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6306 offsetOfBlockInDepot
.u
.HighPart
= 0;
6307 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6310 * Read those bytes in the buffer from the small block file.
6312 res
= BlockChainStream_ReadAt(
6313 This
->parentStorage
->smallBlockDepotChain
,
6314 offsetOfBlockInDepot
,
6319 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6320 res
= STG_E_READFAULT
;
6324 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6331 /******************************************************************************
6332 * SmallBlockChainStream_SetNextBlockInChain
6334 * Writes the index of the next block of the specified block in the small
6336 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6337 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6339 static void SmallBlockChainStream_SetNextBlockInChain(
6340 SmallBlockChainStream
* This
,
6344 ULARGE_INTEGER offsetOfBlockInDepot
;
6348 offsetOfBlockInDepot
.u
.HighPart
= 0;
6349 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6351 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6354 * Read those bytes in the buffer from the small block file.
6356 BlockChainStream_WriteAt(
6357 This
->parentStorage
->smallBlockDepotChain
,
6358 offsetOfBlockInDepot
,
6364 /******************************************************************************
6365 * SmallBlockChainStream_FreeBlock
6367 * Flag small block 'blockIndex' as free in the small block depot.
6369 static void SmallBlockChainStream_FreeBlock(
6370 SmallBlockChainStream
* This
,
6373 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6376 /******************************************************************************
6377 * SmallBlockChainStream_GetNextFreeBlock
6379 * Returns the index of a free small block. The small block depot will be
6380 * enlarged if necessary. The small block chain will also be enlarged if
6383 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6384 SmallBlockChainStream
* This
)
6386 ULARGE_INTEGER offsetOfBlockInDepot
;
6389 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6390 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6392 ULONG smallBlocksPerBigBlock
;
6394 offsetOfBlockInDepot
.u
.HighPart
= 0;
6397 * Scan the small block depot for a free block
6399 while (nextBlockIndex
!= BLOCK_UNUSED
)
6401 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6403 res
= BlockChainStream_ReadAt(
6404 This
->parentStorage
->smallBlockDepotChain
,
6405 offsetOfBlockInDepot
,
6411 * If we run out of space for the small block depot, enlarge it
6413 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6415 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6417 if (nextBlockIndex
!= BLOCK_UNUSED
)
6423 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6425 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6426 ULARGE_INTEGER newSize
, offset
;
6429 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6430 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6433 * Initialize all the small blocks to free
6435 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6436 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6437 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6438 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6440 StorageImpl_SaveFileHeader(This
->parentStorage
);
6444 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6446 smallBlocksPerBigBlock
=
6447 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6450 * Verify if we have to allocate big blocks to contain small blocks
6452 if (blockIndex
% smallBlocksPerBigBlock
== 0)
6455 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6456 ULARGE_INTEGER old_size
, size_required
;
6458 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6460 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6462 if (size_required
.QuadPart
> old_size
.QuadPart
)
6464 BlockChainStream_SetSize(
6465 This
->parentStorage
->smallBlockRootChain
,
6468 StorageImpl_ReadDirEntry(
6469 This
->parentStorage
,
6470 This
->parentStorage
->base
.storageDirEntry
,
6473 rootEntry
.size
= size_required
;
6475 StorageImpl_WriteDirEntry(
6476 This
->parentStorage
,
6477 This
->parentStorage
->base
.storageDirEntry
,
6485 /******************************************************************************
6486 * SmallBlockChainStream_ReadAt
6488 * Reads a specified number of bytes from this chain at the specified offset.
6489 * bytesRead may be NULL.
6490 * Failure will be returned if the specified number of bytes has not been read.
6492 HRESULT
SmallBlockChainStream_ReadAt(
6493 SmallBlockChainStream
* This
,
6494 ULARGE_INTEGER offset
,
6500 ULARGE_INTEGER offsetInBigBlockFile
;
6501 ULONG blockNoInSequence
=
6502 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6504 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6505 ULONG bytesToReadInBuffer
;
6507 ULONG bytesReadFromBigBlockFile
;
6509 ULARGE_INTEGER stream_size
;
6512 * This should never happen on a small block file.
6514 assert(offset
.u
.HighPart
==0);
6518 stream_size
= SmallBlockChainStream_GetSize(This
);
6519 if (stream_size
.QuadPart
> offset
.QuadPart
)
6520 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6525 * Find the first block in the stream that contains part of the buffer.
6527 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6529 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6531 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6534 blockNoInSequence
--;
6538 * Start reading the buffer.
6540 bufferWalker
= buffer
;
6542 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6545 * Calculate how many bytes we can copy from this small block.
6547 bytesToReadInBuffer
=
6548 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6551 * Calculate the offset of the small block in the small block file.
6553 offsetInBigBlockFile
.u
.HighPart
= 0;
6554 offsetInBigBlockFile
.u
.LowPart
=
6555 blockIndex
* This
->parentStorage
->smallBlockSize
;
6557 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6560 * Read those bytes in the buffer from the small block file.
6561 * The small block has already been identified so it shouldn't fail
6562 * unless the file is corrupt.
6564 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6565 offsetInBigBlockFile
,
6566 bytesToReadInBuffer
,
6568 &bytesReadFromBigBlockFile
);
6573 if (!bytesReadFromBigBlockFile
)
6574 return STG_E_DOCFILECORRUPT
;
6577 * Step to the next big block.
6579 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6581 return STG_E_DOCFILECORRUPT
;
6583 bufferWalker
+= bytesReadFromBigBlockFile
;
6584 size
-= bytesReadFromBigBlockFile
;
6585 *bytesRead
+= bytesReadFromBigBlockFile
;
6586 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6592 /******************************************************************************
6593 * SmallBlockChainStream_WriteAt
6595 * Writes the specified number of bytes to this chain at the specified offset.
6596 * Will fail if not all specified number of bytes have been written.
6598 HRESULT
SmallBlockChainStream_WriteAt(
6599 SmallBlockChainStream
* This
,
6600 ULARGE_INTEGER offset
,
6603 ULONG
* bytesWritten
)
6605 ULARGE_INTEGER offsetInBigBlockFile
;
6606 ULONG blockNoInSequence
=
6607 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6609 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6610 ULONG bytesToWriteInBuffer
;
6612 ULONG bytesWrittenToBigBlockFile
;
6613 const BYTE
* bufferWalker
;
6617 * This should never happen on a small block file.
6619 assert(offset
.u
.HighPart
==0);
6622 * Find the first block in the stream that contains part of the buffer.
6624 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6626 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6628 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6629 return STG_E_DOCFILECORRUPT
;
6630 blockNoInSequence
--;
6634 * Start writing the buffer.
6637 bufferWalker
= buffer
;
6638 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6641 * Calculate how many bytes we can copy to this small block.
6643 bytesToWriteInBuffer
=
6644 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6647 * Calculate the offset of the small block in the small block file.
6649 offsetInBigBlockFile
.u
.HighPart
= 0;
6650 offsetInBigBlockFile
.u
.LowPart
=
6651 blockIndex
* This
->parentStorage
->smallBlockSize
;
6653 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6656 * Write those bytes in the buffer to the small block file.
6658 res
= BlockChainStream_WriteAt(
6659 This
->parentStorage
->smallBlockRootChain
,
6660 offsetInBigBlockFile
,
6661 bytesToWriteInBuffer
,
6663 &bytesWrittenToBigBlockFile
);
6668 * Step to the next big block.
6670 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6673 bufferWalker
+= bytesWrittenToBigBlockFile
;
6674 size
-= bytesWrittenToBigBlockFile
;
6675 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6676 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6679 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6682 /******************************************************************************
6683 * SmallBlockChainStream_Shrink
6685 * Shrinks this chain in the small block depot.
6687 static BOOL
SmallBlockChainStream_Shrink(
6688 SmallBlockChainStream
* This
,
6689 ULARGE_INTEGER newSize
)
6691 ULONG blockIndex
, extraBlock
;
6695 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6697 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6700 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6703 * Go to the new end of chain
6705 while (count
< numBlocks
)
6707 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6714 * If the count is 0, we have a special case, the head of the chain was
6719 DirEntry chainEntry
;
6721 StorageImpl_ReadDirEntry(This
->parentStorage
,
6722 This
->ownerDirEntry
,
6725 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6727 StorageImpl_WriteDirEntry(This
->parentStorage
,
6728 This
->ownerDirEntry
,
6732 * We start freeing the chain at the head block.
6734 extraBlock
= blockIndex
;
6738 /* Get the next block before marking the new end */
6739 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6743 /* Mark the new end of chain */
6744 SmallBlockChainStream_SetNextBlockInChain(
6747 BLOCK_END_OF_CHAIN
);
6751 * Mark the extra blocks as free
6753 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6755 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6758 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6759 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
6760 extraBlock
= blockIndex
;
6766 /******************************************************************************
6767 * SmallBlockChainStream_Enlarge
6769 * Grows this chain in the small block depot.
6771 static BOOL
SmallBlockChainStream_Enlarge(
6772 SmallBlockChainStream
* This
,
6773 ULARGE_INTEGER newSize
)
6775 ULONG blockIndex
, currentBlock
;
6777 ULONG oldNumBlocks
= 0;
6779 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6782 * Empty chain. Create the head.
6784 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6786 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6787 SmallBlockChainStream_SetNextBlockInChain(
6790 BLOCK_END_OF_CHAIN
);
6792 if (This
->headOfStreamPlaceHolder
!= NULL
)
6794 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6798 DirEntry chainEntry
;
6800 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6803 chainEntry
.startingBlock
= blockIndex
;
6805 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6810 currentBlock
= blockIndex
;
6813 * Figure out how many blocks are needed to contain this stream
6815 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6817 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6821 * Go to the current end of chain
6823 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6826 currentBlock
= blockIndex
;
6827 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6832 * Add new blocks to the chain
6834 while (oldNumBlocks
< newNumBlocks
)
6836 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6837 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6839 SmallBlockChainStream_SetNextBlockInChain(
6842 BLOCK_END_OF_CHAIN
);
6844 currentBlock
= blockIndex
;
6851 /******************************************************************************
6852 * SmallBlockChainStream_SetSize
6854 * Sets the size of this stream.
6855 * The file will grow if we grow the chain.
6857 * TODO: Free the actual blocks in the file when we shrink the chain.
6858 * Currently, the blocks are still in the file. So the file size
6859 * doesn't shrink even if we shrink streams.
6861 BOOL
SmallBlockChainStream_SetSize(
6862 SmallBlockChainStream
* This
,
6863 ULARGE_INTEGER newSize
)
6865 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6867 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6870 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6872 SmallBlockChainStream_Shrink(This
, newSize
);
6876 SmallBlockChainStream_Enlarge(This
, newSize
);
6882 /******************************************************************************
6883 * SmallBlockChainStream_GetCount
6885 * Returns the number of small blocks that comprises this chain.
6886 * This is not the size of the stream as the last block may not be full!
6889 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6894 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6896 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6900 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6901 blockIndex
, &blockIndex
)))
6908 /******************************************************************************
6909 * SmallBlockChainStream_GetSize
6911 * Returns the size of this chain.
6913 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6915 DirEntry chainEntry
;
6917 if(This
->headOfStreamPlaceHolder
!= NULL
)
6919 ULARGE_INTEGER result
;
6920 result
.u
.HighPart
= 0;
6922 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6923 This
->parentStorage
->smallBlockSize
;
6928 StorageImpl_ReadDirEntry(
6929 This
->parentStorage
,
6930 This
->ownerDirEntry
,
6933 return chainEntry
.size
;
6936 static HRESULT
create_storagefile(
6940 STGOPTIONS
* pStgOptions
,
6944 StorageBaseImpl
* newStorage
= 0;
6945 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6946 HRESULT hr
= STG_E_INVALIDFLAG
;
6950 DWORD fileAttributes
;
6951 WCHAR tempFileName
[MAX_PATH
];
6954 return STG_E_INVALIDPOINTER
;
6956 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
6957 return STG_E_INVALIDPARAMETER
;
6959 /* if no share mode given then DENY_NONE is the default */
6960 if (STGM_SHARE_MODE(grfMode
) == 0)
6961 grfMode
|= STGM_SHARE_DENY_NONE
;
6963 if ( FAILED( validateSTGM(grfMode
) ))
6966 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6967 switch(STGM_ACCESS_MODE(grfMode
))
6970 case STGM_READWRITE
:
6976 /* in direct mode, can only use SHARE_EXCLUSIVE */
6977 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6980 /* but in transacted mode, any share mode is valid */
6983 * Generate a unique name.
6987 WCHAR tempPath
[MAX_PATH
];
6988 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6990 memset(tempPath
, 0, sizeof(tempPath
));
6991 memset(tempFileName
, 0, sizeof(tempFileName
));
6993 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6996 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6997 pwcsName
= tempFileName
;
7000 hr
= STG_E_INSUFFICIENTMEMORY
;
7004 creationMode
= TRUNCATE_EXISTING
;
7008 creationMode
= GetCreationModeFromSTGM(grfMode
);
7012 * Interpret the STGM value grfMode
7014 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7015 accessMode
= GetAccessModeFromSTGM(grfMode
);
7017 if (grfMode
& STGM_DELETEONRELEASE
)
7018 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7020 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7022 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7026 FIXME("Storage share mode not implemented.\n");
7031 hFile
= CreateFileW(pwcsName
,
7039 if (hFile
== INVALID_HANDLE_VALUE
)
7041 if(GetLastError() == ERROR_FILE_EXISTS
)
7042 hr
= STG_E_FILEALREADYEXISTS
;
7049 * Allocate and initialize the new IStorage32object.
7051 hr
= Storage_Construct(
7058 pStgOptions
->ulSectorSize
,
7066 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
7068 IStorage_Release((IStorage
*)newStorage
);
7071 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7076 /******************************************************************************
7077 * StgCreateDocfile [OLE32.@]
7078 * Creates a new compound file storage object
7081 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7082 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7083 * reserved [ ?] unused?, usually 0
7084 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7087 * S_OK if the file was successfully created
7088 * some STG_E_ value if error
7090 * if pwcsName is NULL, create file with new unique name
7091 * the function can returns
7092 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7095 HRESULT WINAPI
StgCreateDocfile(
7099 IStorage
**ppstgOpen
)
7101 STGOPTIONS stgoptions
= {1, 0, 512};
7103 TRACE("(%s, %x, %d, %p)\n",
7104 debugstr_w(pwcsName
), grfMode
,
7105 reserved
, ppstgOpen
);
7108 return STG_E_INVALIDPOINTER
;
7110 return STG_E_INVALIDPARAMETER
;
7112 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7115 /******************************************************************************
7116 * StgCreateStorageEx [OLE32.@]
7118 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7120 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7121 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7123 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7125 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7126 return STG_E_INVALIDPARAMETER
;
7129 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7131 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7132 return STG_E_INVALIDPARAMETER
;
7135 if (stgfmt
== STGFMT_FILE
)
7137 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7138 return STG_E_INVALIDPARAMETER
;
7141 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7143 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7147 ERR("Invalid stgfmt argument\n");
7148 return STG_E_INVALIDPARAMETER
;
7151 /******************************************************************************
7152 * StgCreatePropSetStg [OLE32.@]
7154 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7155 IPropertySetStorage
**ppPropSetStg
)
7159 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
7161 hr
= STG_E_INVALIDPARAMETER
;
7163 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
7164 (void**)ppPropSetStg
);
7168 /******************************************************************************
7169 * StgOpenStorageEx [OLE32.@]
7171 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7173 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7174 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7176 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7178 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7179 return STG_E_INVALIDPARAMETER
;
7185 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7186 return STG_E_INVALIDPARAMETER
;
7188 case STGFMT_STORAGE
:
7191 case STGFMT_DOCFILE
:
7192 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7194 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7195 return STG_E_INVALIDPARAMETER
;
7197 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7201 WARN("STGFMT_ANY assuming storage\n");
7205 return STG_E_INVALIDPARAMETER
;
7208 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7212 /******************************************************************************
7213 * StgOpenStorage [OLE32.@]
7215 HRESULT WINAPI
StgOpenStorage(
7216 const OLECHAR
*pwcsName
,
7217 IStorage
*pstgPriority
,
7221 IStorage
**ppstgOpen
)
7223 StorageBaseImpl
* newStorage
= 0;
7229 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7230 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7231 snbExclude
, reserved
, ppstgOpen
);
7235 hr
= STG_E_INVALIDNAME
;
7241 hr
= STG_E_INVALIDPOINTER
;
7247 hr
= STG_E_INVALIDPARAMETER
;
7251 if (grfMode
& STGM_PRIORITY
)
7253 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7254 return STG_E_INVALIDFLAG
;
7255 if (grfMode
& STGM_DELETEONRELEASE
)
7256 return STG_E_INVALIDFUNCTION
;
7257 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7258 return STG_E_INVALIDFLAG
;
7259 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7260 grfMode
|= STGM_SHARE_DENY_NONE
;
7262 /* STGM_PRIORITY stops other IStorage objects on the same file from
7263 * committing until the STGM_PRIORITY IStorage is closed. it also
7264 * stops non-transacted mode StgOpenStorage calls with write access from
7265 * succeeding. obviously, both of these cannot be achieved through just
7266 * file share flags */
7267 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7271 * Validate the sharing mode
7273 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7274 switch(STGM_SHARE_MODE(grfMode
))
7276 case STGM_SHARE_EXCLUSIVE
:
7277 case STGM_SHARE_DENY_WRITE
:
7280 hr
= STG_E_INVALIDFLAG
;
7284 if ( FAILED( validateSTGM(grfMode
) ) ||
7285 (grfMode
&STGM_CREATE
))
7287 hr
= STG_E_INVALIDFLAG
;
7291 /* shared reading requires transacted mode */
7292 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7293 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7294 !(grfMode
&STGM_TRANSACTED
) )
7296 hr
= STG_E_INVALIDFLAG
;
7301 * Interpret the STGM value grfMode
7303 shareMode
= GetShareModeFromSTGM(grfMode
);
7304 accessMode
= GetAccessModeFromSTGM(grfMode
);
7308 hFile
= CreateFileW( pwcsName
,
7313 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7316 if (hFile
==INVALID_HANDLE_VALUE
)
7318 DWORD last_error
= GetLastError();
7324 case ERROR_FILE_NOT_FOUND
:
7325 hr
= STG_E_FILENOTFOUND
;
7328 case ERROR_PATH_NOT_FOUND
:
7329 hr
= STG_E_PATHNOTFOUND
;
7332 case ERROR_ACCESS_DENIED
:
7333 case ERROR_WRITE_PROTECT
:
7334 hr
= STG_E_ACCESSDENIED
;
7337 case ERROR_SHARING_VIOLATION
:
7338 hr
= STG_E_SHAREVIOLATION
;
7349 * Refuse to open the file if it's too small to be a structured storage file
7350 * FIXME: verify the file when reading instead of here
7352 if (GetFileSize(hFile
, NULL
) < 0x100)
7355 hr
= STG_E_FILEALREADYEXISTS
;
7360 * Allocate and initialize the new IStorage32object.
7362 hr
= Storage_Construct(
7375 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7377 if(hr
== STG_E_INVALIDHEADER
)
7378 hr
= STG_E_FILEALREADYEXISTS
;
7383 * Get an "out" pointer for the caller.
7385 *ppstgOpen
= (IStorage
*)newStorage
;
7388 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7392 /******************************************************************************
7393 * StgCreateDocfileOnILockBytes [OLE32.@]
7395 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7399 IStorage
** ppstgOpen
)
7401 StorageBaseImpl
* newStorage
= 0;
7404 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7405 return STG_E_INVALIDPOINTER
;
7408 * Allocate and initialize the new IStorage object.
7410 hr
= Storage_Construct(
7426 * Get an "out" pointer for the caller.
7428 *ppstgOpen
= (IStorage
*)newStorage
;
7433 /******************************************************************************
7434 * StgOpenStorageOnILockBytes [OLE32.@]
7436 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7438 IStorage
*pstgPriority
,
7442 IStorage
**ppstgOpen
)
7444 StorageBaseImpl
* newStorage
= 0;
7447 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7448 return STG_E_INVALIDPOINTER
;
7450 if ( FAILED( validateSTGM(grfMode
) ))
7451 return STG_E_INVALIDFLAG
;
7456 * Allocate and initialize the new IStorage object.
7458 hr
= Storage_Construct(
7474 * Get an "out" pointer for the caller.
7476 *ppstgOpen
= (IStorage
*)newStorage
;
7481 /******************************************************************************
7482 * StgSetTimes [ole32.@]
7483 * StgSetTimes [OLE32.@]
7487 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7488 FILETIME
const *patime
, FILETIME
const *pmtime
)
7490 IStorage
*stg
= NULL
;
7493 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7495 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7499 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7500 IStorage_Release(stg
);
7506 /******************************************************************************
7507 * StgIsStorageILockBytes [OLE32.@]
7509 * Determines if the ILockBytes contains a storage object.
7511 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7514 ULARGE_INTEGER offset
;
7516 offset
.u
.HighPart
= 0;
7517 offset
.u
.LowPart
= 0;
7519 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
7521 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
7527 /******************************************************************************
7528 * WriteClassStg [OLE32.@]
7530 * This method will store the specified CLSID in the specified storage object
7532 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7537 return E_INVALIDARG
;
7540 return STG_E_INVALIDPOINTER
;
7542 hRes
= IStorage_SetClass(pStg
, rclsid
);
7547 /***********************************************************************
7548 * ReadClassStg (OLE32.@)
7550 * This method reads the CLSID previously written to a storage object with
7551 * the WriteClassStg.
7554 * pstg [I] IStorage pointer
7555 * pclsid [O] Pointer to where the CLSID is written
7559 * Failure: HRESULT code.
7561 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7566 TRACE("(%p, %p)\n", pstg
, pclsid
);
7568 if(!pstg
|| !pclsid
)
7569 return E_INVALIDARG
;
7572 * read a STATSTG structure (contains the clsid) from the storage
7574 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7577 *pclsid
=pstatstg
.clsid
;
7582 /***********************************************************************
7583 * OleLoadFromStream (OLE32.@)
7585 * This function loads an object from stream
7587 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7591 LPPERSISTSTREAM xstm
;
7593 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7595 res
=ReadClassStm(pStm
,&clsid
);
7598 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7601 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7603 IUnknown_Release((IUnknown
*)*ppvObj
);
7606 res
=IPersistStream_Load(xstm
,pStm
);
7607 IPersistStream_Release(xstm
);
7608 /* FIXME: all refcounts ok at this point? I think they should be:
7611 * xstm : 0 (released)
7616 /***********************************************************************
7617 * OleSaveToStream (OLE32.@)
7619 * This function saves an object with the IPersistStream interface on it
7620 * to the specified stream.
7622 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7628 TRACE("(%p,%p)\n",pPStm
,pStm
);
7630 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7632 if (SUCCEEDED(res
)){
7634 res
=WriteClassStm(pStm
,&clsid
);
7638 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7641 TRACE("Finished Save\n");
7645 /****************************************************************************
7646 * This method validate a STGM parameter that can contain the values below
7648 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7649 * The stgm values contained in 0xffff0000 are bitmasks.
7651 * STGM_DIRECT 0x00000000
7652 * STGM_TRANSACTED 0x00010000
7653 * STGM_SIMPLE 0x08000000
7655 * STGM_READ 0x00000000
7656 * STGM_WRITE 0x00000001
7657 * STGM_READWRITE 0x00000002
7659 * STGM_SHARE_DENY_NONE 0x00000040
7660 * STGM_SHARE_DENY_READ 0x00000030
7661 * STGM_SHARE_DENY_WRITE 0x00000020
7662 * STGM_SHARE_EXCLUSIVE 0x00000010
7664 * STGM_PRIORITY 0x00040000
7665 * STGM_DELETEONRELEASE 0x04000000
7667 * STGM_CREATE 0x00001000
7668 * STGM_CONVERT 0x00020000
7669 * STGM_FAILIFTHERE 0x00000000
7671 * STGM_NOSCRATCH 0x00100000
7672 * STGM_NOSNAPSHOT 0x00200000
7674 static HRESULT
validateSTGM(DWORD stgm
)
7676 DWORD access
= STGM_ACCESS_MODE(stgm
);
7677 DWORD share
= STGM_SHARE_MODE(stgm
);
7678 DWORD create
= STGM_CREATE_MODE(stgm
);
7680 if (stgm
&~STGM_KNOWN_FLAGS
)
7682 ERR("unknown flags %08x\n", stgm
);
7690 case STGM_READWRITE
:
7698 case STGM_SHARE_DENY_NONE
:
7699 case STGM_SHARE_DENY_READ
:
7700 case STGM_SHARE_DENY_WRITE
:
7701 case STGM_SHARE_EXCLUSIVE
:
7710 case STGM_FAILIFTHERE
:
7717 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7719 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7723 * STGM_CREATE | STGM_CONVERT
7724 * if both are false, STGM_FAILIFTHERE is set to TRUE
7726 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7730 * STGM_NOSCRATCH requires STGM_TRANSACTED
7732 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7736 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7737 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7739 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7740 (!(stgm
& STGM_TRANSACTED
) ||
7741 share
== STGM_SHARE_EXCLUSIVE
||
7742 share
== STGM_SHARE_DENY_WRITE
) )
7748 /****************************************************************************
7749 * GetShareModeFromSTGM
7751 * This method will return a share mode flag from a STGM value.
7752 * The STGM value is assumed valid.
7754 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7756 switch (STGM_SHARE_MODE(stgm
))
7758 case STGM_SHARE_DENY_NONE
:
7759 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7760 case STGM_SHARE_DENY_READ
:
7761 return FILE_SHARE_WRITE
;
7762 case STGM_SHARE_DENY_WRITE
:
7763 return FILE_SHARE_READ
;
7764 case STGM_SHARE_EXCLUSIVE
:
7767 ERR("Invalid share mode!\n");
7772 /****************************************************************************
7773 * GetAccessModeFromSTGM
7775 * This method will return an access mode flag from a STGM value.
7776 * The STGM value is assumed valid.
7778 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7780 switch (STGM_ACCESS_MODE(stgm
))
7783 return GENERIC_READ
;
7785 case STGM_READWRITE
:
7786 return GENERIC_READ
| GENERIC_WRITE
;
7788 ERR("Invalid access mode!\n");
7793 /****************************************************************************
7794 * GetCreationModeFromSTGM
7796 * This method will return a creation mode flag from a STGM value.
7797 * The STGM value is assumed valid.
7799 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7801 switch(STGM_CREATE_MODE(stgm
))
7804 return CREATE_ALWAYS
;
7806 FIXME("STGM_CONVERT not implemented!\n");
7808 case STGM_FAILIFTHERE
:
7811 ERR("Invalid create mode!\n");
7817 /*************************************************************************
7818 * OLECONVERT_LoadOLE10 [Internal]
7820 * Loads the OLE10 STREAM to memory
7823 * pOleStream [I] The OLESTREAM
7824 * pData [I] Data Structure for the OLESTREAM Data
7828 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7829 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7832 * This function is used by OleConvertOLESTREAMToIStorage only.
7834 * Memory allocated for pData must be freed by the caller
7836 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7839 HRESULT hRes
= S_OK
;
7843 pData
->pData
= NULL
;
7844 pData
->pstrOleObjFileName
= NULL
;
7846 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7849 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7850 if(dwSize
!= sizeof(pData
->dwOleID
))
7852 hRes
= CONVERT10_E_OLESTREAM_GET
;
7854 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7856 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7867 /* Get the TypeID... more info needed for this field */
7868 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7869 if(dwSize
!= sizeof(pData
->dwTypeID
))
7871 hRes
= CONVERT10_E_OLESTREAM_GET
;
7876 if(pData
->dwTypeID
!= 0)
7878 /* Get the length of the OleTypeName */
7879 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7880 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7882 hRes
= CONVERT10_E_OLESTREAM_GET
;
7887 if(pData
->dwOleTypeNameLength
> 0)
7889 /* Get the OleTypeName */
7890 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7891 if(dwSize
!= pData
->dwOleTypeNameLength
)
7893 hRes
= CONVERT10_E_OLESTREAM_GET
;
7899 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7900 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7902 hRes
= CONVERT10_E_OLESTREAM_GET
;
7906 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7907 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7908 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7909 if(pData
->pstrOleObjFileName
)
7911 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7912 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7914 hRes
= CONVERT10_E_OLESTREAM_GET
;
7918 hRes
= CONVERT10_E_OLESTREAM_GET
;
7923 /* Get the Width of the Metafile */
7924 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7925 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7927 hRes
= CONVERT10_E_OLESTREAM_GET
;
7931 /* Get the Height of the Metafile */
7932 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7933 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7935 hRes
= CONVERT10_E_OLESTREAM_GET
;
7941 /* Get the Length of the Data */
7942 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7943 if(dwSize
!= sizeof(pData
->dwDataLength
))
7945 hRes
= CONVERT10_E_OLESTREAM_GET
;
7949 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7951 if(!bStrem1
) /* if it is a second OLE stream data */
7953 pData
->dwDataLength
-= 8;
7954 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7955 if(dwSize
!= sizeof(pData
->strUnknown
))
7957 hRes
= CONVERT10_E_OLESTREAM_GET
;
7963 if(pData
->dwDataLength
> 0)
7965 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7967 /* Get Data (ex. IStorage, Metafile, or BMP) */
7970 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7971 if(dwSize
!= pData
->dwDataLength
)
7973 hRes
= CONVERT10_E_OLESTREAM_GET
;
7978 hRes
= CONVERT10_E_OLESTREAM_GET
;
7987 /*************************************************************************
7988 * OLECONVERT_SaveOLE10 [Internal]
7990 * Saves the OLE10 STREAM From memory
7993 * pData [I] Data Structure for the OLESTREAM Data
7994 * pOleStream [I] The OLESTREAM to save
7998 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8001 * This function is used by OleConvertIStorageToOLESTREAM only.
8004 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8007 HRESULT hRes
= S_OK
;
8011 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8012 if(dwSize
!= sizeof(pData
->dwOleID
))
8014 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8019 /* Set the TypeID */
8020 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8021 if(dwSize
!= sizeof(pData
->dwTypeID
))
8023 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8027 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8029 /* Set the Length of the OleTypeName */
8030 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8031 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8033 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8038 if(pData
->dwOleTypeNameLength
> 0)
8040 /* Set the OleTypeName */
8041 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8042 if(dwSize
!= pData
->dwOleTypeNameLength
)
8044 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8051 /* Set the width of the Metafile */
8052 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8053 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8055 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8061 /* Set the height of the Metafile */
8062 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8063 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8065 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8071 /* Set the length of the Data */
8072 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8073 if(dwSize
!= sizeof(pData
->dwDataLength
))
8075 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8081 if(pData
->dwDataLength
> 0)
8083 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8084 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8085 if(dwSize
!= pData
->dwDataLength
)
8087 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8095 /*************************************************************************
8096 * OLECONVERT_GetOLE20FromOLE10[Internal]
8098 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8099 * opens it, and copies the content to the dest IStorage for
8100 * OleConvertOLESTREAMToIStorage
8104 * pDestStorage [I] The IStorage to copy the data to
8105 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8106 * nBufferLength [I] The size of the buffer
8115 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8119 IStorage
*pTempStorage
;
8120 DWORD dwNumOfBytesWritten
;
8121 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8122 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8124 /* Create a temp File */
8125 GetTempPathW(MAX_PATH
, wstrTempDir
);
8126 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8127 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8129 if(hFile
!= INVALID_HANDLE_VALUE
)
8131 /* Write IStorage Data to File */
8132 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8135 /* Open and copy temp storage to the Dest Storage */
8136 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8139 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8140 IStorage_Release(pTempStorage
);
8142 DeleteFileW(wstrTempFile
);
8147 /*************************************************************************
8148 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8150 * Saves the OLE10 STREAM From memory
8153 * pStorage [I] The Src IStorage to copy
8154 * pData [I] The Dest Memory to write to.
8157 * The size in bytes allocated for pData
8160 * Memory allocated for pData must be freed by the caller
8162 * Used by OleConvertIStorageToOLESTREAM only.
8165 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8169 DWORD nDataLength
= 0;
8170 IStorage
*pTempStorage
;
8171 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8172 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8176 /* Create temp Storage */
8177 GetTempPathW(MAX_PATH
, wstrTempDir
);
8178 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8179 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8183 /* Copy Src Storage to the Temp Storage */
8184 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8185 IStorage_Release(pTempStorage
);
8187 /* Open Temp Storage as a file and copy to memory */
8188 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8189 if(hFile
!= INVALID_HANDLE_VALUE
)
8191 nDataLength
= GetFileSize(hFile
, NULL
);
8192 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8193 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8196 DeleteFileW(wstrTempFile
);
8201 /*************************************************************************
8202 * OLECONVERT_CreateOleStream [Internal]
8204 * Creates the "\001OLE" stream in the IStorage if necessary.
8207 * pStorage [I] Dest storage to create the stream in
8213 * This function is used by OleConvertOLESTREAMToIStorage only.
8215 * This stream is still unknown, MS Word seems to have extra data
8216 * but since the data is stored in the OLESTREAM there should be
8217 * no need to recreate the stream. If the stream is manually
8218 * deleted it will create it with this default data.
8221 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8225 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8226 BYTE pOleStreamHeader
[] =
8228 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8230 0x00, 0x00, 0x00, 0x00
8233 /* Create stream if not present */
8234 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8235 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8239 /* Write default Data */
8240 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8241 IStream_Release(pStream
);
8245 /* write a string to a stream, preceded by its length */
8246 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8253 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8254 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8259 str
= CoTaskMemAlloc( len
);
8260 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8261 r
= IStream_Write( stm
, str
, len
, NULL
);
8262 CoTaskMemFree( str
);
8266 /* read a string preceded by its length from a stream */
8267 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8270 DWORD len
, count
= 0;
8274 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8277 if( count
!= sizeof(len
) )
8278 return E_OUTOFMEMORY
;
8280 TRACE("%d bytes\n",len
);
8282 str
= CoTaskMemAlloc( len
);
8284 return E_OUTOFMEMORY
;
8286 r
= IStream_Read( stm
, str
, len
, &count
);
8291 CoTaskMemFree( str
);
8292 return E_OUTOFMEMORY
;
8295 TRACE("Read string %s\n",debugstr_an(str
,len
));
8297 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8298 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8300 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8301 CoTaskMemFree( str
);
8309 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8310 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8314 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8316 static const BYTE unknown1
[12] =
8317 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8318 0xFF, 0xFF, 0xFF, 0xFF};
8319 static const BYTE unknown2
[16] =
8320 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8323 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8324 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8325 debugstr_w(szProgIDName
));
8327 /* Create a CompObj stream */
8328 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8329 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8333 /* Write CompObj Structure to stream */
8334 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8336 if( SUCCEEDED( r
) )
8337 r
= WriteClassStm( pstm
, clsid
);
8339 if( SUCCEEDED( r
) )
8340 r
= STREAM_WriteString( pstm
, lpszUserType
);
8341 if( SUCCEEDED( r
) )
8342 r
= STREAM_WriteString( pstm
, szClipName
);
8343 if( SUCCEEDED( r
) )
8344 r
= STREAM_WriteString( pstm
, szProgIDName
);
8345 if( SUCCEEDED( r
) )
8346 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8348 IStream_Release( pstm
);
8353 /***********************************************************************
8354 * WriteFmtUserTypeStg (OLE32.@)
8356 HRESULT WINAPI
WriteFmtUserTypeStg(
8357 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8360 WCHAR szwClipName
[0x40];
8361 CLSID clsid
= CLSID_NULL
;
8362 LPWSTR wstrProgID
= NULL
;
8365 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8367 /* get the clipboard format name */
8368 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8371 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8373 /* FIXME: There's room to save a CLSID and its ProgID, but
8374 the CLSID is not looked up in the registry and in all the
8375 tests I wrote it was CLSID_NULL. Where does it come from?
8378 /* get the real program ID. This may fail, but that's fine */
8379 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8381 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8383 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8384 lpszUserType
, szwClipName
, wstrProgID
);
8386 CoTaskMemFree(wstrProgID
);
8392 /******************************************************************************
8393 * ReadFmtUserTypeStg [OLE32.@]
8395 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8399 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8400 unsigned char unknown1
[12];
8401 unsigned char unknown2
[16];
8403 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8406 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8408 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8409 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8412 WARN("Failed to open stream r = %08x\n", r
);
8416 /* read the various parts of the structure */
8417 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8418 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8420 r
= ReadClassStm( stm
, &clsid
);
8424 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8428 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8432 r
= STREAM_ReadString( stm
, &szProgIDName
);
8436 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8437 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8440 /* ok, success... now we just need to store what we found */
8442 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8443 CoTaskMemFree( szOleTypeName
);
8445 if( lplpszUserType
)
8446 *lplpszUserType
= szCLSIDName
;
8447 CoTaskMemFree( szProgIDName
);
8450 IStream_Release( stm
);
8456 /*************************************************************************
8457 * OLECONVERT_CreateCompObjStream [Internal]
8459 * Creates a "\001CompObj" is the destination IStorage if necessary.
8462 * pStorage [I] The dest IStorage to create the CompObj Stream
8464 * strOleTypeName [I] The ProgID
8468 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8471 * This function is used by OleConvertOLESTREAMToIStorage only.
8473 * The stream data is stored in the OLESTREAM and there should be
8474 * no need to recreate the stream. If the stream is manually
8475 * deleted it will attempt to create it by querying the registry.
8479 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8482 HRESULT hStorageRes
, hRes
= S_OK
;
8483 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8484 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8485 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8487 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8488 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8490 /* Initialize the CompObj structure */
8491 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8492 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8493 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8496 /* Create a CompObj stream if it doesn't exist */
8497 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8498 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8499 if(hStorageRes
== S_OK
)
8501 /* copy the OleTypeName to the compobj struct */
8502 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8503 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8505 /* copy the OleTypeName to the compobj struct */
8506 /* Note: in the test made, these were Identical */
8507 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8508 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8511 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8512 bufferW
, OLESTREAM_MAX_STR_LEN
);
8513 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8519 /* Get the CLSID Default Name from the Registry */
8520 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8521 if(hErr
== ERROR_SUCCESS
)
8523 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8524 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8525 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8526 if(hErr
== ERROR_SUCCESS
)
8528 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8534 /* Write CompObj Structure to stream */
8535 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8537 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8539 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8540 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8542 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8544 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8545 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8547 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8549 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8550 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8552 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8554 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8555 IStream_Release(pStream
);
8561 /*************************************************************************
8562 * OLECONVERT_CreateOlePresStream[Internal]
8564 * Creates the "\002OlePres000" Stream with the Metafile data
8567 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8568 * dwExtentX [I] Width of the Metafile
8569 * dwExtentY [I] Height of the Metafile
8570 * pData [I] Metafile data
8571 * dwDataLength [I] Size of the Metafile data
8575 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8578 * This function is used by OleConvertOLESTREAMToIStorage only.
8581 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8585 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8586 BYTE pOlePresStreamHeader
[] =
8588 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8589 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8590 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8591 0x00, 0x00, 0x00, 0x00
8594 BYTE pOlePresStreamHeaderEmpty
[] =
8596 0x00, 0x00, 0x00, 0x00,
8597 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8598 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8599 0x00, 0x00, 0x00, 0x00
8602 /* Create the OlePres000 Stream */
8603 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8604 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8609 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8611 memset(&OlePres
, 0, sizeof(OlePres
));
8612 /* Do we have any metafile data to save */
8613 if(dwDataLength
> 0)
8615 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8616 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8620 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8621 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8623 /* Set width and height of the metafile */
8624 OlePres
.dwExtentX
= dwExtentX
;
8625 OlePres
.dwExtentY
= -dwExtentY
;
8627 /* Set Data and Length */
8628 if(dwDataLength
> sizeof(METAFILEPICT16
))
8630 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8631 OlePres
.pData
= &(pData
[8]);
8633 /* Save OlePres000 Data to Stream */
8634 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8635 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8636 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8637 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8638 if(OlePres
.dwSize
> 0)
8640 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8642 IStream_Release(pStream
);
8646 /*************************************************************************
8647 * OLECONVERT_CreateOle10NativeStream [Internal]
8649 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8652 * pStorage [I] Dest storage to create the stream in
8653 * pData [I] Ole10 Native Data (ex. bmp)
8654 * dwDataLength [I] Size of the Ole10 Native Data
8660 * This function is used by OleConvertOLESTREAMToIStorage only.
8662 * Might need to verify the data and return appropriate error message
8665 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8669 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8671 /* Create the Ole10Native Stream */
8672 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8673 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8677 /* Write info to stream */
8678 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8679 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8680 IStream_Release(pStream
);
8685 /*************************************************************************
8686 * OLECONVERT_GetOLE10ProgID [Internal]
8688 * Finds the ProgID (or OleTypeID) from the IStorage
8691 * pStorage [I] The Src IStorage to get the ProgID
8692 * strProgID [I] the ProgID string to get
8693 * dwSize [I] the size of the string
8697 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8700 * This function is used by OleConvertIStorageToOLESTREAM only.
8704 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8708 LARGE_INTEGER iSeekPos
;
8709 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8710 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8712 /* Open the CompObj Stream */
8713 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8714 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8718 /*Get the OleType from the CompObj Stream */
8719 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8720 iSeekPos
.u
.HighPart
= 0;
8722 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8723 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8724 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8725 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8726 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8727 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8728 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8730 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8733 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8735 IStream_Release(pStream
);
8740 LPOLESTR wstrProgID
;
8742 /* Get the OleType from the registry */
8743 REFCLSID clsid
= &(stat
.clsid
);
8744 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8745 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8748 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8755 /*************************************************************************
8756 * OLECONVERT_GetOle10PresData [Internal]
8758 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8761 * pStorage [I] Src IStroage
8762 * pOleStream [I] Dest OleStream Mem Struct
8768 * This function is used by OleConvertIStorageToOLESTREAM only.
8770 * Memory allocated for pData must be freed by the caller
8774 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8779 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8781 /* Initialize Default data for OLESTREAM */
8782 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8783 pOleStreamData
[0].dwTypeID
= 2;
8784 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8785 pOleStreamData
[1].dwTypeID
= 0;
8786 pOleStreamData
[0].dwMetaFileWidth
= 0;
8787 pOleStreamData
[0].dwMetaFileHeight
= 0;
8788 pOleStreamData
[0].pData
= NULL
;
8789 pOleStreamData
[1].pData
= NULL
;
8791 /* Open Ole10Native Stream */
8792 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8793 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8797 /* Read Size and Data */
8798 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8799 if(pOleStreamData
->dwDataLength
> 0)
8801 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8802 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8804 IStream_Release(pStream
);
8810 /*************************************************************************
8811 * OLECONVERT_GetOle20PresData[Internal]
8813 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8816 * pStorage [I] Src IStroage
8817 * pOleStreamData [I] Dest OleStream Mem Struct
8823 * This function is used by OleConvertIStorageToOLESTREAM only.
8825 * Memory allocated for pData must be freed by the caller
8827 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8831 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8832 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8834 /* Initialize Default data for OLESTREAM */
8835 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8836 pOleStreamData
[0].dwTypeID
= 2;
8837 pOleStreamData
[0].dwMetaFileWidth
= 0;
8838 pOleStreamData
[0].dwMetaFileHeight
= 0;
8839 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8840 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8841 pOleStreamData
[1].dwTypeID
= 0;
8842 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8843 pOleStreamData
[1].strOleTypeName
[0] = 0;
8844 pOleStreamData
[1].dwMetaFileWidth
= 0;
8845 pOleStreamData
[1].dwMetaFileHeight
= 0;
8846 pOleStreamData
[1].pData
= NULL
;
8847 pOleStreamData
[1].dwDataLength
= 0;
8850 /* Open OlePress000 stream */
8851 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8852 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8855 LARGE_INTEGER iSeekPos
;
8856 METAFILEPICT16 MetaFilePict
;
8857 static const char strMetafilePictName
[] = "METAFILEPICT";
8859 /* Set the TypeID for a Metafile */
8860 pOleStreamData
[1].dwTypeID
= 5;
8862 /* Set the OleTypeName to Metafile */
8863 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8864 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8866 iSeekPos
.u
.HighPart
= 0;
8867 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8869 /* Get Presentation Data */
8870 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8871 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8872 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8873 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8875 /*Set width and Height */
8876 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8877 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8878 if(olePress
.dwSize
> 0)
8881 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8883 /* Set MetaFilePict struct */
8884 MetaFilePict
.mm
= 8;
8885 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8886 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8887 MetaFilePict
.hMF
= 0;
8889 /* Get Metafile Data */
8890 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8891 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8892 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8894 IStream_Release(pStream
);
8898 /*************************************************************************
8899 * OleConvertOLESTREAMToIStorage [OLE32.@]
8904 * DVTARGETDEVICE parameter is not handled
8905 * Still unsure of some mem fields for OLE 10 Stream
8906 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8907 * and "\001OLE" streams
8910 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8911 LPOLESTREAM pOleStream
,
8913 const DVTARGETDEVICE
* ptd
)
8917 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8919 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8921 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8925 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8928 if(pstg
== NULL
|| pOleStream
== NULL
)
8930 hRes
= E_INVALIDARG
;
8935 /* Load the OLESTREAM to Memory */
8936 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8941 /* Load the OLESTREAM to Memory (part 2)*/
8942 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8948 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8950 /* Do we have the IStorage Data in the OLESTREAM */
8951 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8953 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8954 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8958 /* It must be an original OLE 1.0 source */
8959 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8964 /* It must be an original OLE 1.0 source */
8965 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8968 /* Create CompObj Stream if necessary */
8969 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8972 /*Create the Ole Stream if necessary */
8973 OLECONVERT_CreateOleStream(pstg
);
8978 /* Free allocated memory */
8979 for(i
=0; i
< 2; i
++)
8981 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8982 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8983 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8988 /*************************************************************************
8989 * OleConvertIStorageToOLESTREAM [OLE32.@]
8996 * Still unsure of some mem fields for OLE 10 Stream
8997 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8998 * and "\001OLE" streams.
9001 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9003 LPOLESTREAM pOleStream
)
9006 HRESULT hRes
= S_OK
;
9008 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9009 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9011 TRACE("%p %p\n", pstg
, pOleStream
);
9013 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9015 if(pstg
== NULL
|| pOleStream
== NULL
)
9017 hRes
= E_INVALIDARG
;
9021 /* Get the ProgID */
9022 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9023 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9027 /* Was it originally Ole10 */
9028 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9031 IStream_Release(pStream
);
9032 /* Get Presentation Data for Ole10Native */
9033 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9037 /* Get Presentation Data (OLE20) */
9038 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9041 /* Save OLESTREAM */
9042 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9045 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9050 /* Free allocated memory */
9051 for(i
=0; i
< 2; i
++)
9053 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9059 /***********************************************************************
9060 * GetConvertStg (OLE32.@)
9062 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
9063 FIXME("unimplemented stub!\n");
9067 /******************************************************************************
9068 * StgIsStorageFile [OLE32.@]
9069 * Verify if the file contains a storage object
9075 * S_OK if file has magic bytes as a storage object
9076 * S_FALSE if file is not storage
9079 StgIsStorageFile(LPCOLESTR fn
)
9085 TRACE("%s\n", debugstr_w(fn
));
9086 hf
= CreateFileW(fn
, GENERIC_READ
,
9087 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9088 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9090 if (hf
== INVALID_HANDLE_VALUE
)
9091 return STG_E_FILENOTFOUND
;
9093 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9095 WARN(" unable to read file\n");
9102 if (bytes_read
!= 8) {
9103 TRACE(" too short\n");
9107 if (!memcmp(magic
,STORAGE_magic
,8)) {
9112 TRACE(" -> Invalid header.\n");
9116 /***********************************************************************
9117 * WriteClassStm (OLE32.@)
9119 * Writes a CLSID to a stream.
9122 * pStm [I] Stream to write to.
9123 * rclsid [I] CLSID to write.
9127 * Failure: HRESULT code.
9129 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9131 TRACE("(%p,%p)\n",pStm
,rclsid
);
9133 if (!pStm
|| !rclsid
)
9134 return E_INVALIDARG
;
9136 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9139 /***********************************************************************
9140 * ReadClassStm (OLE32.@)
9142 * Reads a CLSID from a stream.
9145 * pStm [I] Stream to read from.
9146 * rclsid [O] CLSID to read.
9150 * Failure: HRESULT code.
9152 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9157 TRACE("(%p,%p)\n",pStm
,pclsid
);
9159 if (!pStm
|| !pclsid
)
9160 return E_INVALIDARG
;
9162 /* clear the output args */
9163 *pclsid
= CLSID_NULL
;
9165 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9170 if (nbByte
!= sizeof(CLSID
))
9171 return STG_E_READFAULT
;