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 ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
333 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
334 ULARGE_INTEGER offset
,
339 return ILockBytes_WriteAt(This
->lockBytes
,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(
1310 StorageImpl_SaveFileHeader(storage
);
1313 UpdateRawDirEntry(currentData
, newData
);
1315 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1318 *index
= newEntryIndex
;
1323 /***************************************************************************
1327 * Mark a directory entry in the file as free.
1329 static HRESULT
StorageImpl_DestroyDirEntry(
1330 StorageBaseImpl
*base
,
1334 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1335 StorageImpl
*storage
= (StorageImpl
*)base
;
1337 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1339 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1345 /****************************************************************************
1349 * Case insensitive comparison of DirEntry.name by first considering
1352 * Returns <0 when name1 < name2
1353 * >0 when name1 > name2
1354 * 0 when name1 == name2
1356 static LONG
entryNameCmp(
1357 const OLECHAR
*name1
,
1358 const OLECHAR
*name2
)
1360 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1362 while (diff
== 0 && *name1
!= 0)
1365 * We compare the string themselves only when they are of the same length
1367 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1373 /****************************************************************************
1377 * Add a directory entry to a storage
1379 static HRESULT
insertIntoTree(
1380 StorageBaseImpl
*This
,
1381 DirRef parentStorageIndex
,
1382 DirRef newEntryIndex
)
1384 DirEntry currentEntry
;
1388 * Read the inserted entry
1390 StorageBaseImpl_ReadDirEntry(This
,
1395 * Read the storage entry
1397 StorageBaseImpl_ReadDirEntry(This
,
1401 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1404 * The root storage contains some element, therefore, start the research
1405 * for the appropriate location.
1408 DirRef current
, next
, previous
, currentEntryId
;
1411 * Keep a reference to the root of the storage's element tree
1413 currentEntryId
= currentEntry
.dirRootEntry
;
1418 StorageBaseImpl_ReadDirEntry(This
,
1419 currentEntry
.dirRootEntry
,
1422 previous
= currentEntry
.leftChild
;
1423 next
= currentEntry
.rightChild
;
1424 current
= currentEntryId
;
1428 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1432 if (previous
!= DIRENTRY_NULL
)
1434 StorageBaseImpl_ReadDirEntry(This
,
1441 currentEntry
.leftChild
= newEntryIndex
;
1442 StorageBaseImpl_WriteDirEntry(This
,
1450 if (next
!= DIRENTRY_NULL
)
1452 StorageBaseImpl_ReadDirEntry(This
,
1459 currentEntry
.rightChild
= newEntryIndex
;
1460 StorageBaseImpl_WriteDirEntry(This
,
1469 * Trying to insert an item with the same name in the
1470 * subtree structure.
1472 return STG_E_FILEALREADYEXISTS
;
1475 previous
= currentEntry
.leftChild
;
1476 next
= currentEntry
.rightChild
;
1482 * The storage is empty, make the new entry the root of its element tree
1484 currentEntry
.dirRootEntry
= newEntryIndex
;
1485 StorageBaseImpl_WriteDirEntry(This
,
1493 /****************************************************************************
1497 * Find and read the element of a storage with the given name.
1499 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1500 const OLECHAR
*name
, DirEntry
*data
)
1502 DirRef currentEntry
;
1504 /* Read the storage entry to find the root of the tree. */
1505 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1507 currentEntry
= data
->dirRootEntry
;
1509 while (currentEntry
!= DIRENTRY_NULL
)
1513 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1515 cmp
= entryNameCmp(name
, data
->name
);
1522 currentEntry
= data
->leftChild
;
1525 currentEntry
= data
->rightChild
;
1528 return currentEntry
;
1531 /****************************************************************************
1535 * Find and read the binary tree parent of the element with the given name.
1537 * If there is no such element, find a place where it could be inserted and
1538 * return STG_E_FILENOTFOUND.
1540 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1541 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1547 /* Read the storage entry to find the root of the tree. */
1548 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1550 *parentEntry
= storageEntry
;
1551 *relation
= DIRENTRY_RELATION_DIR
;
1553 childEntry
= parentData
->dirRootEntry
;
1555 while (childEntry
!= DIRENTRY_NULL
)
1559 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1561 cmp
= entryNameCmp(childName
, childData
.name
);
1569 *parentData
= childData
;
1570 *parentEntry
= childEntry
;
1571 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1573 childEntry
= parentData
->leftChild
;
1578 *parentData
= childData
;
1579 *parentEntry
= childEntry
;
1580 *relation
= DIRENTRY_RELATION_NEXT
;
1582 childEntry
= parentData
->rightChild
;
1586 if (childEntry
== DIRENTRY_NULL
)
1587 return STG_E_FILENOTFOUND
;
1593 /*************************************************************************
1596 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1598 DWORD ciidExclude
, /* [in] */
1599 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1600 SNB snbExclude
, /* [unique][in] */
1601 IStorage
* pstgDest
) /* [unique][in] */
1603 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1605 IEnumSTATSTG
*elements
= 0;
1606 STATSTG curElement
, strStat
;
1608 IStorage
*pstgTmp
, *pstgChild
;
1609 IStream
*pstrTmp
, *pstrChild
;
1612 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1615 TRACE("(%p, %d, %p, %p, %p)\n",
1616 iface
, ciidExclude
, rgiidExclude
,
1617 snbExclude
, pstgDest
);
1619 if ( pstgDest
== 0 )
1620 return STG_E_INVALIDPOINTER
;
1623 * Enumerate the elements
1625 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1633 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1634 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1636 for(i
= 0; i
< ciidExclude
; ++i
)
1638 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1639 skip_storage
= TRUE
;
1640 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1643 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1649 * Obtain the next element
1651 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1653 if ( hr
== S_FALSE
)
1655 hr
= S_OK
; /* done, every element has been copied */
1661 WCHAR
**snb
= snbExclude
;
1663 while ( *snb
!= NULL
&& !skip
)
1665 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1674 if (curElement
.type
== STGTY_STORAGE
)
1680 * open child source storage
1682 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1683 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1684 NULL
, 0, &pstgChild
);
1690 * create a new storage in destination storage
1692 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1693 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1697 * if it already exist, don't create a new one use this one
1699 if (hr
== STG_E_FILEALREADYEXISTS
)
1701 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1702 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1703 NULL
, 0, &pstgTmp
);
1709 * do the copy recursively
1711 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1714 IStorage_Release( pstgTmp
);
1717 IStorage_Release( pstgChild
);
1719 else if (curElement
.type
== STGTY_STREAM
)
1725 * create a new stream in destination storage. If the stream already
1726 * exist, it will be deleted and a new one will be created.
1728 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1729 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1736 * open child stream storage. This operation must succeed even if the
1737 * stream is already open, so we use internal functions to do it.
1739 srcEntryRef
= findElement( This
, This
->storageDirEntry
, curElement
.pwcsName
,
1743 ERR("source stream not found\n");
1744 hr
= STG_E_DOCFILECORRUPT
;
1749 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntryRef
);
1751 IStream_AddRef(pstrChild
);
1759 * Get the size of the source stream
1761 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1764 * Set the size of the destination stream.
1766 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1771 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1774 IStream_Release( pstrChild
);
1777 IStream_Release( pstrTmp
);
1781 WARN("unknown element type: %d\n", curElement
.type
);
1785 CoTaskMemFree(curElement
.pwcsName
);
1786 } while (hr
== S_OK
);
1791 IEnumSTATSTG_Release(elements
);
1796 /*************************************************************************
1797 * MoveElementTo (IStorage)
1799 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1801 const OLECHAR
*pwcsName
, /* [string][in] */
1802 IStorage
*pstgDest
, /* [unique][in] */
1803 const OLECHAR
*pwcsNewName
,/* [string][in] */
1804 DWORD grfFlags
) /* [in] */
1806 FIXME("(%p %s %p %s %u): stub\n", iface
,
1807 debugstr_w(pwcsName
), pstgDest
,
1808 debugstr_w(pwcsNewName
), grfFlags
);
1812 /*************************************************************************
1815 * Ensures that any changes made to a storage object open in transacted mode
1816 * are reflected in the parent storage
1819 * Wine doesn't implement transacted mode, which seems to be a basic
1820 * optimization, so we can ignore this stub for now.
1822 static HRESULT WINAPI
StorageImpl_Commit(
1824 DWORD grfCommitFlags
)/* [in] */
1826 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1830 /*************************************************************************
1833 * Discard all changes that have been made since the last commit operation
1835 static HRESULT WINAPI
StorageImpl_Revert(
1838 TRACE("(%p)\n", iface
);
1842 /*************************************************************************
1843 * DestroyElement (IStorage)
1845 * Strategy: This implementation is built this way for simplicity not for speed.
1846 * I always delete the topmost element of the enumeration and adjust
1847 * the deleted element pointer all the time. This takes longer to
1848 * do but allow to reinvoke DestroyElement whenever we encounter a
1849 * storage object. The optimisation resides in the usage of another
1850 * enumeration strategy that would give all the leaves of a storage
1851 * first. (postfix order)
1853 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1855 const OLECHAR
*pwcsName
)/* [string][in] */
1857 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1860 DirEntry entryToDelete
;
1861 DirRef entryToDeleteRef
;
1864 iface
, debugstr_w(pwcsName
));
1867 return STG_E_INVALIDPOINTER
;
1870 return STG_E_REVERTED
;
1872 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1873 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1874 return STG_E_ACCESSDENIED
;
1876 entryToDeleteRef
= findElement(
1878 This
->storageDirEntry
,
1882 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1884 return STG_E_FILENOTFOUND
;
1887 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1889 hr
= deleteStorageContents(
1894 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1896 hr
= deleteStreamContents(
1906 * Remove the entry from its parent storage
1908 hr
= removeFromTree(
1910 This
->storageDirEntry
,
1914 * Invalidate the entry
1917 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1923 /******************************************************************************
1924 * Internal stream list handlers
1927 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1929 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1930 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1933 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1935 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1936 list_remove(&(strm
->StrmListEntry
));
1939 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1941 StgStreamImpl
*strm
;
1943 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1945 if (strm
->dirEntry
== streamEntry
)
1954 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1956 StorageInternalImpl
*childstg
;
1958 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1960 if (childstg
->base
.storageDirEntry
== storageEntry
)
1969 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1971 struct list
*cur
, *cur2
;
1972 StgStreamImpl
*strm
=NULL
;
1973 StorageInternalImpl
*childstg
=NULL
;
1975 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1976 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1977 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1978 strm
->parentStorage
= NULL
;
1982 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1983 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1984 StorageBaseImpl_Invalidate( &childstg
->base
);
1987 if (stg
->transactedChild
)
1989 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1991 stg
->transactedChild
= NULL
;
1996 /*********************************************************************
2000 * Delete the contents of a storage entry.
2003 static HRESULT
deleteStorageContents(
2004 StorageBaseImpl
*parentStorage
,
2005 DirRef indexToDelete
,
2006 DirEntry entryDataToDelete
)
2008 IEnumSTATSTG
*elements
= 0;
2009 IStorage
*childStorage
= 0;
2010 STATSTG currentElement
;
2012 HRESULT destroyHr
= S_OK
;
2013 StorageInternalImpl
*stg
, *stg2
;
2015 /* Invalidate any open storage objects. */
2016 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2018 if (stg
->base
.storageDirEntry
== indexToDelete
)
2020 StorageBaseImpl_Invalidate(&stg
->base
);
2025 * Open the storage and enumerate it
2027 hr
= StorageBaseImpl_OpenStorage(
2028 (IStorage
*)parentStorage
,
2029 entryDataToDelete
.name
,
2031 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2042 * Enumerate the elements
2044 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2049 * Obtain the next element
2051 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2054 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2056 CoTaskMemFree(currentElement
.pwcsName
);
2060 * We need to Reset the enumeration every time because we delete elements
2061 * and the enumeration could be invalid
2063 IEnumSTATSTG_Reset(elements
);
2065 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2067 IStorage_Release(childStorage
);
2068 IEnumSTATSTG_Release(elements
);
2073 /*********************************************************************
2077 * Perform the deletion of a stream's data
2080 static HRESULT
deleteStreamContents(
2081 StorageBaseImpl
*parentStorage
,
2082 DirRef indexToDelete
,
2083 DirEntry entryDataToDelete
)
2087 ULARGE_INTEGER size
;
2088 StgStreamImpl
*strm
, *strm2
;
2090 /* Invalidate any open stream objects. */
2091 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2093 if (strm
->dirEntry
== indexToDelete
)
2095 TRACE("Stream deleted %p\n", strm
);
2096 strm
->parentStorage
= NULL
;
2097 list_remove(&strm
->StrmListEntry
);
2101 size
.u
.HighPart
= 0;
2104 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2105 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2115 hr
= IStream_SetSize(pis
, size
);
2123 * Release the stream object.
2125 IStream_Release(pis
);
2130 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2134 case DIRENTRY_RELATION_PREVIOUS
:
2135 entry
->leftChild
= new_target
;
2137 case DIRENTRY_RELATION_NEXT
:
2138 entry
->rightChild
= new_target
;
2140 case DIRENTRY_RELATION_DIR
:
2141 entry
->dirRootEntry
= new_target
;
2148 /*************************************************************************
2152 * This method removes a directory entry from its parent storage tree without
2153 * freeing any resources attached to it.
2155 static HRESULT
removeFromTree(
2156 StorageBaseImpl
*This
,
2157 DirRef parentStorageIndex
,
2158 DirRef deletedIndex
)
2161 DirEntry entryToDelete
;
2162 DirEntry parentEntry
;
2163 DirRef parentEntryRef
;
2164 ULONG typeOfRelation
;
2166 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2172 * Find the element that links to the one we want to delete.
2174 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2175 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2180 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2183 * Replace the deleted entry with its left child
2185 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2187 hr
= StorageBaseImpl_WriteDirEntry(
2196 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2199 * We need to reinsert the right child somewhere. We already know it and
2200 * its children are greater than everything in the left tree, so we
2201 * insert it at the rightmost point in the left tree.
2203 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2204 DirEntry newRightChildParentEntry
;
2208 hr
= StorageBaseImpl_ReadDirEntry(
2210 newRightChildParent
,
2211 &newRightChildParentEntry
);
2217 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2218 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2219 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2221 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2223 hr
= StorageBaseImpl_WriteDirEntry(
2225 newRightChildParent
,
2226 &newRightChildParentEntry
);
2236 * Replace the deleted entry with its right child
2238 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2240 hr
= StorageBaseImpl_WriteDirEntry(
2254 /******************************************************************************
2255 * SetElementTimes (IStorage)
2257 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2259 const OLECHAR
*pwcsName
,/* [string][in] */
2260 const FILETIME
*pctime
, /* [in] */
2261 const FILETIME
*patime
, /* [in] */
2262 const FILETIME
*pmtime
) /* [in] */
2264 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2268 /******************************************************************************
2269 * SetStateBits (IStorage)
2271 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2273 DWORD grfStateBits
,/* [in] */
2274 DWORD grfMask
) /* [in] */
2276 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2279 return STG_E_REVERTED
;
2281 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2285 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2286 DirRef index
, const DirEntry
*data
)
2288 StorageImpl
*This
= (StorageImpl
*)base
;
2289 return StorageImpl_WriteDirEntry(This
, index
, data
);
2292 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2293 DirRef index
, DirEntry
*data
)
2295 StorageImpl
*This
= (StorageImpl
*)base
;
2296 return StorageImpl_ReadDirEntry(This
, index
, data
);
2299 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2303 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2305 if (!This
->blockChainCache
[i
])
2307 return &This
->blockChainCache
[i
];
2311 i
= This
->blockChainToEvict
;
2313 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2314 This
->blockChainCache
[i
] = NULL
;
2316 This
->blockChainToEvict
++;
2317 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2318 This
->blockChainToEvict
= 0;
2320 return &This
->blockChainCache
[i
];
2323 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2326 int i
, free_index
=-1;
2328 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2330 if (!This
->blockChainCache
[i
])
2332 if (free_index
== -1) free_index
= i
;
2334 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2336 return &This
->blockChainCache
[i
];
2340 if (free_index
== -1)
2342 free_index
= This
->blockChainToEvict
;
2344 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2345 This
->blockChainCache
[free_index
] = NULL
;
2347 This
->blockChainToEvict
++;
2348 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2349 This
->blockChainToEvict
= 0;
2352 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2353 return &This
->blockChainCache
[free_index
];
2356 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2360 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2362 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2364 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2365 This
->blockChainCache
[i
] = NULL
;
2371 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2372 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2374 StorageImpl
*This
= (StorageImpl
*)base
;
2379 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2380 if (FAILED(hr
)) return hr
;
2382 if (data
.size
.QuadPart
== 0)
2388 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2390 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2397 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2399 SmallBlockChainStream
*stream
;
2401 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2402 if (!stream
) return E_OUTOFMEMORY
;
2404 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2406 SmallBlockChainStream_Destroy(stream
);
2412 BlockChainStream
*stream
= NULL
;
2414 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2415 if (!stream
) return E_OUTOFMEMORY
;
2417 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2423 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2424 ULARGE_INTEGER newsize
)
2426 StorageImpl
*This
= (StorageImpl
*)base
;
2429 SmallBlockChainStream
*smallblock
=NULL
;
2430 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2432 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2433 if (FAILED(hr
)) return hr
;
2435 /* In simple mode keep the stream size above the small block limit */
2436 if (This
->base
.openFlags
& STGM_SIMPLE
)
2437 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2439 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2442 /* Create a block chain object of the appropriate type */
2443 if (data
.size
.QuadPart
== 0)
2445 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2447 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2448 if (!smallblock
) return E_OUTOFMEMORY
;
2452 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2453 bigblock
= *pbigblock
;
2454 if (!bigblock
) return E_OUTOFMEMORY
;
2457 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2459 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2460 if (!smallblock
) return E_OUTOFMEMORY
;
2464 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2465 bigblock
= *pbigblock
;
2466 if (!bigblock
) return E_OUTOFMEMORY
;
2469 /* Change the block chain type if necessary. */
2470 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2472 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2475 SmallBlockChainStream_Destroy(smallblock
);
2479 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2480 *pbigblock
= bigblock
;
2482 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2484 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2489 /* Set the size of the block chain. */
2492 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2493 SmallBlockChainStream_Destroy(smallblock
);
2497 BlockChainStream_SetSize(bigblock
, newsize
);
2500 /* Set the size in the directory entry. */
2501 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2504 data
.size
= newsize
;
2506 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2511 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2512 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2514 StorageImpl
*This
= (StorageImpl
*)base
;
2517 ULARGE_INTEGER newSize
;
2519 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2520 if (FAILED(hr
)) return hr
;
2522 /* Grow the stream if necessary */
2523 newSize
.QuadPart
= 0;
2524 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2526 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2528 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2532 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2533 if (FAILED(hr
)) return hr
;
2536 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2538 SmallBlockChainStream
*stream
;
2540 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2541 if (!stream
) return E_OUTOFMEMORY
;
2543 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2545 SmallBlockChainStream_Destroy(stream
);
2551 BlockChainStream
*stream
;
2553 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2554 if (!stream
) return E_OUTOFMEMORY
;
2556 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2562 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2565 StorageImpl
*This
= (StorageImpl
*)base
;
2566 DirEntry dst_data
, src_data
;
2569 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2572 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2576 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2577 dst_data
.startingBlock
= src_data
.startingBlock
;
2578 dst_data
.size
= src_data
.size
;
2580 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2586 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2588 StorageImpl
*This
= (StorageImpl
*) iface
;
2592 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2594 *result
= statstg
.pwcsName
;
2600 * Virtual function table for the IStorage32Impl class.
2602 static const IStorageVtbl Storage32Impl_Vtbl
=
2604 StorageBaseImpl_QueryInterface
,
2605 StorageBaseImpl_AddRef
,
2606 StorageBaseImpl_Release
,
2607 StorageBaseImpl_CreateStream
,
2608 StorageBaseImpl_OpenStream
,
2609 StorageBaseImpl_CreateStorage
,
2610 StorageBaseImpl_OpenStorage
,
2611 StorageBaseImpl_CopyTo
,
2612 StorageBaseImpl_MoveElementTo
,
2615 StorageBaseImpl_EnumElements
,
2616 StorageBaseImpl_DestroyElement
,
2617 StorageBaseImpl_RenameElement
,
2618 StorageBaseImpl_SetElementTimes
,
2619 StorageBaseImpl_SetClass
,
2620 StorageBaseImpl_SetStateBits
,
2621 StorageBaseImpl_Stat
2624 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2626 StorageImpl_Destroy
,
2627 StorageImpl_Invalidate
,
2628 StorageImpl_GetFilename
,
2629 StorageImpl_CreateDirEntry
,
2630 StorageImpl_BaseWriteDirEntry
,
2631 StorageImpl_BaseReadDirEntry
,
2632 StorageImpl_DestroyDirEntry
,
2633 StorageImpl_StreamReadAt
,
2634 StorageImpl_StreamWriteAt
,
2635 StorageImpl_StreamSetSize
,
2636 StorageImpl_StreamLink
2639 static HRESULT
StorageImpl_Construct(
2647 StorageImpl
** result
)
2651 DirEntry currentEntry
;
2652 DirRef currentEntryRef
;
2654 if ( FAILED( validateSTGM(openFlags
) ))
2655 return STG_E_INVALIDFLAG
;
2657 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2659 return E_OUTOFMEMORY
;
2661 memset(This
, 0, sizeof(StorageImpl
));
2663 list_init(&This
->base
.strmHead
);
2665 list_init(&This
->base
.storageHead
);
2667 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2668 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2669 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2670 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2672 This
->base
.create
= create
;
2674 This
->base
.reverted
= 0;
2676 This
->hFile
= hFile
;
2679 * Initialize the big block cache.
2681 This
->bigBlockSize
= sector_size
;
2682 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2684 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2687 This
->lockBytes
= pLkbyt
;
2688 ILockBytes_AddRef(pLkbyt
);
2696 ULARGE_INTEGER size
;
2697 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2700 * Initialize all header variables:
2701 * - The big block depot consists of one block and it is at block 0
2702 * - The directory table starts at block 1
2703 * - There is no small block depot
2705 memset( This
->bigBlockDepotStart
,
2707 sizeof(This
->bigBlockDepotStart
));
2709 This
->bigBlockDepotCount
= 1;
2710 This
->bigBlockDepotStart
[0] = 0;
2711 This
->rootStartBlock
= 1;
2712 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2713 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2714 if (sector_size
== 4096)
2715 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2717 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2718 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2719 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2720 This
->extBigBlockDepotCount
= 0;
2722 StorageImpl_SaveFileHeader(This
);
2725 * Add one block for the big block depot and one block for the directory table
2727 size
.u
.HighPart
= 0;
2728 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2729 ILockBytes_SetSize(This
->lockBytes
, size
);
2732 * Initialize the big block depot
2734 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2735 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2736 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2737 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2742 * Load the header for the file.
2744 hr
= StorageImpl_LoadFileHeader(This
);
2753 * There is no block depot cached yet.
2755 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2758 * Start searching for free blocks with block 0.
2760 This
->prevFreeBlock
= 0;
2762 This
->firstFreeSmallBlock
= 0;
2765 * Create the block chain abstractions.
2767 if(!(This
->rootBlockChain
=
2768 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2770 hr
= STG_E_READFAULT
;
2774 if(!(This
->smallBlockDepotChain
=
2775 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2778 hr
= STG_E_READFAULT
;
2783 * Write the root storage entry (memory only)
2789 * Initialize the directory table
2791 memset(&rootEntry
, 0, sizeof(rootEntry
));
2792 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2793 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2794 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2795 rootEntry
.stgType
= STGTY_ROOT
;
2796 rootEntry
.leftChild
= DIRENTRY_NULL
;
2797 rootEntry
.rightChild
= DIRENTRY_NULL
;
2798 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2799 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2800 rootEntry
.size
.u
.HighPart
= 0;
2801 rootEntry
.size
.u
.LowPart
= 0;
2803 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2807 * Find the ID of the root storage.
2809 currentEntryRef
= 0;
2813 hr
= StorageImpl_ReadDirEntry(
2820 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2821 (currentEntry
.stgType
== STGTY_ROOT
) )
2823 This
->base
.storageDirEntry
= currentEntryRef
;
2829 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2833 hr
= STG_E_READFAULT
;
2838 * Create the block chain abstraction for the small block root chain.
2840 if(!(This
->smallBlockRootChain
=
2841 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2843 hr
= STG_E_READFAULT
;
2849 IStorage_Release((IStorage
*)This
);
2858 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2860 StorageImpl
*This
= (StorageImpl
*) iface
;
2862 StorageBaseImpl_DeleteAll(&This
->base
);
2864 This
->base
.reverted
= 1;
2867 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2869 StorageImpl
*This
= (StorageImpl
*) iface
;
2871 TRACE("(%p)\n", This
);
2873 StorageImpl_Invalidate(iface
);
2875 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2876 BlockChainStream_Destroy(This
->rootBlockChain
);
2877 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2879 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2880 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2882 if (This
->lockBytes
)
2883 ILockBytes_Release(This
->lockBytes
);
2884 HeapFree(GetProcessHeap(), 0, This
);
2887 /******************************************************************************
2888 * Storage32Impl_GetNextFreeBigBlock
2890 * Returns the index of the next free big block.
2891 * If the big block depot is filled, this method will enlarge it.
2894 static ULONG
StorageImpl_GetNextFreeBigBlock(
2897 ULONG depotBlockIndexPos
;
2898 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2900 ULONG depotBlockOffset
;
2901 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2902 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2904 ULONG freeBlock
= BLOCK_UNUSED
;
2905 ULARGE_INTEGER neededSize
;
2908 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2909 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2912 * Scan the entire big block depot until we find a block marked free
2914 while (nextBlockIndex
!= BLOCK_UNUSED
)
2916 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2918 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2921 * Grow the primary depot.
2923 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2925 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2928 * Add a block depot.
2930 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2931 This
->bigBlockDepotCount
++;
2932 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2935 * Flag it as a block depot.
2937 StorageImpl_SetNextBlockInChain(This
,
2941 /* Save new header information.
2943 StorageImpl_SaveFileHeader(This
);
2948 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2950 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2953 * Grow the extended depot.
2955 ULONG extIndex
= BLOCK_UNUSED
;
2956 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2957 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2959 if (extBlockOffset
== 0)
2961 /* We need an extended block.
2963 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2964 This
->extBigBlockDepotCount
++;
2965 depotBlockIndexPos
= extIndex
+ 1;
2968 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2971 * Add a block depot and mark it in the extended block.
2973 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2974 This
->bigBlockDepotCount
++;
2975 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2977 /* Flag the block depot.
2979 StorageImpl_SetNextBlockInChain(This
,
2983 /* If necessary, flag the extended depot block.
2985 if (extIndex
!= BLOCK_UNUSED
)
2986 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2988 /* Save header information.
2990 StorageImpl_SaveFileHeader(This
);
2994 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2998 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2999 ( nextBlockIndex
!= BLOCK_UNUSED
))
3001 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3003 if (nextBlockIndex
== BLOCK_UNUSED
)
3005 freeBlock
= (depotIndex
* blocksPerDepot
) +
3006 (depotBlockOffset
/sizeof(ULONG
));
3009 depotBlockOffset
+= sizeof(ULONG
);
3014 depotBlockOffset
= 0;
3018 * make sure that the block physically exists before using it
3020 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3022 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3024 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3025 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3027 This
->prevFreeBlock
= freeBlock
;
3032 /******************************************************************************
3033 * Storage32Impl_AddBlockDepot
3035 * This will create a depot block, essentially it is a block initialized
3038 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3040 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3043 * Initialize blocks as free
3045 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3046 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3049 /******************************************************************************
3050 * Storage32Impl_GetExtDepotBlock
3052 * Returns the index of the block that corresponds to the specified depot
3053 * index. This method is only for depot indexes equal or greater than
3054 * COUNT_BBDEPOTINHEADER.
3056 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3058 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3059 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3060 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3061 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3062 ULONG blockIndex
= BLOCK_UNUSED
;
3063 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3065 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3067 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3068 return BLOCK_UNUSED
;
3070 while (extBlockCount
> 0)
3072 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3076 if (extBlockIndex
!= BLOCK_UNUSED
)
3077 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3078 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3083 /******************************************************************************
3084 * Storage32Impl_SetExtDepotBlock
3086 * Associates the specified block index to the specified depot index.
3087 * This method is only for depot indexes equal or greater than
3088 * COUNT_BBDEPOTINHEADER.
3090 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3092 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3093 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3094 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3095 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3096 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3098 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3100 while (extBlockCount
> 0)
3102 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3106 if (extBlockIndex
!= BLOCK_UNUSED
)
3108 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3109 extBlockOffset
* sizeof(ULONG
),
3114 /******************************************************************************
3115 * Storage32Impl_AddExtBlockDepot
3117 * Creates an extended depot block.
3119 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3121 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3122 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3123 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3124 ULONG index
= BLOCK_UNUSED
;
3125 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3126 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3127 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3129 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3130 blocksPerDepotBlock
;
3132 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3135 * The first extended block.
3137 This
->extBigBlockDepotStart
= index
;
3143 * Follow the chain to the last one.
3145 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3147 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3151 * Add the new extended block to the chain.
3153 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3158 * Initialize this block.
3160 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3161 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3166 /******************************************************************************
3167 * Storage32Impl_FreeBigBlock
3169 * This method will flag the specified block as free in the big block depot.
3171 static void StorageImpl_FreeBigBlock(
3175 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3177 if (blockIndex
< This
->prevFreeBlock
)
3178 This
->prevFreeBlock
= blockIndex
;
3181 /************************************************************************
3182 * Storage32Impl_GetNextBlockInChain
3184 * This method will retrieve the block index of the next big block in
3187 * Params: This - Pointer to the Storage object.
3188 * blockIndex - Index of the block to retrieve the chain
3190 * nextBlockIndex - receives the return value.
3192 * Returns: This method returns the index of the next block in the chain.
3193 * It will return the constants:
3194 * BLOCK_SPECIAL - If the block given was not part of a
3196 * BLOCK_END_OF_CHAIN - If the block given was the last in
3198 * BLOCK_UNUSED - If the block given was not past of a chain
3200 * BLOCK_EXTBBDEPOT - This block is part of the extended
3203 * See Windows documentation for more details on IStorage methods.
3205 static HRESULT
StorageImpl_GetNextBlockInChain(
3208 ULONG
* nextBlockIndex
)
3210 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3211 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3212 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3213 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3215 ULONG depotBlockIndexPos
;
3216 int index
, num_blocks
;
3218 *nextBlockIndex
= BLOCK_SPECIAL
;
3220 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3222 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3223 This
->bigBlockDepotCount
);
3224 return STG_E_READFAULT
;
3228 * Cache the currently accessed depot block.
3230 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3232 This
->indexBlockDepotCached
= depotBlockCount
;
3234 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3236 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3241 * We have to look in the extended depot.
3243 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3246 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3249 return STG_E_READFAULT
;
3251 num_blocks
= This
->bigBlockSize
/ 4;
3253 for (index
= 0; index
< num_blocks
; index
++)
3255 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3256 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3260 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3265 /******************************************************************************
3266 * Storage32Impl_GetNextExtendedBlock
3268 * Given an extended block this method will return the next extended block.
3271 * The last ULONG of an extended block is the block index of the next
3272 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3276 * - The index of the next extended block
3277 * - BLOCK_UNUSED: there is no next extended block.
3278 * - Any other return values denotes failure.
3280 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3282 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3283 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3285 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3288 return nextBlockIndex
;
3291 /******************************************************************************
3292 * Storage32Impl_SetNextBlockInChain
3294 * This method will write the index of the specified block's next block
3295 * in the big block depot.
3297 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3300 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3301 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3302 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3305 static void StorageImpl_SetNextBlockInChain(
3310 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3311 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3312 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3313 ULONG depotBlockIndexPos
;
3315 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3316 assert(blockIndex
!= nextBlock
);
3318 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3320 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3325 * We have to look in the extended depot.
3327 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3330 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3333 * Update the cached block depot, if necessary.
3335 if (depotBlockCount
== This
->indexBlockDepotCached
)
3337 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3341 /******************************************************************************
3342 * Storage32Impl_LoadFileHeader
3344 * This method will read in the file header
3346 static HRESULT
StorageImpl_LoadFileHeader(
3350 BYTE headerBigBlock
[HEADER_SIZE
];
3352 ULARGE_INTEGER offset
;
3357 * Get a pointer to the big block of data containing the header.
3359 offset
.u
.HighPart
= 0;
3360 offset
.u
.LowPart
= 0;
3361 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3362 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3363 hr
= STG_E_FILENOTFOUND
;
3366 * Extract the information from the header.
3371 * Check for the "magic number" signature and return an error if it is not
3374 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3376 return STG_E_OLDFORMAT
;
3379 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3381 return STG_E_INVALIDHEADER
;
3384 StorageUtl_ReadWord(
3386 OFFSET_BIGBLOCKSIZEBITS
,
3387 &This
->bigBlockSizeBits
);
3389 StorageUtl_ReadWord(
3391 OFFSET_SMALLBLOCKSIZEBITS
,
3392 &This
->smallBlockSizeBits
);
3394 StorageUtl_ReadDWord(
3396 OFFSET_BBDEPOTCOUNT
,
3397 &This
->bigBlockDepotCount
);
3399 StorageUtl_ReadDWord(
3401 OFFSET_ROOTSTARTBLOCK
,
3402 &This
->rootStartBlock
);
3404 StorageUtl_ReadDWord(
3406 OFFSET_SMALLBLOCKLIMIT
,
3407 &This
->smallBlockLimit
);
3409 StorageUtl_ReadDWord(
3411 OFFSET_SBDEPOTSTART
,
3412 &This
->smallBlockDepotStart
);
3414 StorageUtl_ReadDWord(
3416 OFFSET_EXTBBDEPOTSTART
,
3417 &This
->extBigBlockDepotStart
);
3419 StorageUtl_ReadDWord(
3421 OFFSET_EXTBBDEPOTCOUNT
,
3422 &This
->extBigBlockDepotCount
);
3424 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3426 StorageUtl_ReadDWord(
3428 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3429 &(This
->bigBlockDepotStart
[index
]));
3433 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3435 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3436 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3439 * Right now, the code is making some assumptions about the size of the
3440 * blocks, just make sure they are what we're expecting.
3442 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3443 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3444 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3446 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3447 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3448 hr
= STG_E_INVALIDHEADER
;
3457 /******************************************************************************
3458 * Storage32Impl_SaveFileHeader
3460 * This method will save to the file the header
3462 static void StorageImpl_SaveFileHeader(
3465 BYTE headerBigBlock
[HEADER_SIZE
];
3468 ULARGE_INTEGER offset
;
3469 DWORD bytes_read
, bytes_written
;
3470 DWORD major_version
, dirsectorcount
;
3473 * Get a pointer to the big block of data containing the header.
3475 offset
.u
.HighPart
= 0;
3476 offset
.u
.LowPart
= 0;
3477 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3478 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3479 hr
= STG_E_FILENOTFOUND
;
3481 if (This
->bigBlockSizeBits
== 0x9)
3483 else if (This
->bigBlockSizeBits
== 0xc)
3487 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3492 * If the block read failed, the file is probably new.
3497 * Initialize for all unknown fields.
3499 memset(headerBigBlock
, 0, HEADER_SIZE
);
3502 * Initialize the magic number.
3504 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3508 * Write the information to the header.
3510 StorageUtl_WriteWord(
3512 OFFSET_MINORVERSION
,
3515 StorageUtl_WriteWord(
3517 OFFSET_MAJORVERSION
,
3520 StorageUtl_WriteWord(
3522 OFFSET_BYTEORDERMARKER
,
3525 StorageUtl_WriteWord(
3527 OFFSET_BIGBLOCKSIZEBITS
,
3528 This
->bigBlockSizeBits
);
3530 StorageUtl_WriteWord(
3532 OFFSET_SMALLBLOCKSIZEBITS
,
3533 This
->smallBlockSizeBits
);
3535 if (major_version
>= 4)
3537 if (This
->rootBlockChain
)
3538 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3540 /* This file is being created, and it will start out with one block. */
3544 /* This field must be 0 in versions older than 4 */
3547 StorageUtl_WriteDWord(
3549 OFFSET_DIRSECTORCOUNT
,
3552 StorageUtl_WriteDWord(
3554 OFFSET_BBDEPOTCOUNT
,
3555 This
->bigBlockDepotCount
);
3557 StorageUtl_WriteDWord(
3559 OFFSET_ROOTSTARTBLOCK
,
3560 This
->rootStartBlock
);
3562 StorageUtl_WriteDWord(
3564 OFFSET_SMALLBLOCKLIMIT
,
3565 This
->smallBlockLimit
);
3567 StorageUtl_WriteDWord(
3569 OFFSET_SBDEPOTSTART
,
3570 This
->smallBlockDepotStart
);
3572 StorageUtl_WriteDWord(
3574 OFFSET_SBDEPOTCOUNT
,
3575 This
->smallBlockDepotChain
?
3576 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3578 StorageUtl_WriteDWord(
3580 OFFSET_EXTBBDEPOTSTART
,
3581 This
->extBigBlockDepotStart
);
3583 StorageUtl_WriteDWord(
3585 OFFSET_EXTBBDEPOTCOUNT
,
3586 This
->extBigBlockDepotCount
);
3588 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3590 StorageUtl_WriteDWord(
3592 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3593 (This
->bigBlockDepotStart
[index
]));
3597 * Write the big block back to the file.
3599 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3602 /******************************************************************************
3603 * StorageImpl_ReadRawDirEntry
3605 * This method will read the raw data from a directory entry in the file.
3607 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3609 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3611 ULARGE_INTEGER offset
;
3615 offset
.u
.HighPart
= 0;
3616 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3618 hr
= BlockChainStream_ReadAt(
3619 This
->rootBlockChain
,
3625 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3626 return STG_E_READFAULT
;
3631 /******************************************************************************
3632 * StorageImpl_WriteRawDirEntry
3634 * This method will write the raw data from a directory entry in the file.
3636 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3638 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3640 ULARGE_INTEGER offset
;
3644 offset
.u
.HighPart
= 0;
3645 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3647 hr
= BlockChainStream_WriteAt(
3648 This
->rootBlockChain
,
3657 /******************************************************************************
3660 * Update raw directory entry data from the fields in newData.
3662 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3664 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3666 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3669 buffer
+ OFFSET_PS_NAME
,
3671 DIRENTRY_NAME_BUFFER_LEN
);
3673 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3675 StorageUtl_WriteWord(
3677 OFFSET_PS_NAMELENGTH
,
3678 newData
->sizeOfNameString
);
3680 StorageUtl_WriteDWord(
3682 OFFSET_PS_LEFTCHILD
,
3683 newData
->leftChild
);
3685 StorageUtl_WriteDWord(
3687 OFFSET_PS_RIGHTCHILD
,
3688 newData
->rightChild
);
3690 StorageUtl_WriteDWord(
3693 newData
->dirRootEntry
);
3695 StorageUtl_WriteGUID(
3700 StorageUtl_WriteDWord(
3703 newData
->ctime
.dwLowDateTime
);
3705 StorageUtl_WriteDWord(
3707 OFFSET_PS_CTIMEHIGH
,
3708 newData
->ctime
.dwHighDateTime
);
3710 StorageUtl_WriteDWord(
3713 newData
->mtime
.dwLowDateTime
);
3715 StorageUtl_WriteDWord(
3717 OFFSET_PS_MTIMEHIGH
,
3718 newData
->ctime
.dwHighDateTime
);
3720 StorageUtl_WriteDWord(
3722 OFFSET_PS_STARTBLOCK
,
3723 newData
->startingBlock
);
3725 StorageUtl_WriteDWord(
3728 newData
->size
.u
.LowPart
);
3731 /******************************************************************************
3732 * Storage32Impl_ReadDirEntry
3734 * This method will read the specified directory entry.
3736 HRESULT
StorageImpl_ReadDirEntry(
3741 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3744 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3746 if (SUCCEEDED(readRes
))
3748 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3751 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3752 DIRENTRY_NAME_BUFFER_LEN
);
3753 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3755 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3757 StorageUtl_ReadWord(
3759 OFFSET_PS_NAMELENGTH
,
3760 &buffer
->sizeOfNameString
);
3762 StorageUtl_ReadDWord(
3764 OFFSET_PS_LEFTCHILD
,
3765 &buffer
->leftChild
);
3767 StorageUtl_ReadDWord(
3769 OFFSET_PS_RIGHTCHILD
,
3770 &buffer
->rightChild
);
3772 StorageUtl_ReadDWord(
3775 &buffer
->dirRootEntry
);
3777 StorageUtl_ReadGUID(
3782 StorageUtl_ReadDWord(
3785 &buffer
->ctime
.dwLowDateTime
);
3787 StorageUtl_ReadDWord(
3789 OFFSET_PS_CTIMEHIGH
,
3790 &buffer
->ctime
.dwHighDateTime
);
3792 StorageUtl_ReadDWord(
3795 &buffer
->mtime
.dwLowDateTime
);
3797 StorageUtl_ReadDWord(
3799 OFFSET_PS_MTIMEHIGH
,
3800 &buffer
->mtime
.dwHighDateTime
);
3802 StorageUtl_ReadDWord(
3804 OFFSET_PS_STARTBLOCK
,
3805 &buffer
->startingBlock
);
3807 StorageUtl_ReadDWord(
3810 &buffer
->size
.u
.LowPart
);
3812 buffer
->size
.u
.HighPart
= 0;
3818 /*********************************************************************
3819 * Write the specified directory entry to the file
3821 HRESULT
StorageImpl_WriteDirEntry(
3824 const DirEntry
* buffer
)
3826 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3829 UpdateRawDirEntry(currentEntry
, buffer
);
3831 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3835 static BOOL
StorageImpl_ReadBigBlock(
3840 ULARGE_INTEGER ulOffset
;
3843 ulOffset
.u
.HighPart
= 0;
3844 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3846 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3847 return (read
== This
->bigBlockSize
);
3850 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3856 ULARGE_INTEGER ulOffset
;
3860 ulOffset
.u
.HighPart
= 0;
3861 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3862 ulOffset
.u
.LowPart
+= offset
;
3864 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3865 *value
= lendian32toh(tmp
);
3866 return (read
== sizeof(DWORD
));
3869 static BOOL
StorageImpl_WriteBigBlock(
3874 ULARGE_INTEGER ulOffset
;
3877 ulOffset
.u
.HighPart
= 0;
3878 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3880 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3881 return (wrote
== This
->bigBlockSize
);
3884 static BOOL
StorageImpl_WriteDWordToBigBlock(
3890 ULARGE_INTEGER ulOffset
;
3893 ulOffset
.u
.HighPart
= 0;
3894 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3895 ulOffset
.u
.LowPart
+= offset
;
3897 value
= htole32(value
);
3898 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3899 return (wrote
== sizeof(DWORD
));
3902 /******************************************************************************
3903 * Storage32Impl_SmallBlocksToBigBlocks
3905 * This method will convert a small block chain to a big block chain.
3906 * The small block chain will be destroyed.
3908 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3910 SmallBlockChainStream
** ppsbChain
)
3912 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3913 ULARGE_INTEGER size
, offset
;
3914 ULONG cbRead
, cbWritten
;
3915 ULARGE_INTEGER cbTotalRead
;
3916 DirRef streamEntryRef
;
3917 HRESULT resWrite
= S_OK
;
3919 DirEntry streamEntry
;
3921 BlockChainStream
*bbTempChain
= NULL
;
3922 BlockChainStream
*bigBlockChain
= NULL
;
3925 * Create a temporary big block chain that doesn't have
3926 * an associated directory entry. This temporary chain will be
3927 * used to copy data from small blocks to big blocks.
3929 bbTempChain
= BlockChainStream_Construct(This
,
3932 if(!bbTempChain
) return NULL
;
3934 * Grow the big block chain.
3936 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3937 BlockChainStream_SetSize(bbTempChain
, size
);
3940 * Copy the contents of the small block chain to the big block chain
3941 * by small block size increments.
3943 offset
.u
.LowPart
= 0;
3944 offset
.u
.HighPart
= 0;
3945 cbTotalRead
.QuadPart
= 0;
3947 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3950 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3952 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3955 if (FAILED(resRead
))
3960 cbTotalRead
.QuadPart
+= cbRead
;
3962 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3968 if (FAILED(resWrite
))
3971 offset
.u
.LowPart
+= cbRead
;
3975 resRead
= STG_E_READFAULT
;
3978 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3979 HeapFree(GetProcessHeap(),0,buffer
);
3981 size
.u
.HighPart
= 0;
3984 if (FAILED(resRead
) || FAILED(resWrite
))
3986 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3987 BlockChainStream_SetSize(bbTempChain
, size
);
3988 BlockChainStream_Destroy(bbTempChain
);
3993 * Destroy the small block chain.
3995 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3996 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3997 SmallBlockChainStream_Destroy(*ppsbChain
);
4001 * Change the directory entry. This chain is now a big block chain
4002 * and it doesn't reside in the small blocks chain anymore.
4004 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4006 streamEntry
.startingBlock
= bbHeadOfChain
;
4008 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4011 * Destroy the temporary entryless big block chain.
4012 * Create a new big block chain associated with this entry.
4014 BlockChainStream_Destroy(bbTempChain
);
4015 bigBlockChain
= BlockChainStream_Construct(This
,
4019 return bigBlockChain
;
4022 /******************************************************************************
4023 * Storage32Impl_BigBlocksToSmallBlocks
4025 * This method will convert a big block chain to a small block chain.
4026 * The big block chain will be destroyed on success.
4028 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4030 BlockChainStream
** ppbbChain
)
4032 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4033 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4034 DirRef streamEntryRef
;
4035 HRESULT resWrite
= S_OK
, resRead
;
4036 DirEntry streamEntry
;
4038 SmallBlockChainStream
* sbTempChain
;
4040 TRACE("%p %p\n", This
, ppbbChain
);
4042 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4048 size
= BlockChainStream_GetSize(*ppbbChain
);
4049 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4051 offset
.u
.HighPart
= 0;
4052 offset
.u
.LowPart
= 0;
4053 cbTotalRead
.QuadPart
= 0;
4054 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4057 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4058 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4066 cbTotalRead
.QuadPart
+= cbRead
;
4068 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4069 cbRead
, buffer
, &cbWritten
);
4071 if(FAILED(resWrite
))
4074 offset
.u
.LowPart
+= cbRead
;
4078 resRead
= STG_E_READFAULT
;
4081 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
4082 HeapFree(GetProcessHeap(), 0, buffer
);
4084 size
.u
.HighPart
= 0;
4087 if(FAILED(resRead
) || FAILED(resWrite
))
4089 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4090 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4091 SmallBlockChainStream_Destroy(sbTempChain
);
4095 /* destroy the original big block chain */
4096 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4097 BlockChainStream_SetSize(*ppbbChain
, size
);
4098 BlockChainStream_Destroy(*ppbbChain
);
4101 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4102 streamEntry
.startingBlock
= sbHeadOfChain
;
4103 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4105 SmallBlockChainStream_Destroy(sbTempChain
);
4106 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4109 static HRESULT
StorageBaseImpl_CopyStream(
4110 StorageBaseImpl
*dst
, DirRef dst_entry
,
4111 StorageBaseImpl
*src
, DirRef src_entry
)
4116 ULARGE_INTEGER bytes_copied
;
4117 ULONG bytestocopy
, bytesread
, byteswritten
;
4119 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4123 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4125 bytes_copied
.QuadPart
= 0;
4126 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4128 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4130 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4132 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4135 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4136 data
, &byteswritten
);
4139 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4140 bytes_copied
.QuadPart
+= byteswritten
;
4148 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4150 DirRef result
=This
->firstFreeEntry
;
4152 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4155 if (result
== This
->entries_size
)
4157 ULONG new_size
= This
->entries_size
* 2;
4158 TransactedDirEntry
*new_entries
;
4160 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4161 if (!new_entries
) return DIRENTRY_NULL
;
4163 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4164 HeapFree(GetProcessHeap(), 0, This
->entries
);
4166 This
->entries
= new_entries
;
4167 This
->entries_size
= new_size
;
4170 This
->entries
[result
].inuse
= 1;
4172 This
->firstFreeEntry
= result
+1;
4177 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4178 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4180 DirRef stubEntryRef
;
4181 TransactedDirEntry
*entry
;
4183 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4185 if (stubEntryRef
!= DIRENTRY_NULL
)
4187 entry
= &This
->entries
[stubEntryRef
];
4189 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4194 return stubEntryRef
;
4197 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4198 TransactedSnapshotImpl
*This
, DirRef entry
)
4203 if (!This
->entries
[entry
].read
)
4205 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4206 This
->entries
[entry
].transactedParentEntry
,
4209 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4211 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4213 if (data
.leftChild
== DIRENTRY_NULL
)
4217 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4219 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4221 if (data
.rightChild
== DIRENTRY_NULL
)
4225 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4227 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4229 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4235 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4236 This
->entries
[entry
].read
= 1;
4243 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4244 TransactedSnapshotImpl
*This
, DirRef entry
)
4248 if (!This
->entries
[entry
].stream_dirty
)
4250 DirEntry new_entrydata
;
4252 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4253 new_entrydata
.name
[0] = 'S';
4254 new_entrydata
.sizeOfNameString
= 1;
4255 new_entrydata
.stgType
= STGTY_STREAM
;
4256 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4257 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4258 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4259 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4261 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4262 &This
->entries
[entry
].stream_entry
);
4264 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4266 hr
= StorageBaseImpl_CopyStream(
4267 This
->scratch
, This
->entries
[entry
].stream_entry
,
4268 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4271 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4275 This
->entries
[entry
].stream_dirty
= 1;
4277 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4279 /* Since this entry is modified, and we aren't using its stream data, we
4280 * no longer care about the original entry. */
4282 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4284 if (delete_ref
!= DIRENTRY_NULL
)
4285 This
->entries
[delete_ref
].deleted
= 1;
4287 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4294 /* Find the first entry in a depth-first traversal. */
4295 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4296 TransactedSnapshotImpl
* This
, DirRef parent
)
4298 DirRef cursor
, prev
;
4299 TransactedDirEntry
*entry
;
4302 entry
= &This
->entries
[cursor
];
4305 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4308 cursor
= entry
->data
.leftChild
;
4309 entry
= &This
->entries
[cursor
];
4310 entry
->parent
= prev
;
4312 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4315 cursor
= entry
->data
.rightChild
;
4316 entry
= &This
->entries
[cursor
];
4317 entry
->parent
= prev
;
4319 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4322 cursor
= entry
->data
.dirRootEntry
;
4323 entry
= &This
->entries
[cursor
];
4324 entry
->parent
= prev
;
4333 /* Find the next entry in a depth-first traversal. */
4334 static DirRef
TransactedSnapshotImpl_FindNextChild(
4335 TransactedSnapshotImpl
* This
, DirRef current
)
4338 TransactedDirEntry
*parent_entry
;
4340 parent
= This
->entries
[current
].parent
;
4341 parent_entry
= &This
->entries
[parent
];
4343 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4345 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4347 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4348 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4351 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4353 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4354 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4361 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4362 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4363 TransactedSnapshotImpl
* This
, DirRef entry
)
4365 return entry
!= DIRENTRY_NULL
&&
4366 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4369 /* Destroy the entries created by CopyTree. */
4370 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4371 TransactedSnapshotImpl
* This
, DirRef stop
)
4374 TransactedDirEntry
*entry
;
4375 ULARGE_INTEGER zero
;
4379 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4382 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4384 if (cursor
== DIRENTRY_NULL
)
4387 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4389 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4391 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4393 entry
= &This
->entries
[cursor
];
4395 if (entry
->stream_dirty
)
4396 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4397 entry
->newTransactedParentEntry
, zero
);
4399 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4400 entry
->newTransactedParentEntry
);
4402 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4405 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4409 /* Make a copy of our edited tree that we can use in the parent. */
4410 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4413 TransactedDirEntry
*entry
;
4416 cursor
= This
->base
.storageDirEntry
;
4417 entry
= &This
->entries
[cursor
];
4418 entry
->parent
= DIRENTRY_NULL
;
4419 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4421 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4424 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4426 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4427 entry
= &This
->entries
[cursor
];
4429 while (cursor
!= DIRENTRY_NULL
)
4431 /* Make a copy of this entry in the transacted parent. */
4433 (!entry
->dirty
&& !entry
->stream_dirty
&&
4434 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4435 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4436 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4437 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4442 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4444 newData
.size
.QuadPart
= 0;
4445 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4447 if (newData
.leftChild
!= DIRENTRY_NULL
)
4448 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4450 if (newData
.rightChild
!= DIRENTRY_NULL
)
4451 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4453 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4454 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4456 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4457 &entry
->newTransactedParentEntry
);
4460 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4464 if (entry
->stream_dirty
)
4466 hr
= StorageBaseImpl_CopyStream(
4467 This
->transactedParent
, entry
->newTransactedParentEntry
,
4468 This
->scratch
, entry
->stream_entry
);
4470 else if (entry
->data
.size
.QuadPart
)
4472 hr
= StorageBaseImpl_StreamLink(
4473 This
->transactedParent
, entry
->newTransactedParentEntry
,
4474 entry
->transactedParentEntry
);
4479 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4480 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4485 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4486 entry
= &This
->entries
[cursor
];
4492 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4494 DWORD grfCommitFlags
) /* [in] */
4496 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4497 TransactedDirEntry
*root_entry
;
4498 DirRef i
, dir_root_ref
;
4500 ULARGE_INTEGER zero
;
4505 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4507 /* Cannot commit a read-only transacted storage */
4508 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4509 return STG_E_ACCESSDENIED
;
4511 /* To prevent data loss, we create the new structure in the file before we
4512 * delete the old one, so that in case of errors the old data is intact. We
4513 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4514 * needed in the rare situation where we have just enough free disk space to
4515 * overwrite the existing data. */
4517 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4519 if (!root_entry
->read
)
4522 hr
= TransactedSnapshotImpl_CopyTree(This
);
4523 if (FAILED(hr
)) return hr
;
4525 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4526 dir_root_ref
= DIRENTRY_NULL
;
4528 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4530 /* Update the storage to use the new data in one step. */
4531 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4532 root_entry
->transactedParentEntry
, &data
);
4536 data
.dirRootEntry
= dir_root_ref
;
4537 data
.clsid
= root_entry
->data
.clsid
;
4538 data
.ctime
= root_entry
->data
.ctime
;
4539 data
.mtime
= root_entry
->data
.mtime
;
4541 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4542 root_entry
->transactedParentEntry
, &data
);
4547 /* Destroy the old now-orphaned data. */
4548 for (i
=0; i
<This
->entries_size
; i
++)
4550 TransactedDirEntry
*entry
= &This
->entries
[i
];
4555 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4556 entry
->transactedParentEntry
, zero
);
4557 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4558 entry
->transactedParentEntry
);
4559 memset(entry
, 0, sizeof(TransactedDirEntry
));
4560 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4562 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4564 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4565 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4566 entry
->transactedParentEntry
);
4567 if (entry
->stream_dirty
)
4569 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4570 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4571 entry
->stream_dirty
= 0;
4574 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4581 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4587 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4590 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4591 ULARGE_INTEGER zero
;
4594 TRACE("(%p)\n", iface
);
4596 /* Destroy the open objects. */
4597 StorageBaseImpl_DeleteAll(&This
->base
);
4599 /* Clear out the scratch file. */
4601 for (i
=0; i
<This
->entries_size
; i
++)
4603 if (This
->entries
[i
].stream_dirty
)
4605 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4608 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4612 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4614 This
->firstFreeEntry
= 0;
4615 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4620 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4622 if (!This
->reverted
)
4624 TRACE("Storage invalidated (stg=%p)\n", This
);
4628 StorageBaseImpl_DeleteAll(This
);
4632 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4634 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4636 TransactedSnapshotImpl_Revert((IStorage
*)iface
);
4638 IStorage_Release((IStorage
*)This
->transactedParent
);
4640 IStorage_Release((IStorage
*)This
->scratch
);
4642 HeapFree(GetProcessHeap(), 0, This
->entries
);
4644 HeapFree(GetProcessHeap(), 0, This
);
4647 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4649 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4651 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4654 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4655 const DirEntry
*newData
, DirRef
*index
)
4657 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4659 TransactedDirEntry
*new_entry
;
4661 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4662 if (new_ref
== DIRENTRY_NULL
)
4663 return E_OUTOFMEMORY
;
4665 new_entry
= &This
->entries
[new_ref
];
4667 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4668 new_entry
->read
= 1;
4669 new_entry
->dirty
= 1;
4670 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4674 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4679 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4680 DirRef index
, const DirEntry
*data
)
4682 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4685 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4687 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4688 if (FAILED(hr
)) return hr
;
4690 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4692 if (index
!= This
->base
.storageDirEntry
)
4694 This
->entries
[index
].dirty
= 1;
4696 if (data
->size
.QuadPart
== 0 &&
4697 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4699 /* Since this entry is modified, and we aren't using its stream data, we
4700 * no longer care about the original entry. */
4702 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4704 if (delete_ref
!= DIRENTRY_NULL
)
4705 This
->entries
[delete_ref
].deleted
= 1;
4707 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4714 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4715 DirRef index
, DirEntry
*data
)
4717 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4720 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4721 if (FAILED(hr
)) return hr
;
4723 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4725 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4730 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4733 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4735 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4736 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4738 /* If we deleted this entry while it has stream data. We must have left the
4739 * data because some other entry is using it, and we need to leave the
4740 * original entry alone. */
4741 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4742 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4746 This
->entries
[index
].deleted
= 1;
4752 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4753 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4755 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4757 if (This
->entries
[index
].stream_dirty
)
4759 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4760 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4762 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4764 /* This stream doesn't live in the parent, and we haven't allocated storage
4771 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4772 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4776 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4777 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4779 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4782 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4783 if (FAILED(hr
)) return hr
;
4785 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4786 if (FAILED(hr
)) return hr
;
4788 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4789 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4791 if (SUCCEEDED(hr
) && size
!= 0)
4792 This
->entries
[index
].data
.size
.QuadPart
= max(
4793 This
->entries
[index
].data
.size
.QuadPart
,
4794 offset
.QuadPart
+ size
);
4799 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4800 DirRef index
, ULARGE_INTEGER newsize
)
4802 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4805 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4806 if (FAILED(hr
)) return hr
;
4808 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4811 if (newsize
.QuadPart
== 0)
4813 /* Destroy any parent references or entries in the scratch file. */
4814 if (This
->entries
[index
].stream_dirty
)
4816 ULARGE_INTEGER zero
;
4818 StorageBaseImpl_StreamSetSize(This
->scratch
,
4819 This
->entries
[index
].stream_entry
, zero
);
4820 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4821 This
->entries
[index
].stream_entry
);
4822 This
->entries
[index
].stream_dirty
= 0;
4824 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4827 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4829 if (delete_ref
!= DIRENTRY_NULL
)
4830 This
->entries
[delete_ref
].deleted
= 1;
4832 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4837 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4838 if (FAILED(hr
)) return hr
;
4840 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
4841 This
->entries
[index
].stream_entry
, newsize
);
4845 This
->entries
[index
].data
.size
= newsize
;
4850 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
4851 DirRef dst
, DirRef src
)
4853 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4855 TransactedDirEntry
*dst_entry
, *src_entry
;
4857 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
4858 if (FAILED(hr
)) return hr
;
4860 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
4861 if (FAILED(hr
)) return hr
;
4863 dst_entry
= &This
->entries
[dst
];
4864 src_entry
= &This
->entries
[src
];
4866 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
4867 dst_entry
->stream_entry
= src_entry
->stream_entry
;
4868 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
4869 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
4870 dst_entry
->data
.size
= src_entry
->data
.size
;
4875 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4877 StorageBaseImpl_QueryInterface
,
4878 StorageBaseImpl_AddRef
,
4879 StorageBaseImpl_Release
,
4880 StorageBaseImpl_CreateStream
,
4881 StorageBaseImpl_OpenStream
,
4882 StorageBaseImpl_CreateStorage
,
4883 StorageBaseImpl_OpenStorage
,
4884 StorageBaseImpl_CopyTo
,
4885 StorageBaseImpl_MoveElementTo
,
4886 TransactedSnapshotImpl_Commit
,
4887 TransactedSnapshotImpl_Revert
,
4888 StorageBaseImpl_EnumElements
,
4889 StorageBaseImpl_DestroyElement
,
4890 StorageBaseImpl_RenameElement
,
4891 StorageBaseImpl_SetElementTimes
,
4892 StorageBaseImpl_SetClass
,
4893 StorageBaseImpl_SetStateBits
,
4894 StorageBaseImpl_Stat
4897 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4899 TransactedSnapshotImpl_Destroy
,
4900 TransactedSnapshotImpl_Invalidate
,
4901 TransactedSnapshotImpl_GetFilename
,
4902 TransactedSnapshotImpl_CreateDirEntry
,
4903 TransactedSnapshotImpl_WriteDirEntry
,
4904 TransactedSnapshotImpl_ReadDirEntry
,
4905 TransactedSnapshotImpl_DestroyDirEntry
,
4906 TransactedSnapshotImpl_StreamReadAt
,
4907 TransactedSnapshotImpl_StreamWriteAt
,
4908 TransactedSnapshotImpl_StreamSetSize
,
4909 TransactedSnapshotImpl_StreamLink
4912 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4913 TransactedSnapshotImpl
** result
)
4917 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4920 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4922 /* This is OK because the property set storage functions use the IStorage functions. */
4923 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4925 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4927 list_init(&(*result
)->base
.strmHead
);
4929 list_init(&(*result
)->base
.storageHead
);
4931 (*result
)->base
.ref
= 1;
4933 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4935 /* Create a new temporary storage to act as the scratch file. */
4936 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
,
4937 0, (IStorage
**)&(*result
)->scratch
);
4941 ULONG num_entries
= 20;
4943 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
4945 (*result
)->entries_size
= num_entries
;
4947 (*result
)->firstFreeEntry
= 0;
4949 if ((*result
)->entries
)
4951 /* parentStorage already has 1 reference, which we take over here. */
4952 (*result
)->transactedParent
= parentStorage
;
4954 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4956 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
4960 IStorage_Release((IStorage
*)(*result
)->scratch
);
4966 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4971 return E_OUTOFMEMORY
;
4974 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4975 StorageBaseImpl
** result
)
4979 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4981 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4984 return TransactedSnapshotImpl_Construct(parentStorage
,
4985 (TransactedSnapshotImpl
**)result
);
4988 static HRESULT
Storage_Construct(
4996 StorageBaseImpl
** result
)
4998 StorageImpl
*newStorage
;
4999 StorageBaseImpl
*newTransactedStorage
;
5002 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5003 if (FAILED(hr
)) goto end
;
5005 if (openFlags
& STGM_TRANSACTED
)
5007 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5009 IStorage_Release((IStorage
*)newStorage
);
5011 *result
= newTransactedStorage
;
5014 *result
= &newStorage
->base
;
5020 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5022 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5024 if (!This
->base
.reverted
)
5026 TRACE("Storage invalidated (stg=%p)\n", This
);
5028 This
->base
.reverted
= 1;
5030 This
->parentStorage
= NULL
;
5032 StorageBaseImpl_DeleteAll(&This
->base
);
5034 list_remove(&This
->ParentListEntry
);
5038 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5040 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5042 StorageInternalImpl_Invalidate(&This
->base
);
5044 HeapFree(GetProcessHeap(), 0, This
);
5047 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5049 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5051 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5054 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5055 const DirEntry
*newData
, DirRef
*index
)
5057 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5059 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5063 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5064 DirRef index
, const DirEntry
*data
)
5066 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5068 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5072 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5073 DirRef index
, DirEntry
*data
)
5075 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5077 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5081 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5084 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5086 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5090 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5091 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5093 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5095 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5096 index
, offset
, size
, buffer
, bytesRead
);
5099 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5100 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5102 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5104 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5105 index
, offset
, size
, buffer
, bytesWritten
);
5108 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5109 DirRef index
, ULARGE_INTEGER newsize
)
5111 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5113 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5117 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5118 DirRef dst
, DirRef src
)
5120 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5122 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5126 /******************************************************************************
5128 ** Storage32InternalImpl_Commit
5131 static HRESULT WINAPI
StorageInternalImpl_Commit(
5133 DWORD grfCommitFlags
) /* [in] */
5135 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
5139 /******************************************************************************
5141 ** Storage32InternalImpl_Revert
5144 static HRESULT WINAPI
StorageInternalImpl_Revert(
5147 FIXME("(%p): stub\n", iface
);
5151 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5153 IStorage_Release((IStorage
*)This
->parentStorage
);
5154 HeapFree(GetProcessHeap(), 0, This
);
5157 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5158 IEnumSTATSTG
* iface
,
5162 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5165 return E_INVALIDARG
;
5169 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5170 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5173 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
5177 return E_NOINTERFACE
;
5180 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5181 IEnumSTATSTG
* iface
)
5183 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5184 return InterlockedIncrement(&This
->ref
);
5187 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5188 IEnumSTATSTG
* iface
)
5190 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5194 newRef
= InterlockedDecrement(&This
->ref
);
5198 IEnumSTATSTGImpl_Destroy(This
);
5204 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5205 IEnumSTATSTGImpl
* This
,
5208 DirRef result
= DIRENTRY_NULL
;
5212 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5214 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5215 This
->parentStorage
->storageDirEntry
, &entry
);
5216 searchNode
= entry
.dirRootEntry
;
5218 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5220 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5224 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5228 searchNode
= entry
.rightChild
;
5232 result
= searchNode
;
5233 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5234 searchNode
= entry
.leftChild
;
5242 if (result
!= DIRENTRY_NULL
)
5243 memcpy(This
->name
, result_name
, sizeof(result_name
));
5249 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5250 IEnumSTATSTG
* iface
,
5253 ULONG
* pceltFetched
)
5255 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5257 DirEntry currentEntry
;
5258 STATSTG
* currentReturnStruct
= rgelt
;
5259 ULONG objectFetched
= 0;
5260 DirRef currentSearchNode
;
5263 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5264 return E_INVALIDARG
;
5266 if (This
->parentStorage
->reverted
)
5267 return STG_E_REVERTED
;
5270 * To avoid the special case, get another pointer to a ULONG value if
5271 * the caller didn't supply one.
5273 if (pceltFetched
==0)
5274 pceltFetched
= &objectFetched
;
5277 * Start the iteration, we will iterate until we hit the end of the
5278 * linked list or until we hit the number of items to iterate through
5282 while ( *pceltFetched
< celt
)
5284 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5286 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5290 * Read the entry from the storage.
5292 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5297 * Copy the information to the return buffer.
5299 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5300 currentReturnStruct
,
5305 * Step to the next item in the iteration
5308 currentReturnStruct
++;
5311 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5318 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5319 IEnumSTATSTG
* iface
,
5322 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5324 ULONG objectFetched
= 0;
5325 DirRef currentSearchNode
;
5328 if (This
->parentStorage
->reverted
)
5329 return STG_E_REVERTED
;
5331 while ( (objectFetched
< celt
) )
5333 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5335 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5341 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5347 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5348 IEnumSTATSTG
* iface
)
5350 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5352 if (This
->parentStorage
->reverted
)
5353 return STG_E_REVERTED
;
5360 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5361 IEnumSTATSTG
* iface
,
5362 IEnumSTATSTG
** ppenum
)
5364 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5366 IEnumSTATSTGImpl
* newClone
;
5368 if (This
->parentStorage
->reverted
)
5369 return STG_E_REVERTED
;
5372 * Perform a sanity check on the parameters.
5375 return E_INVALIDARG
;
5377 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5378 This
->storageDirEntry
);
5382 * The new clone enumeration must point to the same current node as
5385 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5387 *ppenum
= (IEnumSTATSTG
*)newClone
;
5390 * Don't forget to nail down a reference to the clone before
5393 IEnumSTATSTGImpl_AddRef(*ppenum
);
5399 * Virtual function table for the IEnumSTATSTGImpl class.
5401 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5403 IEnumSTATSTGImpl_QueryInterface
,
5404 IEnumSTATSTGImpl_AddRef
,
5405 IEnumSTATSTGImpl_Release
,
5406 IEnumSTATSTGImpl_Next
,
5407 IEnumSTATSTGImpl_Skip
,
5408 IEnumSTATSTGImpl_Reset
,
5409 IEnumSTATSTGImpl_Clone
5412 /******************************************************************************
5413 ** IEnumSTATSTGImpl implementation
5416 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5417 StorageBaseImpl
* parentStorage
,
5418 DirRef storageDirEntry
)
5420 IEnumSTATSTGImpl
* newEnumeration
;
5422 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5424 if (newEnumeration
!=0)
5427 * Set-up the virtual function table and reference count.
5429 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5430 newEnumeration
->ref
= 0;
5433 * We want to nail-down the reference to the storage in case the
5434 * enumeration out-lives the storage in the client application.
5436 newEnumeration
->parentStorage
= parentStorage
;
5437 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
5439 newEnumeration
->storageDirEntry
= storageDirEntry
;
5442 * Make sure the current node of the iterator is the first one.
5444 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
5447 return newEnumeration
;
5451 * Virtual function table for the Storage32InternalImpl class.
5453 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5455 StorageBaseImpl_QueryInterface
,
5456 StorageBaseImpl_AddRef
,
5457 StorageBaseImpl_Release
,
5458 StorageBaseImpl_CreateStream
,
5459 StorageBaseImpl_OpenStream
,
5460 StorageBaseImpl_CreateStorage
,
5461 StorageBaseImpl_OpenStorage
,
5462 StorageBaseImpl_CopyTo
,
5463 StorageBaseImpl_MoveElementTo
,
5464 StorageInternalImpl_Commit
,
5465 StorageInternalImpl_Revert
,
5466 StorageBaseImpl_EnumElements
,
5467 StorageBaseImpl_DestroyElement
,
5468 StorageBaseImpl_RenameElement
,
5469 StorageBaseImpl_SetElementTimes
,
5470 StorageBaseImpl_SetClass
,
5471 StorageBaseImpl_SetStateBits
,
5472 StorageBaseImpl_Stat
5475 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5477 StorageInternalImpl_Destroy
,
5478 StorageInternalImpl_Invalidate
,
5479 StorageInternalImpl_GetFilename
,
5480 StorageInternalImpl_CreateDirEntry
,
5481 StorageInternalImpl_WriteDirEntry
,
5482 StorageInternalImpl_ReadDirEntry
,
5483 StorageInternalImpl_DestroyDirEntry
,
5484 StorageInternalImpl_StreamReadAt
,
5485 StorageInternalImpl_StreamWriteAt
,
5486 StorageInternalImpl_StreamSetSize
,
5487 StorageInternalImpl_StreamLink
5490 /******************************************************************************
5491 ** Storage32InternalImpl implementation
5494 static StorageInternalImpl
* StorageInternalImpl_Construct(
5495 StorageBaseImpl
* parentStorage
,
5497 DirRef storageDirEntry
)
5499 StorageInternalImpl
* newStorage
;
5501 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5505 list_init(&newStorage
->base
.strmHead
);
5507 list_init(&newStorage
->base
.storageHead
);
5510 * Initialize the virtual function table.
5512 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5513 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5514 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5515 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5517 newStorage
->base
.reverted
= 0;
5519 newStorage
->base
.ref
= 1;
5521 newStorage
->parentStorage
= parentStorage
;
5524 * Keep a reference to the directory entry of this storage
5526 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5528 newStorage
->base
.create
= 0;
5536 /******************************************************************************
5537 ** StorageUtl implementation
5540 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5544 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5545 *value
= lendian16toh(tmp
);
5548 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5550 value
= htole16(value
);
5551 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5554 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5558 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5559 *value
= lendian32toh(tmp
);
5562 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5564 value
= htole32(value
);
5565 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5568 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5569 ULARGE_INTEGER
* value
)
5571 #ifdef WORDS_BIGENDIAN
5574 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5575 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5576 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5578 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5582 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5583 const ULARGE_INTEGER
*value
)
5585 #ifdef WORDS_BIGENDIAN
5588 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5589 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5590 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5592 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5596 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5598 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5599 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5600 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5602 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5605 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5607 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5608 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5609 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5611 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5614 void StorageUtl_CopyDirEntryToSTATSTG(
5615 StorageBaseImpl
* storage
,
5616 STATSTG
* destination
,
5617 const DirEntry
* source
,
5621 * The copy of the string occurs only when the flag is not set
5623 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5625 /* Use the filename for the root storage. */
5626 destination
->pwcsName
= 0;
5627 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5629 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5630 (source
->name
[0] == 0) )
5632 destination
->pwcsName
= 0;
5636 destination
->pwcsName
=
5637 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5639 strcpyW(destination
->pwcsName
, source
->name
);
5642 switch (source
->stgType
)
5646 destination
->type
= STGTY_STORAGE
;
5649 destination
->type
= STGTY_STREAM
;
5652 destination
->type
= STGTY_STREAM
;
5656 destination
->cbSize
= source
->size
;
5658 currentReturnStruct->mtime = {0}; TODO
5659 currentReturnStruct->ctime = {0};
5660 currentReturnStruct->atime = {0};
5662 destination
->grfMode
= 0;
5663 destination
->grfLocksSupported
= 0;
5664 destination
->clsid
= source
->clsid
;
5665 destination
->grfStateBits
= 0;
5666 destination
->reserved
= 0;
5669 /******************************************************************************
5670 ** BlockChainStream implementation
5673 /* Read and save the index of all blocks in this stream. */
5674 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5676 ULONG next_sector
, next_offset
;
5678 struct BlockChainRun
*last_run
;
5680 if (This
->indexCacheLen
== 0)
5684 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5688 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5689 next_offset
= last_run
->lastOffset
+1;
5690 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5691 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5693 if (FAILED(hr
)) return hr
;
5696 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5698 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5700 /* Add the current block to the cache. */
5701 if (This
->indexCacheSize
== 0)
5703 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5704 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5705 This
->indexCacheSize
= 16;
5707 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5709 struct BlockChainRun
*new_cache
;
5712 new_size
= This
->indexCacheSize
* 2;
5713 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5714 if (!new_cache
) return E_OUTOFMEMORY
;
5715 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5717 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5718 This
->indexCache
= new_cache
;
5719 This
->indexCacheSize
= new_size
;
5722 This
->indexCacheLen
++;
5723 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5724 last_run
->firstSector
= next_sector
;
5725 last_run
->firstOffset
= next_offset
;
5728 last_run
->lastOffset
= next_offset
;
5730 /* Find the next block. */
5732 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5733 if (FAILED(hr
)) return hr
;
5736 if (This
->indexCacheLen
)
5738 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5739 This
->numBlocks
= last_run
->lastOffset
+1;
5743 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5744 This
->numBlocks
= 0;
5750 /* Locate the nth block in this stream. */
5751 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5753 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5754 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5756 if (offset
>= This
->numBlocks
)
5757 return BLOCK_END_OF_CHAIN
;
5759 while (min_run
< max_run
)
5761 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5762 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5764 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5765 max_run
= run_to_check
-1;
5767 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5769 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5770 min_run
= run_to_check
+1;
5773 /* Block is in this run. */
5774 min_run
= max_run
= run_to_check
;
5777 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5780 BlockChainStream
* BlockChainStream_Construct(
5781 StorageImpl
* parentStorage
,
5782 ULONG
* headOfStreamPlaceHolder
,
5785 BlockChainStream
* newStream
;
5787 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5789 newStream
->parentStorage
= parentStorage
;
5790 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5791 newStream
->ownerDirEntry
= dirEntry
;
5792 newStream
->indexCache
= NULL
;
5793 newStream
->indexCacheLen
= 0;
5794 newStream
->indexCacheSize
= 0;
5796 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
5798 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
5799 HeapFree(GetProcessHeap(), 0, newStream
);
5806 void BlockChainStream_Destroy(BlockChainStream
* This
)
5809 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5810 HeapFree(GetProcessHeap(), 0, This
);
5813 /******************************************************************************
5814 * BlockChainStream_GetHeadOfChain
5816 * Returns the head of this stream chain.
5817 * Some special chains don't have directory entries, their heads are kept in
5818 * This->headOfStreamPlaceHolder.
5821 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5823 DirEntry chainEntry
;
5826 if (This
->headOfStreamPlaceHolder
!= 0)
5827 return *(This
->headOfStreamPlaceHolder
);
5829 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5831 hr
= StorageImpl_ReadDirEntry(
5832 This
->parentStorage
,
5833 This
->ownerDirEntry
,
5838 return chainEntry
.startingBlock
;
5842 return BLOCK_END_OF_CHAIN
;
5845 /******************************************************************************
5846 * BlockChainStream_GetCount
5848 * Returns the number of blocks that comprises this chain.
5849 * This is not the size of the stream as the last block may not be full!
5851 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5853 return This
->numBlocks
;
5856 /******************************************************************************
5857 * BlockChainStream_ReadAt
5859 * Reads a specified number of bytes from this chain at the specified offset.
5860 * bytesRead may be NULL.
5861 * Failure will be returned if the specified number of bytes has not been read.
5863 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5864 ULARGE_INTEGER offset
,
5869 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5870 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5871 ULONG bytesToReadInBuffer
;
5874 ULARGE_INTEGER stream_size
;
5876 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5879 * Find the first block in the stream that contains part of the buffer.
5881 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5885 stream_size
= BlockChainStream_GetSize(This
);
5886 if (stream_size
.QuadPart
> offset
.QuadPart
)
5887 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
5892 * Start reading the buffer.
5894 bufferWalker
= buffer
;
5896 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5898 ULARGE_INTEGER ulOffset
;
5901 * Calculate how many bytes we can copy from this big block.
5903 bytesToReadInBuffer
=
5904 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5906 TRACE("block %i\n",blockIndex
);
5907 ulOffset
.u
.HighPart
= 0;
5908 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5911 StorageImpl_ReadAt(This
->parentStorage
,
5914 bytesToReadInBuffer
,
5917 * Step to the next big block.
5919 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5920 return STG_E_DOCFILECORRUPT
;
5922 bufferWalker
+= bytesReadAt
;
5923 size
-= bytesReadAt
;
5924 *bytesRead
+= bytesReadAt
;
5925 offsetInBlock
= 0; /* There is no offset on the next block */
5927 if (bytesToReadInBuffer
!= bytesReadAt
)
5934 /******************************************************************************
5935 * BlockChainStream_WriteAt
5937 * Writes the specified number of bytes to this chain at the specified offset.
5938 * Will fail if not all specified number of bytes have been written.
5940 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5941 ULARGE_INTEGER offset
,
5944 ULONG
* bytesWritten
)
5946 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5947 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5950 const BYTE
* bufferWalker
;
5953 * Find the first block in the stream that contains part of the buffer.
5955 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5957 /* BlockChainStream_SetSize should have already been called to ensure we have
5958 * enough blocks in the chain to write into */
5959 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5961 ERR("not enough blocks in chain to write data\n");
5962 return STG_E_DOCFILECORRUPT
;
5966 bufferWalker
= buffer
;
5968 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5970 ULARGE_INTEGER ulOffset
;
5971 DWORD bytesWrittenAt
;
5973 * Calculate how many bytes we can copy from this big block.
5976 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5978 TRACE("block %i\n",blockIndex
);
5979 ulOffset
.u
.HighPart
= 0;
5980 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5983 StorageImpl_WriteAt(This
->parentStorage
,
5990 * Step to the next big block.
5992 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5994 return STG_E_DOCFILECORRUPT
;
5996 bufferWalker
+= bytesWrittenAt
;
5997 size
-= bytesWrittenAt
;
5998 *bytesWritten
+= bytesWrittenAt
;
5999 offsetInBlock
= 0; /* There is no offset on the next block */
6001 if (bytesWrittenAt
!= bytesToWrite
)
6005 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6008 /******************************************************************************
6009 * BlockChainStream_Shrink
6011 * Shrinks this chain in the big block depot.
6013 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6014 ULARGE_INTEGER newSize
)
6020 * Figure out how many blocks are needed to contain the new size
6022 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6024 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6030 * Go to the new end of chain
6032 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6034 /* Mark the new end of chain */
6035 StorageImpl_SetNextBlockInChain(
6036 This
->parentStorage
,
6038 BLOCK_END_OF_CHAIN
);
6040 This
->tailIndex
= blockIndex
;
6044 if (This
->headOfStreamPlaceHolder
!= 0)
6046 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6050 DirEntry chainEntry
;
6051 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6053 StorageImpl_ReadDirEntry(
6054 This
->parentStorage
,
6055 This
->ownerDirEntry
,
6058 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6060 StorageImpl_WriteDirEntry(
6061 This
->parentStorage
,
6062 This
->ownerDirEntry
,
6066 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6069 This
->numBlocks
= numBlocks
;
6072 * Mark the extra blocks as free
6074 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6076 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6077 StorageImpl_FreeBigBlock(This
->parentStorage
,
6078 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6079 if (last_run
->lastOffset
== last_run
->firstOffset
)
6080 This
->indexCacheLen
--;
6082 last_run
->lastOffset
--;
6088 /******************************************************************************
6089 * BlockChainStream_Enlarge
6091 * Grows this chain in the big block depot.
6093 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6094 ULARGE_INTEGER newSize
)
6096 ULONG blockIndex
, currentBlock
;
6098 ULONG oldNumBlocks
= 0;
6100 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6103 * Empty chain. Create the head.
6105 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6107 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6108 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6110 BLOCK_END_OF_CHAIN
);
6112 if (This
->headOfStreamPlaceHolder
!= 0)
6114 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6118 DirEntry chainEntry
;
6119 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6121 StorageImpl_ReadDirEntry(
6122 This
->parentStorage
,
6123 This
->ownerDirEntry
,
6126 chainEntry
.startingBlock
= blockIndex
;
6128 StorageImpl_WriteDirEntry(
6129 This
->parentStorage
,
6130 This
->ownerDirEntry
,
6134 This
->tailIndex
= blockIndex
;
6135 This
->numBlocks
= 1;
6139 * Figure out how many blocks are needed to contain this stream
6141 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6143 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6147 * Go to the current end of chain
6149 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6151 currentBlock
= blockIndex
;
6153 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6156 currentBlock
= blockIndex
;
6158 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6163 This
->tailIndex
= currentBlock
;
6166 currentBlock
= This
->tailIndex
;
6167 oldNumBlocks
= This
->numBlocks
;
6170 * Add new blocks to the chain
6172 if (oldNumBlocks
< newNumBlocks
)
6174 while (oldNumBlocks
< newNumBlocks
)
6176 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6178 StorageImpl_SetNextBlockInChain(
6179 This
->parentStorage
,
6183 StorageImpl_SetNextBlockInChain(
6184 This
->parentStorage
,
6186 BLOCK_END_OF_CHAIN
);
6188 currentBlock
= blockIndex
;
6192 This
->tailIndex
= blockIndex
;
6193 This
->numBlocks
= newNumBlocks
;
6196 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6202 /******************************************************************************
6203 * BlockChainStream_SetSize
6205 * Sets the size of this stream. The big block depot will be updated.
6206 * The file will grow if we grow the chain.
6208 * TODO: Free the actual blocks in the file when we shrink the chain.
6209 * Currently, the blocks are still in the file. So the file size
6210 * doesn't shrink even if we shrink streams.
6212 BOOL
BlockChainStream_SetSize(
6213 BlockChainStream
* This
,
6214 ULARGE_INTEGER newSize
)
6216 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6218 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6221 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6223 BlockChainStream_Shrink(This
, newSize
);
6227 BlockChainStream_Enlarge(This
, newSize
);
6233 /******************************************************************************
6234 * BlockChainStream_GetSize
6236 * Returns the size of this chain.
6237 * Will return the block count if this chain doesn't have a directory entry.
6239 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6241 DirEntry chainEntry
;
6243 if(This
->headOfStreamPlaceHolder
== NULL
)
6246 * This chain has a directory entry so use the size value from there.
6248 StorageImpl_ReadDirEntry(
6249 This
->parentStorage
,
6250 This
->ownerDirEntry
,
6253 return chainEntry
.size
;
6258 * this chain is a chain that does not have a directory entry, figure out the
6259 * size by making the product number of used blocks times the
6262 ULARGE_INTEGER result
;
6263 result
.u
.HighPart
= 0;
6266 BlockChainStream_GetCount(This
) *
6267 This
->parentStorage
->bigBlockSize
;
6273 /******************************************************************************
6274 ** SmallBlockChainStream implementation
6277 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6278 StorageImpl
* parentStorage
,
6279 ULONG
* headOfStreamPlaceHolder
,
6282 SmallBlockChainStream
* newStream
;
6284 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6286 newStream
->parentStorage
= parentStorage
;
6287 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6288 newStream
->ownerDirEntry
= dirEntry
;
6293 void SmallBlockChainStream_Destroy(
6294 SmallBlockChainStream
* This
)
6296 HeapFree(GetProcessHeap(), 0, This
);
6299 /******************************************************************************
6300 * SmallBlockChainStream_GetHeadOfChain
6302 * Returns the head of this chain of small blocks.
6304 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6305 SmallBlockChainStream
* This
)
6307 DirEntry chainEntry
;
6310 if (This
->headOfStreamPlaceHolder
!= NULL
)
6311 return *(This
->headOfStreamPlaceHolder
);
6313 if (This
->ownerDirEntry
)
6315 hr
= StorageImpl_ReadDirEntry(
6316 This
->parentStorage
,
6317 This
->ownerDirEntry
,
6322 return chainEntry
.startingBlock
;
6327 return BLOCK_END_OF_CHAIN
;
6330 /******************************************************************************
6331 * SmallBlockChainStream_GetNextBlockInChain
6333 * Returns the index of the next small block in this chain.
6336 * - BLOCK_END_OF_CHAIN: end of this chain
6337 * - BLOCK_UNUSED: small block 'blockIndex' is free
6339 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6340 SmallBlockChainStream
* This
,
6342 ULONG
* nextBlockInChain
)
6344 ULARGE_INTEGER offsetOfBlockInDepot
;
6349 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6351 offsetOfBlockInDepot
.u
.HighPart
= 0;
6352 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6355 * Read those bytes in the buffer from the small block file.
6357 res
= BlockChainStream_ReadAt(
6358 This
->parentStorage
->smallBlockDepotChain
,
6359 offsetOfBlockInDepot
,
6364 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6365 res
= STG_E_READFAULT
;
6369 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6376 /******************************************************************************
6377 * SmallBlockChainStream_SetNextBlockInChain
6379 * Writes the index of the next block of the specified block in the small
6381 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6382 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6384 static void SmallBlockChainStream_SetNextBlockInChain(
6385 SmallBlockChainStream
* This
,
6389 ULARGE_INTEGER offsetOfBlockInDepot
;
6393 offsetOfBlockInDepot
.u
.HighPart
= 0;
6394 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6396 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6399 * Read those bytes in the buffer from the small block file.
6401 BlockChainStream_WriteAt(
6402 This
->parentStorage
->smallBlockDepotChain
,
6403 offsetOfBlockInDepot
,
6409 /******************************************************************************
6410 * SmallBlockChainStream_FreeBlock
6412 * Flag small block 'blockIndex' as free in the small block depot.
6414 static void SmallBlockChainStream_FreeBlock(
6415 SmallBlockChainStream
* This
,
6418 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6421 /******************************************************************************
6422 * SmallBlockChainStream_GetNextFreeBlock
6424 * Returns the index of a free small block. The small block depot will be
6425 * enlarged if necessary. The small block chain will also be enlarged if
6428 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6429 SmallBlockChainStream
* This
)
6431 ULARGE_INTEGER offsetOfBlockInDepot
;
6434 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6435 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6437 ULONG smallBlocksPerBigBlock
;
6439 ULONG blocksRequired
;
6440 ULARGE_INTEGER old_size
, size_required
;
6442 offsetOfBlockInDepot
.u
.HighPart
= 0;
6445 * Scan the small block depot for a free block
6447 while (nextBlockIndex
!= BLOCK_UNUSED
)
6449 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6451 res
= BlockChainStream_ReadAt(
6452 This
->parentStorage
->smallBlockDepotChain
,
6453 offsetOfBlockInDepot
,
6459 * If we run out of space for the small block depot, enlarge it
6461 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6463 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6465 if (nextBlockIndex
!= BLOCK_UNUSED
)
6471 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6473 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6474 ULARGE_INTEGER newSize
, offset
;
6477 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6478 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6481 * Initialize all the small blocks to free
6483 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6484 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6485 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6486 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6488 StorageImpl_SaveFileHeader(This
->parentStorage
);
6492 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6494 smallBlocksPerBigBlock
=
6495 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6498 * Verify if we have to allocate big blocks to contain small blocks
6500 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6502 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6504 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6506 if (size_required
.QuadPart
> old_size
.QuadPart
)
6508 BlockChainStream_SetSize(
6509 This
->parentStorage
->smallBlockRootChain
,
6512 StorageImpl_ReadDirEntry(
6513 This
->parentStorage
,
6514 This
->parentStorage
->base
.storageDirEntry
,
6517 rootEntry
.size
= size_required
;
6519 StorageImpl_WriteDirEntry(
6520 This
->parentStorage
,
6521 This
->parentStorage
->base
.storageDirEntry
,
6528 /******************************************************************************
6529 * SmallBlockChainStream_ReadAt
6531 * Reads a specified number of bytes from this chain at the specified offset.
6532 * bytesRead may be NULL.
6533 * Failure will be returned if the specified number of bytes has not been read.
6535 HRESULT
SmallBlockChainStream_ReadAt(
6536 SmallBlockChainStream
* This
,
6537 ULARGE_INTEGER offset
,
6543 ULARGE_INTEGER offsetInBigBlockFile
;
6544 ULONG blockNoInSequence
=
6545 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6547 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6548 ULONG bytesToReadInBuffer
;
6550 ULONG bytesReadFromBigBlockFile
;
6552 ULARGE_INTEGER stream_size
;
6555 * This should never happen on a small block file.
6557 assert(offset
.u
.HighPart
==0);
6561 stream_size
= SmallBlockChainStream_GetSize(This
);
6562 if (stream_size
.QuadPart
> offset
.QuadPart
)
6563 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6568 * Find the first block in the stream that contains part of the buffer.
6570 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6572 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6574 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6577 blockNoInSequence
--;
6581 * Start reading the buffer.
6583 bufferWalker
= buffer
;
6585 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6588 * Calculate how many bytes we can copy from this small block.
6590 bytesToReadInBuffer
=
6591 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6594 * Calculate the offset of the small block in the small block file.
6596 offsetInBigBlockFile
.u
.HighPart
= 0;
6597 offsetInBigBlockFile
.u
.LowPart
=
6598 blockIndex
* This
->parentStorage
->smallBlockSize
;
6600 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6603 * Read those bytes in the buffer from the small block file.
6604 * The small block has already been identified so it shouldn't fail
6605 * unless the file is corrupt.
6607 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6608 offsetInBigBlockFile
,
6609 bytesToReadInBuffer
,
6611 &bytesReadFromBigBlockFile
);
6616 if (!bytesReadFromBigBlockFile
)
6617 return STG_E_DOCFILECORRUPT
;
6620 * Step to the next big block.
6622 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6624 return STG_E_DOCFILECORRUPT
;
6626 bufferWalker
+= bytesReadFromBigBlockFile
;
6627 size
-= bytesReadFromBigBlockFile
;
6628 *bytesRead
+= bytesReadFromBigBlockFile
;
6629 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6635 /******************************************************************************
6636 * SmallBlockChainStream_WriteAt
6638 * Writes the specified number of bytes to this chain at the specified offset.
6639 * Will fail if not all specified number of bytes have been written.
6641 HRESULT
SmallBlockChainStream_WriteAt(
6642 SmallBlockChainStream
* This
,
6643 ULARGE_INTEGER offset
,
6646 ULONG
* bytesWritten
)
6648 ULARGE_INTEGER offsetInBigBlockFile
;
6649 ULONG blockNoInSequence
=
6650 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6652 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6653 ULONG bytesToWriteInBuffer
;
6655 ULONG bytesWrittenToBigBlockFile
;
6656 const BYTE
* bufferWalker
;
6660 * This should never happen on a small block file.
6662 assert(offset
.u
.HighPart
==0);
6665 * Find the first block in the stream that contains part of the buffer.
6667 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6669 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6671 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6672 return STG_E_DOCFILECORRUPT
;
6673 blockNoInSequence
--;
6677 * Start writing the buffer.
6680 bufferWalker
= buffer
;
6681 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6684 * Calculate how many bytes we can copy to this small block.
6686 bytesToWriteInBuffer
=
6687 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6690 * Calculate the offset of the small block in the small block file.
6692 offsetInBigBlockFile
.u
.HighPart
= 0;
6693 offsetInBigBlockFile
.u
.LowPart
=
6694 blockIndex
* This
->parentStorage
->smallBlockSize
;
6696 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6699 * Write those bytes in the buffer to the small block file.
6701 res
= BlockChainStream_WriteAt(
6702 This
->parentStorage
->smallBlockRootChain
,
6703 offsetInBigBlockFile
,
6704 bytesToWriteInBuffer
,
6706 &bytesWrittenToBigBlockFile
);
6711 * Step to the next big block.
6713 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6716 bufferWalker
+= bytesWrittenToBigBlockFile
;
6717 size
-= bytesWrittenToBigBlockFile
;
6718 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6719 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6722 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6725 /******************************************************************************
6726 * SmallBlockChainStream_Shrink
6728 * Shrinks this chain in the small block depot.
6730 static BOOL
SmallBlockChainStream_Shrink(
6731 SmallBlockChainStream
* This
,
6732 ULARGE_INTEGER newSize
)
6734 ULONG blockIndex
, extraBlock
;
6738 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6740 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6743 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6746 * Go to the new end of chain
6748 while (count
< numBlocks
)
6750 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6757 * If the count is 0, we have a special case, the head of the chain was
6762 DirEntry chainEntry
;
6764 StorageImpl_ReadDirEntry(This
->parentStorage
,
6765 This
->ownerDirEntry
,
6768 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6770 StorageImpl_WriteDirEntry(This
->parentStorage
,
6771 This
->ownerDirEntry
,
6775 * We start freeing the chain at the head block.
6777 extraBlock
= blockIndex
;
6781 /* Get the next block before marking the new end */
6782 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6786 /* Mark the new end of chain */
6787 SmallBlockChainStream_SetNextBlockInChain(
6790 BLOCK_END_OF_CHAIN
);
6794 * Mark the extra blocks as free
6796 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6798 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6801 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6802 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
6803 extraBlock
= blockIndex
;
6809 /******************************************************************************
6810 * SmallBlockChainStream_Enlarge
6812 * Grows this chain in the small block depot.
6814 static BOOL
SmallBlockChainStream_Enlarge(
6815 SmallBlockChainStream
* This
,
6816 ULARGE_INTEGER newSize
)
6818 ULONG blockIndex
, currentBlock
;
6820 ULONG oldNumBlocks
= 0;
6822 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6825 * Empty chain. Create the head.
6827 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6829 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6830 SmallBlockChainStream_SetNextBlockInChain(
6833 BLOCK_END_OF_CHAIN
);
6835 if (This
->headOfStreamPlaceHolder
!= NULL
)
6837 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6841 DirEntry chainEntry
;
6843 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6846 chainEntry
.startingBlock
= blockIndex
;
6848 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6853 currentBlock
= blockIndex
;
6856 * Figure out how many blocks are needed to contain this stream
6858 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6860 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6864 * Go to the current end of chain
6866 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6869 currentBlock
= blockIndex
;
6870 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6875 * Add new blocks to the chain
6877 while (oldNumBlocks
< newNumBlocks
)
6879 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6880 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6882 SmallBlockChainStream_SetNextBlockInChain(
6885 BLOCK_END_OF_CHAIN
);
6887 currentBlock
= blockIndex
;
6894 /******************************************************************************
6895 * SmallBlockChainStream_SetSize
6897 * Sets the size of this stream.
6898 * The file will grow if we grow the chain.
6900 * TODO: Free the actual blocks in the file when we shrink the chain.
6901 * Currently, the blocks are still in the file. So the file size
6902 * doesn't shrink even if we shrink streams.
6904 BOOL
SmallBlockChainStream_SetSize(
6905 SmallBlockChainStream
* This
,
6906 ULARGE_INTEGER newSize
)
6908 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6910 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6913 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6915 SmallBlockChainStream_Shrink(This
, newSize
);
6919 SmallBlockChainStream_Enlarge(This
, newSize
);
6925 /******************************************************************************
6926 * SmallBlockChainStream_GetCount
6928 * Returns the number of small blocks that comprises this chain.
6929 * This is not the size of the stream as the last block may not be full!
6932 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6937 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6939 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6943 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6944 blockIndex
, &blockIndex
)))
6951 /******************************************************************************
6952 * SmallBlockChainStream_GetSize
6954 * Returns the size of this chain.
6956 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6958 DirEntry chainEntry
;
6960 if(This
->headOfStreamPlaceHolder
!= NULL
)
6962 ULARGE_INTEGER result
;
6963 result
.u
.HighPart
= 0;
6965 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6966 This
->parentStorage
->smallBlockSize
;
6971 StorageImpl_ReadDirEntry(
6972 This
->parentStorage
,
6973 This
->ownerDirEntry
,
6976 return chainEntry
.size
;
6979 static HRESULT
create_storagefile(
6983 STGOPTIONS
* pStgOptions
,
6987 StorageBaseImpl
* newStorage
= 0;
6988 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6989 HRESULT hr
= STG_E_INVALIDFLAG
;
6993 DWORD fileAttributes
;
6994 WCHAR tempFileName
[MAX_PATH
];
6997 return STG_E_INVALIDPOINTER
;
6999 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7000 return STG_E_INVALIDPARAMETER
;
7002 /* if no share mode given then DENY_NONE is the default */
7003 if (STGM_SHARE_MODE(grfMode
) == 0)
7004 grfMode
|= STGM_SHARE_DENY_NONE
;
7006 if ( FAILED( validateSTGM(grfMode
) ))
7009 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7010 switch(STGM_ACCESS_MODE(grfMode
))
7013 case STGM_READWRITE
:
7019 /* in direct mode, can only use SHARE_EXCLUSIVE */
7020 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7023 /* but in transacted mode, any share mode is valid */
7026 * Generate a unique name.
7030 WCHAR tempPath
[MAX_PATH
];
7031 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7033 memset(tempPath
, 0, sizeof(tempPath
));
7034 memset(tempFileName
, 0, sizeof(tempFileName
));
7036 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7039 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7040 pwcsName
= tempFileName
;
7043 hr
= STG_E_INSUFFICIENTMEMORY
;
7047 creationMode
= TRUNCATE_EXISTING
;
7051 creationMode
= GetCreationModeFromSTGM(grfMode
);
7055 * Interpret the STGM value grfMode
7057 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7058 accessMode
= GetAccessModeFromSTGM(grfMode
);
7060 if (grfMode
& STGM_DELETEONRELEASE
)
7061 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7063 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7065 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7069 FIXME("Storage share mode not implemented.\n");
7074 hFile
= CreateFileW(pwcsName
,
7082 if (hFile
== INVALID_HANDLE_VALUE
)
7084 if(GetLastError() == ERROR_FILE_EXISTS
)
7085 hr
= STG_E_FILEALREADYEXISTS
;
7092 * Allocate and initialize the new IStorage32object.
7094 hr
= Storage_Construct(
7101 pStgOptions
->ulSectorSize
,
7109 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
7111 IStorage_Release((IStorage
*)newStorage
);
7114 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7119 /******************************************************************************
7120 * StgCreateDocfile [OLE32.@]
7121 * Creates a new compound file storage object
7124 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7125 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7126 * reserved [ ?] unused?, usually 0
7127 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7130 * S_OK if the file was successfully created
7131 * some STG_E_ value if error
7133 * if pwcsName is NULL, create file with new unique name
7134 * the function can returns
7135 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7138 HRESULT WINAPI
StgCreateDocfile(
7142 IStorage
**ppstgOpen
)
7144 STGOPTIONS stgoptions
= {1, 0, 512};
7146 TRACE("(%s, %x, %d, %p)\n",
7147 debugstr_w(pwcsName
), grfMode
,
7148 reserved
, ppstgOpen
);
7151 return STG_E_INVALIDPOINTER
;
7153 return STG_E_INVALIDPARAMETER
;
7155 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7158 /******************************************************************************
7159 * StgCreateStorageEx [OLE32.@]
7161 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7163 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7164 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7166 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7168 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7169 return STG_E_INVALIDPARAMETER
;
7172 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7174 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7175 return STG_E_INVALIDPARAMETER
;
7178 if (stgfmt
== STGFMT_FILE
)
7180 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7181 return STG_E_INVALIDPARAMETER
;
7184 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7186 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7190 ERR("Invalid stgfmt argument\n");
7191 return STG_E_INVALIDPARAMETER
;
7194 /******************************************************************************
7195 * StgCreatePropSetStg [OLE32.@]
7197 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7198 IPropertySetStorage
**ppPropSetStg
)
7202 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
7204 hr
= STG_E_INVALIDPARAMETER
;
7206 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
7207 (void**)ppPropSetStg
);
7211 /******************************************************************************
7212 * StgOpenStorageEx [OLE32.@]
7214 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7216 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7217 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7219 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7221 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7222 return STG_E_INVALIDPARAMETER
;
7228 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7229 return STG_E_INVALIDPARAMETER
;
7231 case STGFMT_STORAGE
:
7234 case STGFMT_DOCFILE
:
7235 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7237 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7238 return STG_E_INVALIDPARAMETER
;
7240 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7244 WARN("STGFMT_ANY assuming storage\n");
7248 return STG_E_INVALIDPARAMETER
;
7251 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7255 /******************************************************************************
7256 * StgOpenStorage [OLE32.@]
7258 HRESULT WINAPI
StgOpenStorage(
7259 const OLECHAR
*pwcsName
,
7260 IStorage
*pstgPriority
,
7264 IStorage
**ppstgOpen
)
7266 StorageBaseImpl
* newStorage
= 0;
7272 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7273 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7274 snbExclude
, reserved
, ppstgOpen
);
7278 hr
= STG_E_INVALIDNAME
;
7284 hr
= STG_E_INVALIDPOINTER
;
7290 hr
= STG_E_INVALIDPARAMETER
;
7294 if (grfMode
& STGM_PRIORITY
)
7296 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7297 return STG_E_INVALIDFLAG
;
7298 if (grfMode
& STGM_DELETEONRELEASE
)
7299 return STG_E_INVALIDFUNCTION
;
7300 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7301 return STG_E_INVALIDFLAG
;
7302 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7303 grfMode
|= STGM_SHARE_DENY_NONE
;
7305 /* STGM_PRIORITY stops other IStorage objects on the same file from
7306 * committing until the STGM_PRIORITY IStorage is closed. it also
7307 * stops non-transacted mode StgOpenStorage calls with write access from
7308 * succeeding. obviously, both of these cannot be achieved through just
7309 * file share flags */
7310 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7314 * Validate the sharing mode
7316 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7317 switch(STGM_SHARE_MODE(grfMode
))
7319 case STGM_SHARE_EXCLUSIVE
:
7320 case STGM_SHARE_DENY_WRITE
:
7323 hr
= STG_E_INVALIDFLAG
;
7327 if ( FAILED( validateSTGM(grfMode
) ) ||
7328 (grfMode
&STGM_CREATE
))
7330 hr
= STG_E_INVALIDFLAG
;
7334 /* shared reading requires transacted mode */
7335 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7336 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7337 !(grfMode
&STGM_TRANSACTED
) )
7339 hr
= STG_E_INVALIDFLAG
;
7344 * Interpret the STGM value grfMode
7346 shareMode
= GetShareModeFromSTGM(grfMode
);
7347 accessMode
= GetAccessModeFromSTGM(grfMode
);
7351 hFile
= CreateFileW( pwcsName
,
7356 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7359 if (hFile
==INVALID_HANDLE_VALUE
)
7361 DWORD last_error
= GetLastError();
7367 case ERROR_FILE_NOT_FOUND
:
7368 hr
= STG_E_FILENOTFOUND
;
7371 case ERROR_PATH_NOT_FOUND
:
7372 hr
= STG_E_PATHNOTFOUND
;
7375 case ERROR_ACCESS_DENIED
:
7376 case ERROR_WRITE_PROTECT
:
7377 hr
= STG_E_ACCESSDENIED
;
7380 case ERROR_SHARING_VIOLATION
:
7381 hr
= STG_E_SHAREVIOLATION
;
7392 * Refuse to open the file if it's too small to be a structured storage file
7393 * FIXME: verify the file when reading instead of here
7395 if (GetFileSize(hFile
, NULL
) < 0x100)
7398 hr
= STG_E_FILEALREADYEXISTS
;
7403 * Allocate and initialize the new IStorage32object.
7405 hr
= Storage_Construct(
7418 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7420 if(hr
== STG_E_INVALIDHEADER
)
7421 hr
= STG_E_FILEALREADYEXISTS
;
7426 * Get an "out" pointer for the caller.
7428 *ppstgOpen
= (IStorage
*)newStorage
;
7431 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7435 /******************************************************************************
7436 * StgCreateDocfileOnILockBytes [OLE32.@]
7438 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7442 IStorage
** ppstgOpen
)
7444 StorageBaseImpl
* newStorage
= 0;
7447 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7448 return STG_E_INVALIDPOINTER
;
7451 * Allocate and initialize the new IStorage object.
7453 hr
= Storage_Construct(
7469 * Get an "out" pointer for the caller.
7471 *ppstgOpen
= (IStorage
*)newStorage
;
7476 /******************************************************************************
7477 * StgOpenStorageOnILockBytes [OLE32.@]
7479 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7481 IStorage
*pstgPriority
,
7485 IStorage
**ppstgOpen
)
7487 StorageBaseImpl
* newStorage
= 0;
7490 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7491 return STG_E_INVALIDPOINTER
;
7493 if ( FAILED( validateSTGM(grfMode
) ))
7494 return STG_E_INVALIDFLAG
;
7499 * Allocate and initialize the new IStorage object.
7501 hr
= Storage_Construct(
7517 * Get an "out" pointer for the caller.
7519 *ppstgOpen
= (IStorage
*)newStorage
;
7524 /******************************************************************************
7525 * StgSetTimes [ole32.@]
7526 * StgSetTimes [OLE32.@]
7530 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7531 FILETIME
const *patime
, FILETIME
const *pmtime
)
7533 IStorage
*stg
= NULL
;
7536 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7538 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7542 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7543 IStorage_Release(stg
);
7549 /******************************************************************************
7550 * StgIsStorageILockBytes [OLE32.@]
7552 * Determines if the ILockBytes contains a storage object.
7554 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7557 ULARGE_INTEGER offset
;
7559 offset
.u
.HighPart
= 0;
7560 offset
.u
.LowPart
= 0;
7562 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
7564 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
7570 /******************************************************************************
7571 * WriteClassStg [OLE32.@]
7573 * This method will store the specified CLSID in the specified storage object
7575 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7580 return E_INVALIDARG
;
7583 return STG_E_INVALIDPOINTER
;
7585 hRes
= IStorage_SetClass(pStg
, rclsid
);
7590 /***********************************************************************
7591 * ReadClassStg (OLE32.@)
7593 * This method reads the CLSID previously written to a storage object with
7594 * the WriteClassStg.
7597 * pstg [I] IStorage pointer
7598 * pclsid [O] Pointer to where the CLSID is written
7602 * Failure: HRESULT code.
7604 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7609 TRACE("(%p, %p)\n", pstg
, pclsid
);
7611 if(!pstg
|| !pclsid
)
7612 return E_INVALIDARG
;
7615 * read a STATSTG structure (contains the clsid) from the storage
7617 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7620 *pclsid
=pstatstg
.clsid
;
7625 /***********************************************************************
7626 * OleLoadFromStream (OLE32.@)
7628 * This function loads an object from stream
7630 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7634 LPPERSISTSTREAM xstm
;
7636 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7638 res
=ReadClassStm(pStm
,&clsid
);
7641 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7644 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7646 IUnknown_Release((IUnknown
*)*ppvObj
);
7649 res
=IPersistStream_Load(xstm
,pStm
);
7650 IPersistStream_Release(xstm
);
7651 /* FIXME: all refcounts ok at this point? I think they should be:
7654 * xstm : 0 (released)
7659 /***********************************************************************
7660 * OleSaveToStream (OLE32.@)
7662 * This function saves an object with the IPersistStream interface on it
7663 * to the specified stream.
7665 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7671 TRACE("(%p,%p)\n",pPStm
,pStm
);
7673 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7675 if (SUCCEEDED(res
)){
7677 res
=WriteClassStm(pStm
,&clsid
);
7681 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7684 TRACE("Finished Save\n");
7688 /****************************************************************************
7689 * This method validate a STGM parameter that can contain the values below
7691 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7692 * The stgm values contained in 0xffff0000 are bitmasks.
7694 * STGM_DIRECT 0x00000000
7695 * STGM_TRANSACTED 0x00010000
7696 * STGM_SIMPLE 0x08000000
7698 * STGM_READ 0x00000000
7699 * STGM_WRITE 0x00000001
7700 * STGM_READWRITE 0x00000002
7702 * STGM_SHARE_DENY_NONE 0x00000040
7703 * STGM_SHARE_DENY_READ 0x00000030
7704 * STGM_SHARE_DENY_WRITE 0x00000020
7705 * STGM_SHARE_EXCLUSIVE 0x00000010
7707 * STGM_PRIORITY 0x00040000
7708 * STGM_DELETEONRELEASE 0x04000000
7710 * STGM_CREATE 0x00001000
7711 * STGM_CONVERT 0x00020000
7712 * STGM_FAILIFTHERE 0x00000000
7714 * STGM_NOSCRATCH 0x00100000
7715 * STGM_NOSNAPSHOT 0x00200000
7717 static HRESULT
validateSTGM(DWORD stgm
)
7719 DWORD access
= STGM_ACCESS_MODE(stgm
);
7720 DWORD share
= STGM_SHARE_MODE(stgm
);
7721 DWORD create
= STGM_CREATE_MODE(stgm
);
7723 if (stgm
&~STGM_KNOWN_FLAGS
)
7725 ERR("unknown flags %08x\n", stgm
);
7733 case STGM_READWRITE
:
7741 case STGM_SHARE_DENY_NONE
:
7742 case STGM_SHARE_DENY_READ
:
7743 case STGM_SHARE_DENY_WRITE
:
7744 case STGM_SHARE_EXCLUSIVE
:
7753 case STGM_FAILIFTHERE
:
7760 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7762 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7766 * STGM_CREATE | STGM_CONVERT
7767 * if both are false, STGM_FAILIFTHERE is set to TRUE
7769 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7773 * STGM_NOSCRATCH requires STGM_TRANSACTED
7775 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7779 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7780 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7782 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7783 (!(stgm
& STGM_TRANSACTED
) ||
7784 share
== STGM_SHARE_EXCLUSIVE
||
7785 share
== STGM_SHARE_DENY_WRITE
) )
7791 /****************************************************************************
7792 * GetShareModeFromSTGM
7794 * This method will return a share mode flag from a STGM value.
7795 * The STGM value is assumed valid.
7797 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7799 switch (STGM_SHARE_MODE(stgm
))
7801 case STGM_SHARE_DENY_NONE
:
7802 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7803 case STGM_SHARE_DENY_READ
:
7804 return FILE_SHARE_WRITE
;
7805 case STGM_SHARE_DENY_WRITE
:
7806 return FILE_SHARE_READ
;
7807 case STGM_SHARE_EXCLUSIVE
:
7810 ERR("Invalid share mode!\n");
7815 /****************************************************************************
7816 * GetAccessModeFromSTGM
7818 * This method will return an access mode flag from a STGM value.
7819 * The STGM value is assumed valid.
7821 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7823 switch (STGM_ACCESS_MODE(stgm
))
7826 return GENERIC_READ
;
7828 case STGM_READWRITE
:
7829 return GENERIC_READ
| GENERIC_WRITE
;
7831 ERR("Invalid access mode!\n");
7836 /****************************************************************************
7837 * GetCreationModeFromSTGM
7839 * This method will return a creation mode flag from a STGM value.
7840 * The STGM value is assumed valid.
7842 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7844 switch(STGM_CREATE_MODE(stgm
))
7847 return CREATE_ALWAYS
;
7849 FIXME("STGM_CONVERT not implemented!\n");
7851 case STGM_FAILIFTHERE
:
7854 ERR("Invalid create mode!\n");
7860 /*************************************************************************
7861 * OLECONVERT_LoadOLE10 [Internal]
7863 * Loads the OLE10 STREAM to memory
7866 * pOleStream [I] The OLESTREAM
7867 * pData [I] Data Structure for the OLESTREAM Data
7871 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7872 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7875 * This function is used by OleConvertOLESTREAMToIStorage only.
7877 * Memory allocated for pData must be freed by the caller
7879 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7882 HRESULT hRes
= S_OK
;
7886 pData
->pData
= NULL
;
7887 pData
->pstrOleObjFileName
= NULL
;
7889 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7892 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7893 if(dwSize
!= sizeof(pData
->dwOleID
))
7895 hRes
= CONVERT10_E_OLESTREAM_GET
;
7897 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7899 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7910 /* Get the TypeID... more info needed for this field */
7911 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7912 if(dwSize
!= sizeof(pData
->dwTypeID
))
7914 hRes
= CONVERT10_E_OLESTREAM_GET
;
7919 if(pData
->dwTypeID
!= 0)
7921 /* Get the length of the OleTypeName */
7922 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7923 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7925 hRes
= CONVERT10_E_OLESTREAM_GET
;
7930 if(pData
->dwOleTypeNameLength
> 0)
7932 /* Get the OleTypeName */
7933 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7934 if(dwSize
!= pData
->dwOleTypeNameLength
)
7936 hRes
= CONVERT10_E_OLESTREAM_GET
;
7942 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7943 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7945 hRes
= CONVERT10_E_OLESTREAM_GET
;
7949 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7950 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7951 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7952 if(pData
->pstrOleObjFileName
)
7954 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7955 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7957 hRes
= CONVERT10_E_OLESTREAM_GET
;
7961 hRes
= CONVERT10_E_OLESTREAM_GET
;
7966 /* Get the Width of the Metafile */
7967 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7968 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7970 hRes
= CONVERT10_E_OLESTREAM_GET
;
7974 /* Get the Height of the Metafile */
7975 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7976 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7978 hRes
= CONVERT10_E_OLESTREAM_GET
;
7984 /* Get the Length of the Data */
7985 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7986 if(dwSize
!= sizeof(pData
->dwDataLength
))
7988 hRes
= CONVERT10_E_OLESTREAM_GET
;
7992 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7994 if(!bStrem1
) /* if it is a second OLE stream data */
7996 pData
->dwDataLength
-= 8;
7997 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7998 if(dwSize
!= sizeof(pData
->strUnknown
))
8000 hRes
= CONVERT10_E_OLESTREAM_GET
;
8006 if(pData
->dwDataLength
> 0)
8008 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8010 /* Get Data (ex. IStorage, Metafile, or BMP) */
8013 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8014 if(dwSize
!= pData
->dwDataLength
)
8016 hRes
= CONVERT10_E_OLESTREAM_GET
;
8021 hRes
= CONVERT10_E_OLESTREAM_GET
;
8030 /*************************************************************************
8031 * OLECONVERT_SaveOLE10 [Internal]
8033 * Saves the OLE10 STREAM From memory
8036 * pData [I] Data Structure for the OLESTREAM Data
8037 * pOleStream [I] The OLESTREAM to save
8041 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8044 * This function is used by OleConvertIStorageToOLESTREAM only.
8047 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8050 HRESULT hRes
= S_OK
;
8054 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8055 if(dwSize
!= sizeof(pData
->dwOleID
))
8057 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8062 /* Set the TypeID */
8063 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8064 if(dwSize
!= sizeof(pData
->dwTypeID
))
8066 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8070 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8072 /* Set the Length of the OleTypeName */
8073 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8074 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8076 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8081 if(pData
->dwOleTypeNameLength
> 0)
8083 /* Set the OleTypeName */
8084 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8085 if(dwSize
!= pData
->dwOleTypeNameLength
)
8087 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8094 /* Set the width of the Metafile */
8095 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8096 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8098 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8104 /* Set the height of the Metafile */
8105 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8106 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8108 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8114 /* Set the length of the Data */
8115 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8116 if(dwSize
!= sizeof(pData
->dwDataLength
))
8118 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8124 if(pData
->dwDataLength
> 0)
8126 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8127 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8128 if(dwSize
!= pData
->dwDataLength
)
8130 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8138 /*************************************************************************
8139 * OLECONVERT_GetOLE20FromOLE10[Internal]
8141 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8142 * opens it, and copies the content to the dest IStorage for
8143 * OleConvertOLESTREAMToIStorage
8147 * pDestStorage [I] The IStorage to copy the data to
8148 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8149 * nBufferLength [I] The size of the buffer
8158 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8162 IStorage
*pTempStorage
;
8163 DWORD dwNumOfBytesWritten
;
8164 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8165 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8167 /* Create a temp File */
8168 GetTempPathW(MAX_PATH
, wstrTempDir
);
8169 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8170 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8172 if(hFile
!= INVALID_HANDLE_VALUE
)
8174 /* Write IStorage Data to File */
8175 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8178 /* Open and copy temp storage to the Dest Storage */
8179 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8182 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8183 IStorage_Release(pTempStorage
);
8185 DeleteFileW(wstrTempFile
);
8190 /*************************************************************************
8191 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8193 * Saves the OLE10 STREAM From memory
8196 * pStorage [I] The Src IStorage to copy
8197 * pData [I] The Dest Memory to write to.
8200 * The size in bytes allocated for pData
8203 * Memory allocated for pData must be freed by the caller
8205 * Used by OleConvertIStorageToOLESTREAM only.
8208 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8212 DWORD nDataLength
= 0;
8213 IStorage
*pTempStorage
;
8214 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8215 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8219 /* Create temp Storage */
8220 GetTempPathW(MAX_PATH
, wstrTempDir
);
8221 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8222 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8226 /* Copy Src Storage to the Temp Storage */
8227 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8228 IStorage_Release(pTempStorage
);
8230 /* Open Temp Storage as a file and copy to memory */
8231 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8232 if(hFile
!= INVALID_HANDLE_VALUE
)
8234 nDataLength
= GetFileSize(hFile
, NULL
);
8235 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8236 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8239 DeleteFileW(wstrTempFile
);
8244 /*************************************************************************
8245 * OLECONVERT_CreateOleStream [Internal]
8247 * Creates the "\001OLE" stream in the IStorage if necessary.
8250 * pStorage [I] Dest storage to create the stream in
8256 * This function is used by OleConvertOLESTREAMToIStorage only.
8258 * This stream is still unknown, MS Word seems to have extra data
8259 * but since the data is stored in the OLESTREAM there should be
8260 * no need to recreate the stream. If the stream is manually
8261 * deleted it will create it with this default data.
8264 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8268 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8269 BYTE pOleStreamHeader
[] =
8271 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8273 0x00, 0x00, 0x00, 0x00
8276 /* Create stream if not present */
8277 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8278 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8282 /* Write default Data */
8283 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8284 IStream_Release(pStream
);
8288 /* write a string to a stream, preceded by its length */
8289 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8296 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8297 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8302 str
= CoTaskMemAlloc( len
);
8303 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8304 r
= IStream_Write( stm
, str
, len
, NULL
);
8305 CoTaskMemFree( str
);
8309 /* read a string preceded by its length from a stream */
8310 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8313 DWORD len
, count
= 0;
8317 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8320 if( count
!= sizeof(len
) )
8321 return E_OUTOFMEMORY
;
8323 TRACE("%d bytes\n",len
);
8325 str
= CoTaskMemAlloc( len
);
8327 return E_OUTOFMEMORY
;
8329 r
= IStream_Read( stm
, str
, len
, &count
);
8334 CoTaskMemFree( str
);
8335 return E_OUTOFMEMORY
;
8338 TRACE("Read string %s\n",debugstr_an(str
,len
));
8340 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8341 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8343 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8344 CoTaskMemFree( str
);
8352 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8353 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8357 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8359 static const BYTE unknown1
[12] =
8360 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8361 0xFF, 0xFF, 0xFF, 0xFF};
8362 static const BYTE unknown2
[16] =
8363 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8366 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8367 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8368 debugstr_w(szProgIDName
));
8370 /* Create a CompObj stream */
8371 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8372 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8376 /* Write CompObj Structure to stream */
8377 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8379 if( SUCCEEDED( r
) )
8380 r
= WriteClassStm( pstm
, clsid
);
8382 if( SUCCEEDED( r
) )
8383 r
= STREAM_WriteString( pstm
, lpszUserType
);
8384 if( SUCCEEDED( r
) )
8385 r
= STREAM_WriteString( pstm
, szClipName
);
8386 if( SUCCEEDED( r
) )
8387 r
= STREAM_WriteString( pstm
, szProgIDName
);
8388 if( SUCCEEDED( r
) )
8389 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8391 IStream_Release( pstm
);
8396 /***********************************************************************
8397 * WriteFmtUserTypeStg (OLE32.@)
8399 HRESULT WINAPI
WriteFmtUserTypeStg(
8400 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8403 WCHAR szwClipName
[0x40];
8404 CLSID clsid
= CLSID_NULL
;
8405 LPWSTR wstrProgID
= NULL
;
8408 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8410 /* get the clipboard format name */
8411 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8414 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8416 /* FIXME: There's room to save a CLSID and its ProgID, but
8417 the CLSID is not looked up in the registry and in all the
8418 tests I wrote it was CLSID_NULL. Where does it come from?
8421 /* get the real program ID. This may fail, but that's fine */
8422 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8424 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8426 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8427 lpszUserType
, szwClipName
, wstrProgID
);
8429 CoTaskMemFree(wstrProgID
);
8435 /******************************************************************************
8436 * ReadFmtUserTypeStg [OLE32.@]
8438 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8442 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8443 unsigned char unknown1
[12];
8444 unsigned char unknown2
[16];
8446 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8449 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8451 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8452 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8455 WARN("Failed to open stream r = %08x\n", r
);
8459 /* read the various parts of the structure */
8460 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8461 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8463 r
= ReadClassStm( stm
, &clsid
);
8467 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8471 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8475 r
= STREAM_ReadString( stm
, &szProgIDName
);
8479 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8480 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8483 /* ok, success... now we just need to store what we found */
8485 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8486 CoTaskMemFree( szOleTypeName
);
8488 if( lplpszUserType
)
8489 *lplpszUserType
= szCLSIDName
;
8490 CoTaskMemFree( szProgIDName
);
8493 IStream_Release( stm
);
8499 /*************************************************************************
8500 * OLECONVERT_CreateCompObjStream [Internal]
8502 * Creates a "\001CompObj" is the destination IStorage if necessary.
8505 * pStorage [I] The dest IStorage to create the CompObj Stream
8507 * strOleTypeName [I] The ProgID
8511 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8514 * This function is used by OleConvertOLESTREAMToIStorage only.
8516 * The stream data is stored in the OLESTREAM and there should be
8517 * no need to recreate the stream. If the stream is manually
8518 * deleted it will attempt to create it by querying the registry.
8522 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8525 HRESULT hStorageRes
, hRes
= S_OK
;
8526 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8527 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8528 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8530 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8531 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8533 /* Initialize the CompObj structure */
8534 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8535 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8536 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8539 /* Create a CompObj stream if it doesn't exist */
8540 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8541 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8542 if(hStorageRes
== S_OK
)
8544 /* copy the OleTypeName to the compobj struct */
8545 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8546 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8548 /* copy the OleTypeName to the compobj struct */
8549 /* Note: in the test made, these were Identical */
8550 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8551 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8554 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8555 bufferW
, OLESTREAM_MAX_STR_LEN
);
8556 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8562 /* Get the CLSID Default Name from the Registry */
8563 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8564 if(hErr
== ERROR_SUCCESS
)
8566 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8567 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8568 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8569 if(hErr
== ERROR_SUCCESS
)
8571 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8577 /* Write CompObj Structure to stream */
8578 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8580 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8582 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8583 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8585 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8587 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8588 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8590 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8592 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8593 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8595 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8597 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8598 IStream_Release(pStream
);
8604 /*************************************************************************
8605 * OLECONVERT_CreateOlePresStream[Internal]
8607 * Creates the "\002OlePres000" Stream with the Metafile data
8610 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8611 * dwExtentX [I] Width of the Metafile
8612 * dwExtentY [I] Height of the Metafile
8613 * pData [I] Metafile data
8614 * dwDataLength [I] Size of the Metafile data
8618 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8621 * This function is used by OleConvertOLESTREAMToIStorage only.
8624 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8628 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8629 BYTE pOlePresStreamHeader
[] =
8631 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8632 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8633 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8634 0x00, 0x00, 0x00, 0x00
8637 BYTE pOlePresStreamHeaderEmpty
[] =
8639 0x00, 0x00, 0x00, 0x00,
8640 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8641 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8642 0x00, 0x00, 0x00, 0x00
8645 /* Create the OlePres000 Stream */
8646 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8647 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8652 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8654 memset(&OlePres
, 0, sizeof(OlePres
));
8655 /* Do we have any metafile data to save */
8656 if(dwDataLength
> 0)
8658 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8659 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8663 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8664 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8666 /* Set width and height of the metafile */
8667 OlePres
.dwExtentX
= dwExtentX
;
8668 OlePres
.dwExtentY
= -dwExtentY
;
8670 /* Set Data and Length */
8671 if(dwDataLength
> sizeof(METAFILEPICT16
))
8673 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8674 OlePres
.pData
= &(pData
[8]);
8676 /* Save OlePres000 Data to Stream */
8677 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8678 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8679 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8680 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8681 if(OlePres
.dwSize
> 0)
8683 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8685 IStream_Release(pStream
);
8689 /*************************************************************************
8690 * OLECONVERT_CreateOle10NativeStream [Internal]
8692 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8695 * pStorage [I] Dest storage to create the stream in
8696 * pData [I] Ole10 Native Data (ex. bmp)
8697 * dwDataLength [I] Size of the Ole10 Native Data
8703 * This function is used by OleConvertOLESTREAMToIStorage only.
8705 * Might need to verify the data and return appropriate error message
8708 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8712 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8714 /* Create the Ole10Native Stream */
8715 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8716 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8720 /* Write info to stream */
8721 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8722 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8723 IStream_Release(pStream
);
8728 /*************************************************************************
8729 * OLECONVERT_GetOLE10ProgID [Internal]
8731 * Finds the ProgID (or OleTypeID) from the IStorage
8734 * pStorage [I] The Src IStorage to get the ProgID
8735 * strProgID [I] the ProgID string to get
8736 * dwSize [I] the size of the string
8740 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8743 * This function is used by OleConvertIStorageToOLESTREAM only.
8747 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8751 LARGE_INTEGER iSeekPos
;
8752 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8753 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8755 /* Open the CompObj Stream */
8756 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8757 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8761 /*Get the OleType from the CompObj Stream */
8762 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8763 iSeekPos
.u
.HighPart
= 0;
8765 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8766 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8767 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8768 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8769 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8770 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8771 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8773 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8776 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8778 IStream_Release(pStream
);
8783 LPOLESTR wstrProgID
;
8785 /* Get the OleType from the registry */
8786 REFCLSID clsid
= &(stat
.clsid
);
8787 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8788 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8791 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8798 /*************************************************************************
8799 * OLECONVERT_GetOle10PresData [Internal]
8801 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8804 * pStorage [I] Src IStroage
8805 * pOleStream [I] Dest OleStream Mem Struct
8811 * This function is used by OleConvertIStorageToOLESTREAM only.
8813 * Memory allocated for pData must be freed by the caller
8817 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8822 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8824 /* Initialize Default data for OLESTREAM */
8825 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8826 pOleStreamData
[0].dwTypeID
= 2;
8827 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8828 pOleStreamData
[1].dwTypeID
= 0;
8829 pOleStreamData
[0].dwMetaFileWidth
= 0;
8830 pOleStreamData
[0].dwMetaFileHeight
= 0;
8831 pOleStreamData
[0].pData
= NULL
;
8832 pOleStreamData
[1].pData
= NULL
;
8834 /* Open Ole10Native Stream */
8835 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8836 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8840 /* Read Size and Data */
8841 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8842 if(pOleStreamData
->dwDataLength
> 0)
8844 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8845 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8847 IStream_Release(pStream
);
8853 /*************************************************************************
8854 * OLECONVERT_GetOle20PresData[Internal]
8856 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8859 * pStorage [I] Src IStroage
8860 * pOleStreamData [I] Dest OleStream Mem Struct
8866 * This function is used by OleConvertIStorageToOLESTREAM only.
8868 * Memory allocated for pData must be freed by the caller
8870 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8874 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8875 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8877 /* Initialize Default data for OLESTREAM */
8878 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8879 pOleStreamData
[0].dwTypeID
= 2;
8880 pOleStreamData
[0].dwMetaFileWidth
= 0;
8881 pOleStreamData
[0].dwMetaFileHeight
= 0;
8882 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8883 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8884 pOleStreamData
[1].dwTypeID
= 0;
8885 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8886 pOleStreamData
[1].strOleTypeName
[0] = 0;
8887 pOleStreamData
[1].dwMetaFileWidth
= 0;
8888 pOleStreamData
[1].dwMetaFileHeight
= 0;
8889 pOleStreamData
[1].pData
= NULL
;
8890 pOleStreamData
[1].dwDataLength
= 0;
8893 /* Open OlePress000 stream */
8894 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8895 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8898 LARGE_INTEGER iSeekPos
;
8899 METAFILEPICT16 MetaFilePict
;
8900 static const char strMetafilePictName
[] = "METAFILEPICT";
8902 /* Set the TypeID for a Metafile */
8903 pOleStreamData
[1].dwTypeID
= 5;
8905 /* Set the OleTypeName to Metafile */
8906 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8907 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8909 iSeekPos
.u
.HighPart
= 0;
8910 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8912 /* Get Presentation Data */
8913 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8914 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8915 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8916 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8918 /*Set width and Height */
8919 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8920 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8921 if(olePress
.dwSize
> 0)
8924 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8926 /* Set MetaFilePict struct */
8927 MetaFilePict
.mm
= 8;
8928 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8929 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8930 MetaFilePict
.hMF
= 0;
8932 /* Get Metafile Data */
8933 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8934 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8935 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8937 IStream_Release(pStream
);
8941 /*************************************************************************
8942 * OleConvertOLESTREAMToIStorage [OLE32.@]
8947 * DVTARGETDEVICE parameter is not handled
8948 * Still unsure of some mem fields for OLE 10 Stream
8949 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8950 * and "\001OLE" streams
8953 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8954 LPOLESTREAM pOleStream
,
8956 const DVTARGETDEVICE
* ptd
)
8960 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8962 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8964 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8968 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8971 if(pstg
== NULL
|| pOleStream
== NULL
)
8973 hRes
= E_INVALIDARG
;
8978 /* Load the OLESTREAM to Memory */
8979 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8984 /* Load the OLESTREAM to Memory (part 2)*/
8985 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8991 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8993 /* Do we have the IStorage Data in the OLESTREAM */
8994 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8996 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8997 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9001 /* It must be an original OLE 1.0 source */
9002 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9007 /* It must be an original OLE 1.0 source */
9008 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9011 /* Create CompObj Stream if necessary */
9012 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9015 /*Create the Ole Stream if necessary */
9016 OLECONVERT_CreateOleStream(pstg
);
9021 /* Free allocated memory */
9022 for(i
=0; i
< 2; i
++)
9024 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9025 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9026 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9031 /*************************************************************************
9032 * OleConvertIStorageToOLESTREAM [OLE32.@]
9039 * Still unsure of some mem fields for OLE 10 Stream
9040 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9041 * and "\001OLE" streams.
9044 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9046 LPOLESTREAM pOleStream
)
9049 HRESULT hRes
= S_OK
;
9051 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9052 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9054 TRACE("%p %p\n", pstg
, pOleStream
);
9056 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9058 if(pstg
== NULL
|| pOleStream
== NULL
)
9060 hRes
= E_INVALIDARG
;
9064 /* Get the ProgID */
9065 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9066 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9070 /* Was it originally Ole10 */
9071 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9074 IStream_Release(pStream
);
9075 /* Get Presentation Data for Ole10Native */
9076 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9080 /* Get Presentation Data (OLE20) */
9081 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9084 /* Save OLESTREAM */
9085 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9088 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9093 /* Free allocated memory */
9094 for(i
=0; i
< 2; i
++)
9096 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9102 /***********************************************************************
9103 * GetConvertStg (OLE32.@)
9105 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
9106 FIXME("unimplemented stub!\n");
9110 /******************************************************************************
9111 * StgIsStorageFile [OLE32.@]
9112 * Verify if the file contains a storage object
9118 * S_OK if file has magic bytes as a storage object
9119 * S_FALSE if file is not storage
9122 StgIsStorageFile(LPCOLESTR fn
)
9128 TRACE("%s\n", debugstr_w(fn
));
9129 hf
= CreateFileW(fn
, GENERIC_READ
,
9130 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9131 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9133 if (hf
== INVALID_HANDLE_VALUE
)
9134 return STG_E_FILENOTFOUND
;
9136 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9138 WARN(" unable to read file\n");
9145 if (bytes_read
!= 8) {
9146 TRACE(" too short\n");
9150 if (!memcmp(magic
,STORAGE_magic
,8)) {
9155 TRACE(" -> Invalid header.\n");
9159 /***********************************************************************
9160 * WriteClassStm (OLE32.@)
9162 * Writes a CLSID to a stream.
9165 * pStm [I] Stream to write to.
9166 * rclsid [I] CLSID to write.
9170 * Failure: HRESULT code.
9172 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9174 TRACE("(%p,%p)\n",pStm
,rclsid
);
9176 if (!pStm
|| !rclsid
)
9177 return E_INVALIDARG
;
9179 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9182 /***********************************************************************
9183 * ReadClassStm (OLE32.@)
9185 * Reads a CLSID from a stream.
9188 * pStm [I] Stream to read from.
9189 * rclsid [O] CLSID to read.
9193 * Failure: HRESULT code.
9195 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9200 TRACE("(%p,%p)\n",pStm
,pclsid
);
9202 if (!pStm
|| !pclsid
)
9203 return E_INVALIDARG
;
9205 /* clear the output args */
9206 *pclsid
= CLSID_NULL
;
9208 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9213 if (nbByte
!= sizeof(CLSID
))
9214 return STG_E_READFAULT
;