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 HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
96 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
97 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
98 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
99 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
100 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
102 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
103 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
104 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
105 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
106 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
108 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
109 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
110 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
112 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
113 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
114 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
115 ULONG blockIndex
, ULONG offset
, DWORD value
);
116 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
117 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
119 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
120 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
122 typedef struct TransactedDirEntry
124 /* If applicable, a reference to the original DirEntry in the transacted
125 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
126 DirRef transactedParentEntry
;
128 /* True if this entry is being used. */
131 /* True if data is up to date. */
134 /* True if this entry has been modified. */
137 /* True if this entry's stream has been modified. */
140 /* True if this entry has been deleted in the transacted storage, but the
141 * delete has not yet been committed. */
144 /* If this entry's stream has been modified, a reference to where the stream
145 * is stored in the snapshot file. */
148 /* This directory entry's data, including any changes that have been made. */
151 /* A reference to the parent of this node. This is only valid while we are
152 * committing changes. */
155 /* A reference to a newly-created entry in the transacted parent. This is
156 * always equal to transactedParentEntry except when committing changes. */
157 DirRef newTransactedParentEntry
;
158 } TransactedDirEntry
;
160 /****************************************************************************
161 * Transacted storage object.
163 typedef struct TransactedSnapshotImpl
165 struct StorageBaseImpl base
;
168 * Modified streams are temporarily saved to the scratch file.
170 StorageBaseImpl
*scratch
;
172 /* The directory structure is kept here, so that we can track how these
173 * entries relate to those in the parent storage. */
174 TransactedDirEntry
*entries
;
176 ULONG firstFreeEntry
;
179 * Changes are committed to the transacted parent.
181 StorageBaseImpl
*transactedParent
;
182 } TransactedSnapshotImpl
;
184 /* Generic function to create a transacted wrapper for a direct storage object. */
185 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
187 /* OLESTREAM memory structure to use for Get and Put Routines */
188 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
193 DWORD dwOleTypeNameLength
;
194 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
195 CHAR
*pstrOleObjFileName
;
196 DWORD dwOleObjFileNameLength
;
197 DWORD dwMetaFileWidth
;
198 DWORD dwMetaFileHeight
;
199 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
202 }OLECONVERT_OLESTREAM_DATA
;
204 /* CompObj Stream structure */
205 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
210 DWORD dwCLSIDNameLength
;
211 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
212 DWORD dwOleTypeNameLength
;
213 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
214 DWORD dwProgIDNameLength
;
215 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
217 }OLECONVERT_ISTORAGE_COMPOBJ
;
220 /* Ole Presentation Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
229 }OLECONVERT_ISTORAGE_OLEPRES
;
233 /***********************************************************************
234 * Forward declaration of internal functions used by the method DestroyElement
236 static HRESULT
deleteStorageContents(
237 StorageBaseImpl
*parentStorage
,
238 DirRef indexToDelete
,
239 DirEntry entryDataToDelete
);
241 static HRESULT
deleteStreamContents(
242 StorageBaseImpl
*parentStorage
,
243 DirRef indexToDelete
,
244 DirEntry entryDataToDelete
);
246 static HRESULT
removeFromTree(
247 StorageBaseImpl
*This
,
248 DirRef parentStorageIndex
,
249 DirRef deletedIndex
);
251 /***********************************************************************
252 * Declaration of the functions used to manipulate DirEntry
255 static HRESULT
insertIntoTree(
256 StorageBaseImpl
*This
,
257 DirRef parentStorageIndex
,
258 DirRef newEntryIndex
);
260 static LONG
entryNameCmp(
261 const OLECHAR
*name1
,
262 const OLECHAR
*name2
);
264 static DirRef
findElement(
265 StorageBaseImpl
*storage
,
270 static HRESULT
findTreeParent(
271 StorageBaseImpl
*storage
,
273 const OLECHAR
*childName
,
274 DirEntry
*parentData
,
278 /***********************************************************************
279 * Declaration of miscellaneous functions...
281 static HRESULT
validateSTGM(DWORD stgmValue
);
283 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
284 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
285 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
287 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
290 /****************************************************************************
291 * IEnumSTATSTGImpl definitions.
293 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
294 * This class allows iterating through the content of a storage and to find
295 * specific items inside it.
297 struct IEnumSTATSTGImpl
299 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
300 * since we want to cast this in an IEnumSTATSTG pointer */
302 LONG ref
; /* Reference count */
303 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
304 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
306 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
310 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
311 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
313 /************************************************************************
317 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
319 return (index
+1) * This
->bigBlockSize
;
322 /************************************************************************
323 ** Storage32BaseImpl implementation
325 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
326 ULARGE_INTEGER offset
,
331 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
334 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
335 ULARGE_INTEGER offset
,
340 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
343 /************************************************************************
344 * Storage32BaseImpl_QueryInterface (IUnknown)
346 * This method implements the common QueryInterface for all IStorage32
347 * implementations contained in this file.
349 * See Windows documentation for more details on IUnknown methods.
351 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
356 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
358 if ( (This
==0) || (ppvObject
==0) )
363 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
364 IsEqualGUID(&IID_IStorage
, riid
))
368 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
370 *ppvObject
= &This
->pssVtbl
;
374 return E_NOINTERFACE
;
376 IStorage_AddRef(iface
);
381 /************************************************************************
382 * Storage32BaseImpl_AddRef (IUnknown)
384 * This method implements the common AddRef for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static ULONG WINAPI
StorageBaseImpl_AddRef(
392 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
393 ULONG ref
= InterlockedIncrement(&This
->ref
);
395 TRACE("(%p) AddRef to %d\n", This
, ref
);
400 /************************************************************************
401 * Storage32BaseImpl_Release (IUnknown)
403 * This method implements the common Release for all IStorage32
404 * implementations contained in this file.
406 * See Windows documentation for more details on IUnknown methods.
408 static ULONG WINAPI
StorageBaseImpl_Release(
411 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
413 ULONG ref
= InterlockedDecrement(&This
->ref
);
415 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
420 * Since we are using a system of base-classes, we want to call the
421 * destructor of the appropriate derived class. To do this, we are
422 * using virtual functions to implement the destructor.
424 StorageBaseImpl_Destroy(This
);
430 /************************************************************************
431 * Storage32BaseImpl_OpenStream (IStorage)
433 * This method will open the specified stream object from the current storage.
435 * See Windows documentation for more details on IStorage methods.
437 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
439 const OLECHAR
* pwcsName
, /* [string][in] */
440 void* reserved1
, /* [unique][in] */
441 DWORD grfMode
, /* [in] */
442 DWORD reserved2
, /* [in] */
443 IStream
** ppstm
) /* [out] */
445 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
446 StgStreamImpl
* newStream
;
447 DirEntry currentEntry
;
448 DirRef streamEntryRef
;
449 HRESULT res
= STG_E_UNKNOWN
;
451 TRACE("(%p, %s, %p, %x, %d, %p)\n",
452 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
454 if ( (pwcsName
==NULL
) || (ppstm
==0) )
462 if ( FAILED( validateSTGM(grfMode
) ) ||
463 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
465 res
= STG_E_INVALIDFLAG
;
472 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
474 res
= STG_E_INVALIDFUNCTION
;
480 res
= STG_E_REVERTED
;
485 * Check that we're compatible with the parent's storage mode, but
486 * only if we are not in transacted mode
488 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
489 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
491 res
= STG_E_INVALIDFLAG
;
497 * Search for the element with the given name
499 streamEntryRef
= findElement(
501 This
->storageDirEntry
,
506 * If it was found, construct the stream object and return a pointer to it.
508 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
509 (currentEntry
.stgType
==STGTY_STREAM
) )
511 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
513 /* A single stream cannot be opened a second time. */
514 res
= STG_E_ACCESSDENIED
;
518 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
522 newStream
->grfMode
= grfMode
;
523 *ppstm
= (IStream
*)newStream
;
525 IStream_AddRef(*ppstm
);
535 res
= STG_E_FILENOTFOUND
;
539 TRACE("<-- IStream %p\n", *ppstm
);
540 TRACE("<-- %08x\n", res
);
544 /************************************************************************
545 * Storage32BaseImpl_OpenStorage (IStorage)
547 * This method will open a new storage object from the current storage.
549 * See Windows documentation for more details on IStorage methods.
551 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
553 const OLECHAR
* pwcsName
, /* [string][unique][in] */
554 IStorage
* pstgPriority
, /* [unique][in] */
555 DWORD grfMode
, /* [in] */
556 SNB snbExclude
, /* [unique][in] */
557 DWORD reserved
, /* [in] */
558 IStorage
** ppstg
) /* [out] */
560 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
561 StorageInternalImpl
* newStorage
;
562 StorageBaseImpl
* newTransactedStorage
;
563 DirEntry currentEntry
;
564 DirRef storageEntryRef
;
565 HRESULT res
= STG_E_UNKNOWN
;
567 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
568 iface
, debugstr_w(pwcsName
), pstgPriority
,
569 grfMode
, snbExclude
, reserved
, ppstg
);
571 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
577 if (This
->openFlags
& STGM_SIMPLE
)
579 res
= STG_E_INVALIDFUNCTION
;
584 if (snbExclude
!= NULL
)
586 res
= STG_E_INVALIDPARAMETER
;
590 if ( FAILED( validateSTGM(grfMode
) ))
592 res
= STG_E_INVALIDFLAG
;
599 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
600 (grfMode
& STGM_DELETEONRELEASE
) ||
601 (grfMode
& STGM_PRIORITY
) )
603 res
= STG_E_INVALIDFUNCTION
;
608 return STG_E_REVERTED
;
611 * Check that we're compatible with the parent's storage mode,
612 * but only if we are not transacted
614 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
615 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
617 res
= STG_E_ACCESSDENIED
;
624 storageEntryRef
= findElement(
626 This
->storageDirEntry
,
630 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
631 (currentEntry
.stgType
==STGTY_STORAGE
) )
633 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
635 /* A single storage cannot be opened a second time. */
636 res
= STG_E_ACCESSDENIED
;
640 newStorage
= StorageInternalImpl_Construct(
647 if (grfMode
& STGM_TRANSACTED
)
649 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
653 HeapFree(GetProcessHeap(), 0, newStorage
);
657 *ppstg
= (IStorage
*)newTransactedStorage
;
661 *ppstg
= (IStorage
*)newStorage
;
664 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
670 res
= STG_E_INSUFFICIENTMEMORY
;
674 res
= STG_E_FILENOTFOUND
;
677 TRACE("<-- %08x\n", res
);
681 /************************************************************************
682 * Storage32BaseImpl_EnumElements (IStorage)
684 * This method will create an enumerator object that can be used to
685 * retrieve information about all the elements in the storage object.
687 * See Windows documentation for more details on IStorage methods.
689 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
691 DWORD reserved1
, /* [in] */
692 void* reserved2
, /* [size_is][unique][in] */
693 DWORD reserved3
, /* [in] */
694 IEnumSTATSTG
** ppenum
) /* [out] */
696 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
697 IEnumSTATSTGImpl
* newEnum
;
699 TRACE("(%p, %d, %p, %d, %p)\n",
700 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
702 if ( (This
==0) || (ppenum
==0))
706 return STG_E_REVERTED
;
708 newEnum
= IEnumSTATSTGImpl_Construct(
710 This
->storageDirEntry
);
714 *ppenum
= (IEnumSTATSTG
*)newEnum
;
716 IEnumSTATSTG_AddRef(*ppenum
);
721 return E_OUTOFMEMORY
;
724 /************************************************************************
725 * Storage32BaseImpl_Stat (IStorage)
727 * This method will retrieve information about this storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI
StorageBaseImpl_Stat(
733 STATSTG
* pstatstg
, /* [out] */
734 DWORD grfStatFlag
) /* [in] */
736 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
737 DirEntry currentEntry
;
738 HRESULT res
= STG_E_UNKNOWN
;
740 TRACE("(%p, %p, %x)\n",
741 iface
, pstatstg
, grfStatFlag
);
743 if ( (This
==0) || (pstatstg
==0))
751 res
= STG_E_REVERTED
;
755 res
= StorageBaseImpl_ReadDirEntry(
757 This
->storageDirEntry
,
762 StorageUtl_CopyDirEntryToSTATSTG(
768 pstatstg
->grfMode
= This
->openFlags
;
769 pstatstg
->grfStateBits
= This
->stateBits
;
775 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
);
777 TRACE("<-- %08x\n", res
);
781 /************************************************************************
782 * Storage32BaseImpl_RenameElement (IStorage)
784 * This method will rename the specified element.
786 * See Windows documentation for more details on IStorage methods.
788 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
790 const OLECHAR
* pwcsOldName
, /* [in] */
791 const OLECHAR
* pwcsNewName
) /* [in] */
793 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
794 DirEntry currentEntry
;
795 DirRef currentEntryRef
;
797 TRACE("(%p, %s, %s)\n",
798 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
801 return STG_E_REVERTED
;
803 currentEntryRef
= findElement(This
,
804 This
->storageDirEntry
,
808 if (currentEntryRef
!= DIRENTRY_NULL
)
811 * There is already an element with the new name
813 return STG_E_FILEALREADYEXISTS
;
817 * Search for the old element name
819 currentEntryRef
= findElement(This
,
820 This
->storageDirEntry
,
824 if (currentEntryRef
!= DIRENTRY_NULL
)
826 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
827 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
829 WARN("Element is already open; cannot rename.\n");
830 return STG_E_ACCESSDENIED
;
833 /* Remove the element from its current position in the tree */
834 removeFromTree(This
, This
->storageDirEntry
,
837 /* Change the name of the element */
838 strcpyW(currentEntry
.name
, pwcsNewName
);
840 /* Delete any sibling links */
841 currentEntry
.leftChild
= DIRENTRY_NULL
;
842 currentEntry
.rightChild
= DIRENTRY_NULL
;
844 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
847 /* Insert the element in a new position in the tree */
848 insertIntoTree(This
, This
->storageDirEntry
,
854 * There is no element with the old name
856 return STG_E_FILENOTFOUND
;
859 return StorageBaseImpl_Flush(This
);
862 /************************************************************************
863 * Storage32BaseImpl_CreateStream (IStorage)
865 * This method will create a stream object within this storage
867 * See Windows documentation for more details on IStorage methods.
869 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
871 const OLECHAR
* pwcsName
, /* [string][in] */
872 DWORD grfMode
, /* [in] */
873 DWORD reserved1
, /* [in] */
874 DWORD reserved2
, /* [in] */
875 IStream
** ppstm
) /* [out] */
877 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
878 StgStreamImpl
* newStream
;
879 DirEntry currentEntry
, newStreamEntry
;
880 DirRef currentEntryRef
, newStreamEntryRef
;
883 TRACE("(%p, %s, %x, %d, %d, %p)\n",
884 iface
, debugstr_w(pwcsName
), grfMode
,
885 reserved1
, reserved2
, ppstm
);
888 return STG_E_INVALIDPOINTER
;
891 return STG_E_INVALIDNAME
;
893 if (reserved1
|| reserved2
)
894 return STG_E_INVALIDPARAMETER
;
896 if ( FAILED( validateSTGM(grfMode
) ))
897 return STG_E_INVALIDFLAG
;
899 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
900 return STG_E_INVALIDFLAG
;
903 return STG_E_REVERTED
;
908 if ((grfMode
& STGM_DELETEONRELEASE
) ||
909 (grfMode
& STGM_TRANSACTED
))
910 return STG_E_INVALIDFUNCTION
;
913 * Don't worry about permissions in transacted mode, as we can always write
914 * changes; we just can't always commit them.
916 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
917 /* Can't create a stream on read-only storage */
918 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
919 return STG_E_ACCESSDENIED
;
921 /* Can't create a stream with greater access than the parent. */
922 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
923 return STG_E_ACCESSDENIED
;
926 if(This
->openFlags
& STGM_SIMPLE
)
927 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
931 currentEntryRef
= findElement(This
,
932 This
->storageDirEntry
,
936 if (currentEntryRef
!= DIRENTRY_NULL
)
939 * An element with this name already exists
941 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
943 IStorage_DestroyElement(iface
, pwcsName
);
946 return STG_E_FILEALREADYEXISTS
;
950 * memset the empty entry
952 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
954 newStreamEntry
.sizeOfNameString
=
955 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
957 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
958 return STG_E_INVALIDNAME
;
960 strcpyW(newStreamEntry
.name
, pwcsName
);
962 newStreamEntry
.stgType
= STGTY_STREAM
;
963 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
964 newStreamEntry
.size
.u
.LowPart
= 0;
965 newStreamEntry
.size
.u
.HighPart
= 0;
967 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
968 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
969 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
971 /* call CoFileTime to get the current time
976 /* newStreamEntry.clsid */
979 * Create an entry with the new data
981 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
986 * Insert the new entry in the parent storage's tree.
990 This
->storageDirEntry
,
994 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
999 * Open the stream to return it.
1001 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1005 *ppstm
= (IStream
*)newStream
;
1007 IStream_AddRef(*ppstm
);
1011 return STG_E_INSUFFICIENTMEMORY
;
1014 return StorageBaseImpl_Flush(This
);
1017 /************************************************************************
1018 * Storage32BaseImpl_SetClass (IStorage)
1020 * This method will write the specified CLSID in the directory entry of this
1023 * See Windows documentation for more details on IStorage methods.
1025 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1027 REFCLSID clsid
) /* [in] */
1029 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1031 DirEntry currentEntry
;
1033 TRACE("(%p, %p)\n", iface
, clsid
);
1036 return STG_E_REVERTED
;
1038 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1039 This
->storageDirEntry
,
1041 if (SUCCEEDED(hRes
))
1043 currentEntry
.clsid
= *clsid
;
1045 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1046 This
->storageDirEntry
,
1050 if (SUCCEEDED(hRes
))
1051 hRes
= StorageBaseImpl_Flush(This
);
1056 /************************************************************************
1057 ** Storage32Impl implementation
1060 /************************************************************************
1061 * Storage32BaseImpl_CreateStorage (IStorage)
1063 * This method will create the storage object within the provided storage.
1065 * See Windows documentation for more details on IStorage methods.
1067 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1069 const OLECHAR
*pwcsName
, /* [string][in] */
1070 DWORD grfMode
, /* [in] */
1071 DWORD reserved1
, /* [in] */
1072 DWORD reserved2
, /* [in] */
1073 IStorage
**ppstg
) /* [out] */
1075 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1077 DirEntry currentEntry
;
1079 DirRef currentEntryRef
;
1083 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1084 iface
, debugstr_w(pwcsName
), grfMode
,
1085 reserved1
, reserved2
, ppstg
);
1088 return STG_E_INVALIDPOINTER
;
1090 if (This
->openFlags
& STGM_SIMPLE
)
1092 return STG_E_INVALIDFUNCTION
;
1096 return STG_E_INVALIDNAME
;
1100 if ( FAILED( validateSTGM(grfMode
) ) ||
1101 (grfMode
& STGM_DELETEONRELEASE
) )
1103 WARN("bad grfMode: 0x%x\n", grfMode
);
1104 return STG_E_INVALIDFLAG
;
1108 return STG_E_REVERTED
;
1111 * Check that we're compatible with the parent's storage mode
1113 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1114 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1116 WARN("access denied\n");
1117 return STG_E_ACCESSDENIED
;
1120 currentEntryRef
= findElement(This
,
1121 This
->storageDirEntry
,
1125 if (currentEntryRef
!= DIRENTRY_NULL
)
1128 * An element with this name already exists
1130 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1131 ((This
->openFlags
& STGM_TRANSACTED
) ||
1132 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1134 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1140 WARN("file already exists\n");
1141 return STG_E_FILEALREADYEXISTS
;
1144 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1145 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1147 WARN("read-only storage\n");
1148 return STG_E_ACCESSDENIED
;
1151 memset(&newEntry
, 0, sizeof(DirEntry
));
1153 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1155 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1157 FIXME("name too long\n");
1158 return STG_E_INVALIDNAME
;
1161 strcpyW(newEntry
.name
, pwcsName
);
1163 newEntry
.stgType
= STGTY_STORAGE
;
1164 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1165 newEntry
.size
.u
.LowPart
= 0;
1166 newEntry
.size
.u
.HighPart
= 0;
1168 newEntry
.leftChild
= DIRENTRY_NULL
;
1169 newEntry
.rightChild
= DIRENTRY_NULL
;
1170 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1172 /* call CoFileTime to get the current time
1177 /* newEntry.clsid */
1180 * Create a new directory entry for the storage
1182 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1187 * Insert the new directory entry into the parent storage's tree
1189 hr
= insertIntoTree(
1191 This
->storageDirEntry
,
1195 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1200 * Open it to get a pointer to return.
1202 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1204 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1210 hr
= StorageBaseImpl_Flush(This
);
1216 /***************************************************************************
1220 * Reserve a directory entry in the file and initialize it.
1222 static HRESULT
StorageImpl_CreateDirEntry(
1223 StorageBaseImpl
*base
,
1224 const DirEntry
*newData
,
1227 StorageImpl
*storage
= (StorageImpl
*)base
;
1228 ULONG currentEntryIndex
= 0;
1229 ULONG newEntryIndex
= DIRENTRY_NULL
;
1231 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1232 WORD sizeOfNameString
;
1236 hr
= StorageImpl_ReadRawDirEntry(storage
,
1242 StorageUtl_ReadWord(
1244 OFFSET_PS_NAMELENGTH
,
1247 if (sizeOfNameString
== 0)
1250 * The entry exists and is available, we found it.
1252 newEntryIndex
= currentEntryIndex
;
1258 * We exhausted the directory entries, we will create more space below
1260 newEntryIndex
= currentEntryIndex
;
1262 currentEntryIndex
++;
1264 } while (newEntryIndex
== DIRENTRY_NULL
);
1267 * grow the directory stream
1271 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1272 ULARGE_INTEGER newSize
;
1274 ULONG lastEntry
= 0;
1275 ULONG blockCount
= 0;
1278 * obtain the new count of blocks in the directory stream
1280 blockCount
= BlockChainStream_GetCount(
1281 storage
->rootBlockChain
)+1;
1284 * initialize the size used by the directory stream
1286 newSize
.u
.HighPart
= 0;
1287 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1290 * add a block to the directory stream
1292 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1295 * memset the empty entry in order to initialize the unused newly
1298 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1303 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1306 entryIndex
= newEntryIndex
+ 1;
1307 entryIndex
< lastEntry
;
1310 StorageImpl_WriteRawDirEntry(
1316 StorageImpl_SaveFileHeader(storage
);
1319 UpdateRawDirEntry(currentData
, newData
);
1321 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1324 *index
= newEntryIndex
;
1329 /***************************************************************************
1333 * Mark a directory entry in the file as free.
1335 static HRESULT
StorageImpl_DestroyDirEntry(
1336 StorageBaseImpl
*base
,
1340 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1341 StorageImpl
*storage
= (StorageImpl
*)base
;
1343 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1345 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1351 /****************************************************************************
1355 * Case insensitive comparison of DirEntry.name by first considering
1358 * Returns <0 when name1 < name2
1359 * >0 when name1 > name2
1360 * 0 when name1 == name2
1362 static LONG
entryNameCmp(
1363 const OLECHAR
*name1
,
1364 const OLECHAR
*name2
)
1366 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1368 while (diff
== 0 && *name1
!= 0)
1371 * We compare the string themselves only when they are of the same length
1373 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1379 /****************************************************************************
1383 * Add a directory entry to a storage
1385 static HRESULT
insertIntoTree(
1386 StorageBaseImpl
*This
,
1387 DirRef parentStorageIndex
,
1388 DirRef newEntryIndex
)
1390 DirEntry currentEntry
;
1394 * Read the inserted entry
1396 StorageBaseImpl_ReadDirEntry(This
,
1401 * Read the storage entry
1403 StorageBaseImpl_ReadDirEntry(This
,
1407 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1410 * The root storage contains some element, therefore, start the research
1411 * for the appropriate location.
1414 DirRef current
, next
, previous
, currentEntryId
;
1417 * Keep a reference to the root of the storage's element tree
1419 currentEntryId
= currentEntry
.dirRootEntry
;
1424 StorageBaseImpl_ReadDirEntry(This
,
1425 currentEntry
.dirRootEntry
,
1428 previous
= currentEntry
.leftChild
;
1429 next
= currentEntry
.rightChild
;
1430 current
= currentEntryId
;
1434 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1438 if (previous
!= DIRENTRY_NULL
)
1440 StorageBaseImpl_ReadDirEntry(This
,
1447 currentEntry
.leftChild
= newEntryIndex
;
1448 StorageBaseImpl_WriteDirEntry(This
,
1456 if (next
!= DIRENTRY_NULL
)
1458 StorageBaseImpl_ReadDirEntry(This
,
1465 currentEntry
.rightChild
= newEntryIndex
;
1466 StorageBaseImpl_WriteDirEntry(This
,
1475 * Trying to insert an item with the same name in the
1476 * subtree structure.
1478 return STG_E_FILEALREADYEXISTS
;
1481 previous
= currentEntry
.leftChild
;
1482 next
= currentEntry
.rightChild
;
1488 * The storage is empty, make the new entry the root of its element tree
1490 currentEntry
.dirRootEntry
= newEntryIndex
;
1491 StorageBaseImpl_WriteDirEntry(This
,
1499 /****************************************************************************
1503 * Find and read the element of a storage with the given name.
1505 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1506 const OLECHAR
*name
, DirEntry
*data
)
1508 DirRef currentEntry
;
1510 /* Read the storage entry to find the root of the tree. */
1511 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1513 currentEntry
= data
->dirRootEntry
;
1515 while (currentEntry
!= DIRENTRY_NULL
)
1519 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1521 cmp
= entryNameCmp(name
, data
->name
);
1528 currentEntry
= data
->leftChild
;
1531 currentEntry
= data
->rightChild
;
1534 return currentEntry
;
1537 /****************************************************************************
1541 * Find and read the binary tree parent of the element with the given name.
1543 * If there is no such element, find a place where it could be inserted and
1544 * return STG_E_FILENOTFOUND.
1546 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1547 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1553 /* Read the storage entry to find the root of the tree. */
1554 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1556 *parentEntry
= storageEntry
;
1557 *relation
= DIRENTRY_RELATION_DIR
;
1559 childEntry
= parentData
->dirRootEntry
;
1561 while (childEntry
!= DIRENTRY_NULL
)
1565 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1567 cmp
= entryNameCmp(childName
, childData
.name
);
1575 *parentData
= childData
;
1576 *parentEntry
= childEntry
;
1577 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1579 childEntry
= parentData
->leftChild
;
1584 *parentData
= childData
;
1585 *parentEntry
= childEntry
;
1586 *relation
= DIRENTRY_RELATION_NEXT
;
1588 childEntry
= parentData
->rightChild
;
1592 if (childEntry
== DIRENTRY_NULL
)
1593 return STG_E_FILENOTFOUND
;
1599 /*************************************************************************
1602 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1604 DWORD ciidExclude
, /* [in] */
1605 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1606 SNB snbExclude
, /* [unique][in] */
1607 IStorage
* pstgDest
) /* [unique][in] */
1609 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1611 IEnumSTATSTG
*elements
= 0;
1612 STATSTG curElement
, strStat
;
1614 IStorage
*pstgTmp
, *pstgChild
;
1615 IStream
*pstrTmp
, *pstrChild
;
1618 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1621 TRACE("(%p, %d, %p, %p, %p)\n",
1622 iface
, ciidExclude
, rgiidExclude
,
1623 snbExclude
, pstgDest
);
1625 if ( pstgDest
== 0 )
1626 return STG_E_INVALIDPOINTER
;
1629 * Enumerate the elements
1631 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1639 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1640 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1642 for(i
= 0; i
< ciidExclude
; ++i
)
1644 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1645 skip_storage
= TRUE
;
1646 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1649 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1655 * Obtain the next element
1657 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1659 if ( hr
== S_FALSE
)
1661 hr
= S_OK
; /* done, every element has been copied */
1667 WCHAR
**snb
= snbExclude
;
1669 while ( *snb
!= NULL
&& !skip
)
1671 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1680 if (curElement
.type
== STGTY_STORAGE
)
1686 * open child source storage
1688 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1689 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1690 NULL
, 0, &pstgChild
);
1696 * create a new storage in destination storage
1698 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1699 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1703 * if it already exist, don't create a new one use this one
1705 if (hr
== STG_E_FILEALREADYEXISTS
)
1707 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1708 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1709 NULL
, 0, &pstgTmp
);
1715 * do the copy recursively
1717 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1720 IStorage_Release( pstgTmp
);
1723 IStorage_Release( pstgChild
);
1725 else if (curElement
.type
== STGTY_STREAM
)
1731 * create a new stream in destination storage. If the stream already
1732 * exist, it will be deleted and a new one will be created.
1734 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1735 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1742 * open child stream storage. This operation must succeed even if the
1743 * stream is already open, so we use internal functions to do it.
1745 srcEntryRef
= findElement( This
, This
->storageDirEntry
, curElement
.pwcsName
,
1749 ERR("source stream not found\n");
1750 hr
= STG_E_DOCFILECORRUPT
;
1755 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntryRef
);
1757 IStream_AddRef(pstrChild
);
1765 * Get the size of the source stream
1767 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1770 * Set the size of the destination stream.
1772 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1777 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1780 IStream_Release( pstrChild
);
1783 IStream_Release( pstrTmp
);
1787 WARN("unknown element type: %d\n", curElement
.type
);
1791 CoTaskMemFree(curElement
.pwcsName
);
1792 } while (hr
== S_OK
);
1797 IEnumSTATSTG_Release(elements
);
1802 /*************************************************************************
1803 * MoveElementTo (IStorage)
1805 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1807 const OLECHAR
*pwcsName
, /* [string][in] */
1808 IStorage
*pstgDest
, /* [unique][in] */
1809 const OLECHAR
*pwcsNewName
,/* [string][in] */
1810 DWORD grfFlags
) /* [in] */
1812 FIXME("(%p %s %p %s %u): stub\n", iface
,
1813 debugstr_w(pwcsName
), pstgDest
,
1814 debugstr_w(pwcsNewName
), grfFlags
);
1818 /*************************************************************************
1821 * Ensures that any changes made to a storage object open in transacted mode
1822 * are reflected in the parent storage
1824 * In a non-transacted mode, this ensures all cached writes are completed.
1826 static HRESULT WINAPI
StorageImpl_Commit(
1828 DWORD grfCommitFlags
)/* [in] */
1830 StorageBaseImpl
* const base
=(StorageBaseImpl
*)iface
;
1831 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1832 return StorageBaseImpl_Flush(base
);
1835 /*************************************************************************
1838 * Discard all changes that have been made since the last commit operation
1840 static HRESULT WINAPI
StorageImpl_Revert(
1843 TRACE("(%p)\n", iface
);
1847 /*************************************************************************
1848 * DestroyElement (IStorage)
1850 * Strategy: This implementation is built this way for simplicity not for speed.
1851 * I always delete the topmost element of the enumeration and adjust
1852 * the deleted element pointer all the time. This takes longer to
1853 * do but allow to reinvoke DestroyElement whenever we encounter a
1854 * storage object. The optimisation resides in the usage of another
1855 * enumeration strategy that would give all the leaves of a storage
1856 * first. (postfix order)
1858 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1860 const OLECHAR
*pwcsName
)/* [string][in] */
1862 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1865 DirEntry entryToDelete
;
1866 DirRef entryToDeleteRef
;
1869 iface
, debugstr_w(pwcsName
));
1872 return STG_E_INVALIDPOINTER
;
1875 return STG_E_REVERTED
;
1877 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1878 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1879 return STG_E_ACCESSDENIED
;
1881 entryToDeleteRef
= findElement(
1883 This
->storageDirEntry
,
1887 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1889 return STG_E_FILENOTFOUND
;
1892 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1894 hr
= deleteStorageContents(
1899 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1901 hr
= deleteStreamContents(
1911 * Remove the entry from its parent storage
1913 hr
= removeFromTree(
1915 This
->storageDirEntry
,
1919 * Invalidate the entry
1922 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1925 hr
= StorageBaseImpl_Flush(This
);
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1938 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1944 list_remove(&(strm
->StrmListEntry
));
1947 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1949 StgStreamImpl
*strm
;
1951 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1953 if (strm
->dirEntry
== streamEntry
)
1962 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1964 StorageInternalImpl
*childstg
;
1966 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1968 if (childstg
->base
.storageDirEntry
== storageEntry
)
1977 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1979 struct list
*cur
, *cur2
;
1980 StgStreamImpl
*strm
=NULL
;
1981 StorageInternalImpl
*childstg
=NULL
;
1983 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1984 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1985 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1986 strm
->parentStorage
= NULL
;
1990 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1991 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1992 StorageBaseImpl_Invalidate( &childstg
->base
);
1995 if (stg
->transactedChild
)
1997 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1999 stg
->transactedChild
= NULL
;
2004 /*********************************************************************
2008 * Delete the contents of a storage entry.
2011 static HRESULT
deleteStorageContents(
2012 StorageBaseImpl
*parentStorage
,
2013 DirRef indexToDelete
,
2014 DirEntry entryDataToDelete
)
2016 IEnumSTATSTG
*elements
= 0;
2017 IStorage
*childStorage
= 0;
2018 STATSTG currentElement
;
2020 HRESULT destroyHr
= S_OK
;
2021 StorageInternalImpl
*stg
, *stg2
;
2023 /* Invalidate any open storage objects. */
2024 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2026 if (stg
->base
.storageDirEntry
== indexToDelete
)
2028 StorageBaseImpl_Invalidate(&stg
->base
);
2033 * Open the storage and enumerate it
2035 hr
= StorageBaseImpl_OpenStorage(
2036 (IStorage
*)parentStorage
,
2037 entryDataToDelete
.name
,
2039 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2050 * Enumerate the elements
2052 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2057 * Obtain the next element
2059 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2062 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2064 CoTaskMemFree(currentElement
.pwcsName
);
2068 * We need to Reset the enumeration every time because we delete elements
2069 * and the enumeration could be invalid
2071 IEnumSTATSTG_Reset(elements
);
2073 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2075 IStorage_Release(childStorage
);
2076 IEnumSTATSTG_Release(elements
);
2081 /*********************************************************************
2085 * Perform the deletion of a stream's data
2088 static HRESULT
deleteStreamContents(
2089 StorageBaseImpl
*parentStorage
,
2090 DirRef indexToDelete
,
2091 DirEntry entryDataToDelete
)
2095 ULARGE_INTEGER size
;
2096 StgStreamImpl
*strm
, *strm2
;
2098 /* Invalidate any open stream objects. */
2099 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2101 if (strm
->dirEntry
== indexToDelete
)
2103 TRACE("Stream deleted %p\n", strm
);
2104 strm
->parentStorage
= NULL
;
2105 list_remove(&strm
->StrmListEntry
);
2109 size
.u
.HighPart
= 0;
2112 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2113 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2123 hr
= IStream_SetSize(pis
, size
);
2131 * Release the stream object.
2133 IStream_Release(pis
);
2138 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2142 case DIRENTRY_RELATION_PREVIOUS
:
2143 entry
->leftChild
= new_target
;
2145 case DIRENTRY_RELATION_NEXT
:
2146 entry
->rightChild
= new_target
;
2148 case DIRENTRY_RELATION_DIR
:
2149 entry
->dirRootEntry
= new_target
;
2156 /*************************************************************************
2160 * This method removes a directory entry from its parent storage tree without
2161 * freeing any resources attached to it.
2163 static HRESULT
removeFromTree(
2164 StorageBaseImpl
*This
,
2165 DirRef parentStorageIndex
,
2166 DirRef deletedIndex
)
2169 DirEntry entryToDelete
;
2170 DirEntry parentEntry
;
2171 DirRef parentEntryRef
;
2172 ULONG typeOfRelation
;
2174 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2180 * Find the element that links to the one we want to delete.
2182 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2183 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2188 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2191 * Replace the deleted entry with its left child
2193 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2195 hr
= StorageBaseImpl_WriteDirEntry(
2204 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2207 * We need to reinsert the right child somewhere. We already know it and
2208 * its children are greater than everything in the left tree, so we
2209 * insert it at the rightmost point in the left tree.
2211 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2212 DirEntry newRightChildParentEntry
;
2216 hr
= StorageBaseImpl_ReadDirEntry(
2218 newRightChildParent
,
2219 &newRightChildParentEntry
);
2225 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2226 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2227 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2229 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2231 hr
= StorageBaseImpl_WriteDirEntry(
2233 newRightChildParent
,
2234 &newRightChildParentEntry
);
2244 * Replace the deleted entry with its right child
2246 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2248 hr
= StorageBaseImpl_WriteDirEntry(
2262 /******************************************************************************
2263 * SetElementTimes (IStorage)
2265 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2267 const OLECHAR
*pwcsName
,/* [string][in] */
2268 const FILETIME
*pctime
, /* [in] */
2269 const FILETIME
*patime
, /* [in] */
2270 const FILETIME
*pmtime
) /* [in] */
2272 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2276 /******************************************************************************
2277 * SetStateBits (IStorage)
2279 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2281 DWORD grfStateBits
,/* [in] */
2282 DWORD grfMask
) /* [in] */
2284 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2287 return STG_E_REVERTED
;
2289 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2293 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2294 DirRef index
, const DirEntry
*data
)
2296 StorageImpl
*This
= (StorageImpl
*)base
;
2297 return StorageImpl_WriteDirEntry(This
, index
, data
);
2300 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2301 DirRef index
, DirEntry
*data
)
2303 StorageImpl
*This
= (StorageImpl
*)base
;
2304 return StorageImpl_ReadDirEntry(This
, index
, data
);
2307 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2311 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2313 if (!This
->blockChainCache
[i
])
2315 return &This
->blockChainCache
[i
];
2319 i
= This
->blockChainToEvict
;
2321 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2322 This
->blockChainCache
[i
] = NULL
;
2324 This
->blockChainToEvict
++;
2325 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2326 This
->blockChainToEvict
= 0;
2328 return &This
->blockChainCache
[i
];
2331 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2334 int i
, free_index
=-1;
2336 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2338 if (!This
->blockChainCache
[i
])
2340 if (free_index
== -1) free_index
= i
;
2342 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2344 return &This
->blockChainCache
[i
];
2348 if (free_index
== -1)
2350 free_index
= This
->blockChainToEvict
;
2352 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2353 This
->blockChainCache
[free_index
] = NULL
;
2355 This
->blockChainToEvict
++;
2356 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2357 This
->blockChainToEvict
= 0;
2360 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2361 return &This
->blockChainCache
[free_index
];
2364 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2368 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2370 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2372 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2373 This
->blockChainCache
[i
] = NULL
;
2379 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2380 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2382 StorageImpl
*This
= (StorageImpl
*)base
;
2387 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2388 if (FAILED(hr
)) return hr
;
2390 if (data
.size
.QuadPart
== 0)
2396 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2398 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2405 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2407 SmallBlockChainStream
*stream
;
2409 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2410 if (!stream
) return E_OUTOFMEMORY
;
2412 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2414 SmallBlockChainStream_Destroy(stream
);
2420 BlockChainStream
*stream
= NULL
;
2422 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2423 if (!stream
) return E_OUTOFMEMORY
;
2425 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2431 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2432 ULARGE_INTEGER newsize
)
2434 StorageImpl
*This
= (StorageImpl
*)base
;
2437 SmallBlockChainStream
*smallblock
=NULL
;
2438 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2440 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2441 if (FAILED(hr
)) return hr
;
2443 /* In simple mode keep the stream size above the small block limit */
2444 if (This
->base
.openFlags
& STGM_SIMPLE
)
2445 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2447 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2450 /* Create a block chain object of the appropriate type */
2451 if (data
.size
.QuadPart
== 0)
2453 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2455 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2456 if (!smallblock
) return E_OUTOFMEMORY
;
2460 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2461 bigblock
= *pbigblock
;
2462 if (!bigblock
) return E_OUTOFMEMORY
;
2465 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2467 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2468 if (!smallblock
) return E_OUTOFMEMORY
;
2472 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2473 bigblock
= *pbigblock
;
2474 if (!bigblock
) return E_OUTOFMEMORY
;
2477 /* Change the block chain type if necessary. */
2478 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2480 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2483 SmallBlockChainStream_Destroy(smallblock
);
2487 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2488 *pbigblock
= bigblock
;
2490 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2492 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2497 /* Set the size of the block chain. */
2500 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2501 SmallBlockChainStream_Destroy(smallblock
);
2505 BlockChainStream_SetSize(bigblock
, newsize
);
2508 /* Set the size in the directory entry. */
2509 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2512 data
.size
= newsize
;
2514 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2519 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2520 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2522 StorageImpl
*This
= (StorageImpl
*)base
;
2525 ULARGE_INTEGER newSize
;
2527 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2528 if (FAILED(hr
)) return hr
;
2530 /* Grow the stream if necessary */
2531 newSize
.QuadPart
= 0;
2532 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2534 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2536 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2540 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2541 if (FAILED(hr
)) return hr
;
2544 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2546 SmallBlockChainStream
*stream
;
2548 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2549 if (!stream
) return E_OUTOFMEMORY
;
2551 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2553 SmallBlockChainStream_Destroy(stream
);
2559 BlockChainStream
*stream
;
2561 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2562 if (!stream
) return E_OUTOFMEMORY
;
2564 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2570 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2573 StorageImpl
*This
= (StorageImpl
*)base
;
2574 DirEntry dst_data
, src_data
;
2577 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2580 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2584 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2585 dst_data
.startingBlock
= src_data
.startingBlock
;
2586 dst_data
.size
= src_data
.size
;
2588 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2594 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2596 StorageImpl
*This
= (StorageImpl
*) iface
;
2600 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2602 *result
= statstg
.pwcsName
;
2608 * Virtual function table for the IStorage32Impl class.
2610 static const IStorageVtbl Storage32Impl_Vtbl
=
2612 StorageBaseImpl_QueryInterface
,
2613 StorageBaseImpl_AddRef
,
2614 StorageBaseImpl_Release
,
2615 StorageBaseImpl_CreateStream
,
2616 StorageBaseImpl_OpenStream
,
2617 StorageBaseImpl_CreateStorage
,
2618 StorageBaseImpl_OpenStorage
,
2619 StorageBaseImpl_CopyTo
,
2620 StorageBaseImpl_MoveElementTo
,
2623 StorageBaseImpl_EnumElements
,
2624 StorageBaseImpl_DestroyElement
,
2625 StorageBaseImpl_RenameElement
,
2626 StorageBaseImpl_SetElementTimes
,
2627 StorageBaseImpl_SetClass
,
2628 StorageBaseImpl_SetStateBits
,
2629 StorageBaseImpl_Stat
2632 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2634 StorageImpl_Destroy
,
2635 StorageImpl_Invalidate
,
2637 StorageImpl_GetFilename
,
2638 StorageImpl_CreateDirEntry
,
2639 StorageImpl_BaseWriteDirEntry
,
2640 StorageImpl_BaseReadDirEntry
,
2641 StorageImpl_DestroyDirEntry
,
2642 StorageImpl_StreamReadAt
,
2643 StorageImpl_StreamWriteAt
,
2644 StorageImpl_StreamSetSize
,
2645 StorageImpl_StreamLink
2648 static HRESULT
StorageImpl_Construct(
2656 StorageImpl
** result
)
2660 DirEntry currentEntry
;
2661 DirRef currentEntryRef
;
2663 if ( FAILED( validateSTGM(openFlags
) ))
2664 return STG_E_INVALIDFLAG
;
2666 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2668 return E_OUTOFMEMORY
;
2670 memset(This
, 0, sizeof(StorageImpl
));
2672 list_init(&This
->base
.strmHead
);
2674 list_init(&This
->base
.storageHead
);
2676 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2677 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2678 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2679 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2681 This
->base
.create
= create
;
2683 This
->base
.reverted
= 0;
2686 * Initialize the big block cache.
2688 This
->bigBlockSize
= sector_size
;
2689 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2691 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
2694 This
->lockBytes
= pLkbyt
;
2695 ILockBytes_AddRef(pLkbyt
);
2703 ULARGE_INTEGER size
;
2704 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2707 * Initialize all header variables:
2708 * - The big block depot consists of one block and it is at block 0
2709 * - The directory table starts at block 1
2710 * - There is no small block depot
2712 memset( This
->bigBlockDepotStart
,
2714 sizeof(This
->bigBlockDepotStart
));
2716 This
->bigBlockDepotCount
= 1;
2717 This
->bigBlockDepotStart
[0] = 0;
2718 This
->rootStartBlock
= 1;
2719 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2720 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2721 if (sector_size
== 4096)
2722 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2724 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2725 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2726 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2727 This
->extBigBlockDepotCount
= 0;
2729 StorageImpl_SaveFileHeader(This
);
2732 * Add one block for the big block depot and one block for the directory table
2734 size
.u
.HighPart
= 0;
2735 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2736 ILockBytes_SetSize(This
->lockBytes
, size
);
2739 * Initialize the big block depot
2741 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2742 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2743 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2744 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2749 * Load the header for the file.
2751 hr
= StorageImpl_LoadFileHeader(This
);
2760 * There is no block depot cached yet.
2762 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2765 * Start searching for free blocks with block 0.
2767 This
->prevFreeBlock
= 0;
2769 This
->firstFreeSmallBlock
= 0;
2771 /* Read the extended big block depot locations. */
2772 if (This
->extBigBlockDepotCount
!= 0)
2774 ULONG current_block
= This
->extBigBlockDepotStart
;
2775 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
2778 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
2779 if (!This
->extBigBlockDepotLocations
)
2785 This
->extBigBlockDepotLocationsSize
= cache_size
;
2787 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
2789 if (current_block
== BLOCK_END_OF_CHAIN
)
2791 WARN("File has too few extended big block depot blocks.\n");
2792 hr
= STG_E_DOCFILECORRUPT
;
2795 This
->extBigBlockDepotLocations
[i
] = current_block
;
2796 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
2801 This
->extBigBlockDepotLocations
= NULL
;
2802 This
->extBigBlockDepotLocationsSize
= 0;
2806 * Create the block chain abstractions.
2808 if(!(This
->rootBlockChain
=
2809 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2811 hr
= STG_E_READFAULT
;
2815 if(!(This
->smallBlockDepotChain
=
2816 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2819 hr
= STG_E_READFAULT
;
2824 * Write the root storage entry (memory only)
2830 * Initialize the directory table
2832 memset(&rootEntry
, 0, sizeof(rootEntry
));
2833 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2834 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2835 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2836 rootEntry
.stgType
= STGTY_ROOT
;
2837 rootEntry
.leftChild
= DIRENTRY_NULL
;
2838 rootEntry
.rightChild
= DIRENTRY_NULL
;
2839 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2840 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2841 rootEntry
.size
.u
.HighPart
= 0;
2842 rootEntry
.size
.u
.LowPart
= 0;
2844 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2848 * Find the ID of the root storage.
2850 currentEntryRef
= 0;
2854 hr
= StorageImpl_ReadDirEntry(
2861 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2862 (currentEntry
.stgType
== STGTY_ROOT
) )
2864 This
->base
.storageDirEntry
= currentEntryRef
;
2870 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2874 hr
= STG_E_READFAULT
;
2879 * Create the block chain abstraction for the small block root chain.
2881 if(!(This
->smallBlockRootChain
=
2882 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2884 hr
= STG_E_READFAULT
;
2890 IStorage_Release((IStorage
*)This
);
2895 StorageImpl_Flush((StorageBaseImpl
*)This
);
2902 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2904 StorageImpl
*This
= (StorageImpl
*) iface
;
2906 StorageBaseImpl_DeleteAll(&This
->base
);
2908 This
->base
.reverted
= 1;
2911 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2913 StorageImpl
*This
= (StorageImpl
*) iface
;
2915 TRACE("(%p)\n", This
);
2917 StorageImpl_Flush(iface
);
2919 StorageImpl_Invalidate(iface
);
2921 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
2923 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2924 BlockChainStream_Destroy(This
->rootBlockChain
);
2925 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2927 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2928 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2930 if (This
->lockBytes
)
2931 ILockBytes_Release(This
->lockBytes
);
2932 HeapFree(GetProcessHeap(), 0, This
);
2935 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
)
2937 StorageImpl
*This
= (StorageImpl
*) iface
;
2940 TRACE("(%p)\n", This
);
2942 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
2945 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
2948 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
2950 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2951 if (This
->blockChainCache
[i
])
2952 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
2955 hr
= ILockBytes_Flush(This
->lockBytes
);
2960 /******************************************************************************
2961 * Storage32Impl_GetNextFreeBigBlock
2963 * Returns the index of the next free big block.
2964 * If the big block depot is filled, this method will enlarge it.
2967 static ULONG
StorageImpl_GetNextFreeBigBlock(
2970 ULONG depotBlockIndexPos
;
2971 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2973 ULONG depotBlockOffset
;
2974 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2975 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2977 ULONG freeBlock
= BLOCK_UNUSED
;
2978 ULARGE_INTEGER neededSize
;
2981 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2982 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2985 * Scan the entire big block depot until we find a block marked free
2987 while (nextBlockIndex
!= BLOCK_UNUSED
)
2989 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2991 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2994 * Grow the primary depot.
2996 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2998 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3001 * Add a block depot.
3003 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3004 This
->bigBlockDepotCount
++;
3005 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3008 * Flag it as a block depot.
3010 StorageImpl_SetNextBlockInChain(This
,
3014 /* Save new header information.
3016 StorageImpl_SaveFileHeader(This
);
3021 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3023 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3026 * Grow the extended depot.
3028 ULONG extIndex
= BLOCK_UNUSED
;
3029 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3030 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3032 if (extBlockOffset
== 0)
3034 /* We need an extended block.
3036 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3037 This
->extBigBlockDepotCount
++;
3038 depotBlockIndexPos
= extIndex
+ 1;
3041 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3044 * Add a block depot and mark it in the extended block.
3046 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
3047 This
->bigBlockDepotCount
++;
3048 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3050 /* Flag the block depot.
3052 StorageImpl_SetNextBlockInChain(This
,
3056 /* If necessary, flag the extended depot block.
3058 if (extIndex
!= BLOCK_UNUSED
)
3059 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3061 /* Save header information.
3063 StorageImpl_SaveFileHeader(This
);
3067 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3071 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3072 ( nextBlockIndex
!= BLOCK_UNUSED
))
3074 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3076 if (nextBlockIndex
== BLOCK_UNUSED
)
3078 freeBlock
= (depotIndex
* blocksPerDepot
) +
3079 (depotBlockOffset
/sizeof(ULONG
));
3082 depotBlockOffset
+= sizeof(ULONG
);
3087 depotBlockOffset
= 0;
3091 * make sure that the block physically exists before using it
3093 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3095 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3097 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3098 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3100 This
->prevFreeBlock
= freeBlock
;
3105 /******************************************************************************
3106 * Storage32Impl_AddBlockDepot
3108 * This will create a depot block, essentially it is a block initialized
3111 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
3113 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3116 * Initialize blocks as free
3118 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3119 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3122 /******************************************************************************
3123 * Storage32Impl_GetExtDepotBlock
3125 * Returns the index of the block that corresponds to the specified depot
3126 * index. This method is only for depot indexes equal or greater than
3127 * COUNT_BBDEPOTINHEADER.
3129 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3131 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3132 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3133 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3134 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3135 ULONG blockIndex
= BLOCK_UNUSED
;
3136 ULONG extBlockIndex
;
3138 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3140 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3141 return BLOCK_UNUSED
;
3143 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3145 if (extBlockIndex
!= BLOCK_UNUSED
)
3146 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3147 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3152 /******************************************************************************
3153 * Storage32Impl_SetExtDepotBlock
3155 * Associates the specified block index to the specified depot index.
3156 * This method is only for depot indexes equal or greater than
3157 * COUNT_BBDEPOTINHEADER.
3159 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3161 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3162 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3163 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3164 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3165 ULONG extBlockIndex
;
3167 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3169 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3171 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3173 if (extBlockIndex
!= BLOCK_UNUSED
)
3175 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3176 extBlockOffset
* sizeof(ULONG
),
3181 /******************************************************************************
3182 * Storage32Impl_AddExtBlockDepot
3184 * Creates an extended depot block.
3186 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3188 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3189 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3190 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3191 ULONG index
= BLOCK_UNUSED
;
3192 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3193 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3194 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3196 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3197 blocksPerDepotBlock
;
3199 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3202 * The first extended block.
3204 This
->extBigBlockDepotStart
= index
;
3209 * Find the last existing extended block.
3211 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3214 * Add the new extended block to the chain.
3216 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3221 * Initialize this block.
3223 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3224 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3226 /* Add the block to our cache. */
3227 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3229 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3230 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3232 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3233 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3235 This
->extBigBlockDepotLocations
= new_cache
;
3236 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3238 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3243 /******************************************************************************
3244 * Storage32Impl_FreeBigBlock
3246 * This method will flag the specified block as free in the big block depot.
3248 static void StorageImpl_FreeBigBlock(
3252 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3254 if (blockIndex
< This
->prevFreeBlock
)
3255 This
->prevFreeBlock
= blockIndex
;
3258 /************************************************************************
3259 * Storage32Impl_GetNextBlockInChain
3261 * This method will retrieve the block index of the next big block in
3264 * Params: This - Pointer to the Storage object.
3265 * blockIndex - Index of the block to retrieve the chain
3267 * nextBlockIndex - receives the return value.
3269 * Returns: This method returns the index of the next block in the chain.
3270 * It will return the constants:
3271 * BLOCK_SPECIAL - If the block given was not part of a
3273 * BLOCK_END_OF_CHAIN - If the block given was the last in
3275 * BLOCK_UNUSED - If the block given was not past of a chain
3277 * BLOCK_EXTBBDEPOT - This block is part of the extended
3280 * See Windows documentation for more details on IStorage methods.
3282 static HRESULT
StorageImpl_GetNextBlockInChain(
3285 ULONG
* nextBlockIndex
)
3287 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3288 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3289 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3290 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3292 ULONG depotBlockIndexPos
;
3293 int index
, num_blocks
;
3295 *nextBlockIndex
= BLOCK_SPECIAL
;
3297 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3299 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3300 This
->bigBlockDepotCount
);
3301 return STG_E_READFAULT
;
3305 * Cache the currently accessed depot block.
3307 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3309 This
->indexBlockDepotCached
= depotBlockCount
;
3311 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3313 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3318 * We have to look in the extended depot.
3320 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3323 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3326 return STG_E_READFAULT
;
3328 num_blocks
= This
->bigBlockSize
/ 4;
3330 for (index
= 0; index
< num_blocks
; index
++)
3332 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3333 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3337 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3342 /******************************************************************************
3343 * Storage32Impl_GetNextExtendedBlock
3345 * Given an extended block this method will return the next extended block.
3348 * The last ULONG of an extended block is the block index of the next
3349 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3353 * - The index of the next extended block
3354 * - BLOCK_UNUSED: there is no next extended block.
3355 * - Any other return values denotes failure.
3357 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3359 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3360 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3362 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3365 return nextBlockIndex
;
3368 /******************************************************************************
3369 * Storage32Impl_SetNextBlockInChain
3371 * This method will write the index of the specified block's next block
3372 * in the big block depot.
3374 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3377 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3378 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3379 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3382 static void StorageImpl_SetNextBlockInChain(
3387 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3388 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3389 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3390 ULONG depotBlockIndexPos
;
3392 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3393 assert(blockIndex
!= nextBlock
);
3395 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3397 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3402 * We have to look in the extended depot.
3404 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3407 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3410 * Update the cached block depot, if necessary.
3412 if (depotBlockCount
== This
->indexBlockDepotCached
)
3414 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3418 /******************************************************************************
3419 * Storage32Impl_LoadFileHeader
3421 * This method will read in the file header
3423 static HRESULT
StorageImpl_LoadFileHeader(
3427 BYTE headerBigBlock
[HEADER_SIZE
];
3429 ULARGE_INTEGER offset
;
3434 * Get a pointer to the big block of data containing the header.
3436 offset
.u
.HighPart
= 0;
3437 offset
.u
.LowPart
= 0;
3438 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3439 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3440 hr
= STG_E_FILENOTFOUND
;
3443 * Extract the information from the header.
3448 * Check for the "magic number" signature and return an error if it is not
3451 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3453 return STG_E_OLDFORMAT
;
3456 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3458 return STG_E_INVALIDHEADER
;
3461 StorageUtl_ReadWord(
3463 OFFSET_BIGBLOCKSIZEBITS
,
3464 &This
->bigBlockSizeBits
);
3466 StorageUtl_ReadWord(
3468 OFFSET_SMALLBLOCKSIZEBITS
,
3469 &This
->smallBlockSizeBits
);
3471 StorageUtl_ReadDWord(
3473 OFFSET_BBDEPOTCOUNT
,
3474 &This
->bigBlockDepotCount
);
3476 StorageUtl_ReadDWord(
3478 OFFSET_ROOTSTARTBLOCK
,
3479 &This
->rootStartBlock
);
3481 StorageUtl_ReadDWord(
3483 OFFSET_SMALLBLOCKLIMIT
,
3484 &This
->smallBlockLimit
);
3486 StorageUtl_ReadDWord(
3488 OFFSET_SBDEPOTSTART
,
3489 &This
->smallBlockDepotStart
);
3491 StorageUtl_ReadDWord(
3493 OFFSET_EXTBBDEPOTSTART
,
3494 &This
->extBigBlockDepotStart
);
3496 StorageUtl_ReadDWord(
3498 OFFSET_EXTBBDEPOTCOUNT
,
3499 &This
->extBigBlockDepotCount
);
3501 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3503 StorageUtl_ReadDWord(
3505 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3506 &(This
->bigBlockDepotStart
[index
]));
3510 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3512 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3513 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3516 * Right now, the code is making some assumptions about the size of the
3517 * blocks, just make sure they are what we're expecting.
3519 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3520 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3521 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3523 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3524 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3525 hr
= STG_E_INVALIDHEADER
;
3534 /******************************************************************************
3535 * Storage32Impl_SaveFileHeader
3537 * This method will save to the file the header
3539 static void StorageImpl_SaveFileHeader(
3542 BYTE headerBigBlock
[HEADER_SIZE
];
3545 ULARGE_INTEGER offset
;
3546 DWORD bytes_read
, bytes_written
;
3547 DWORD major_version
, dirsectorcount
;
3550 * Get a pointer to the big block of data containing the header.
3552 offset
.u
.HighPart
= 0;
3553 offset
.u
.LowPart
= 0;
3554 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3555 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3556 hr
= STG_E_FILENOTFOUND
;
3558 if (This
->bigBlockSizeBits
== 0x9)
3560 else if (This
->bigBlockSizeBits
== 0xc)
3564 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
3569 * If the block read failed, the file is probably new.
3574 * Initialize for all unknown fields.
3576 memset(headerBigBlock
, 0, HEADER_SIZE
);
3579 * Initialize the magic number.
3581 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3585 * Write the information to the header.
3587 StorageUtl_WriteWord(
3589 OFFSET_MINORVERSION
,
3592 StorageUtl_WriteWord(
3594 OFFSET_MAJORVERSION
,
3597 StorageUtl_WriteWord(
3599 OFFSET_BYTEORDERMARKER
,
3602 StorageUtl_WriteWord(
3604 OFFSET_BIGBLOCKSIZEBITS
,
3605 This
->bigBlockSizeBits
);
3607 StorageUtl_WriteWord(
3609 OFFSET_SMALLBLOCKSIZEBITS
,
3610 This
->smallBlockSizeBits
);
3612 if (major_version
>= 4)
3614 if (This
->rootBlockChain
)
3615 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
3617 /* This file is being created, and it will start out with one block. */
3621 /* This field must be 0 in versions older than 4 */
3624 StorageUtl_WriteDWord(
3626 OFFSET_DIRSECTORCOUNT
,
3629 StorageUtl_WriteDWord(
3631 OFFSET_BBDEPOTCOUNT
,
3632 This
->bigBlockDepotCount
);
3634 StorageUtl_WriteDWord(
3636 OFFSET_ROOTSTARTBLOCK
,
3637 This
->rootStartBlock
);
3639 StorageUtl_WriteDWord(
3641 OFFSET_SMALLBLOCKLIMIT
,
3642 This
->smallBlockLimit
);
3644 StorageUtl_WriteDWord(
3646 OFFSET_SBDEPOTSTART
,
3647 This
->smallBlockDepotStart
);
3649 StorageUtl_WriteDWord(
3651 OFFSET_SBDEPOTCOUNT
,
3652 This
->smallBlockDepotChain
?
3653 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3655 StorageUtl_WriteDWord(
3657 OFFSET_EXTBBDEPOTSTART
,
3658 This
->extBigBlockDepotStart
);
3660 StorageUtl_WriteDWord(
3662 OFFSET_EXTBBDEPOTCOUNT
,
3663 This
->extBigBlockDepotCount
);
3665 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3667 StorageUtl_WriteDWord(
3669 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3670 (This
->bigBlockDepotStart
[index
]));
3674 * Write the big block back to the file.
3676 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3679 /******************************************************************************
3680 * StorageImpl_ReadRawDirEntry
3682 * This method will read the raw data from a directory entry in the file.
3684 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3686 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3688 ULARGE_INTEGER offset
;
3692 offset
.u
.HighPart
= 0;
3693 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3695 hr
= BlockChainStream_ReadAt(
3696 This
->rootBlockChain
,
3702 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
3703 return STG_E_READFAULT
;
3708 /******************************************************************************
3709 * StorageImpl_WriteRawDirEntry
3711 * This method will write the raw data from a directory entry in the file.
3713 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3715 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3717 ULARGE_INTEGER offset
;
3721 offset
.u
.HighPart
= 0;
3722 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3724 hr
= BlockChainStream_WriteAt(
3725 This
->rootBlockChain
,
3734 /******************************************************************************
3737 * Update raw directory entry data from the fields in newData.
3739 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3741 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3743 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3746 buffer
+ OFFSET_PS_NAME
,
3748 DIRENTRY_NAME_BUFFER_LEN
);
3750 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3752 StorageUtl_WriteWord(
3754 OFFSET_PS_NAMELENGTH
,
3755 newData
->sizeOfNameString
);
3757 StorageUtl_WriteDWord(
3759 OFFSET_PS_LEFTCHILD
,
3760 newData
->leftChild
);
3762 StorageUtl_WriteDWord(
3764 OFFSET_PS_RIGHTCHILD
,
3765 newData
->rightChild
);
3767 StorageUtl_WriteDWord(
3770 newData
->dirRootEntry
);
3772 StorageUtl_WriteGUID(
3777 StorageUtl_WriteDWord(
3780 newData
->ctime
.dwLowDateTime
);
3782 StorageUtl_WriteDWord(
3784 OFFSET_PS_CTIMEHIGH
,
3785 newData
->ctime
.dwHighDateTime
);
3787 StorageUtl_WriteDWord(
3790 newData
->mtime
.dwLowDateTime
);
3792 StorageUtl_WriteDWord(
3794 OFFSET_PS_MTIMEHIGH
,
3795 newData
->ctime
.dwHighDateTime
);
3797 StorageUtl_WriteDWord(
3799 OFFSET_PS_STARTBLOCK
,
3800 newData
->startingBlock
);
3802 StorageUtl_WriteDWord(
3805 newData
->size
.u
.LowPart
);
3808 /******************************************************************************
3809 * Storage32Impl_ReadDirEntry
3811 * This method will read the specified directory entry.
3813 HRESULT
StorageImpl_ReadDirEntry(
3818 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3821 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3823 if (SUCCEEDED(readRes
))
3825 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3828 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3829 DIRENTRY_NAME_BUFFER_LEN
);
3830 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3832 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3834 StorageUtl_ReadWord(
3836 OFFSET_PS_NAMELENGTH
,
3837 &buffer
->sizeOfNameString
);
3839 StorageUtl_ReadDWord(
3841 OFFSET_PS_LEFTCHILD
,
3842 &buffer
->leftChild
);
3844 StorageUtl_ReadDWord(
3846 OFFSET_PS_RIGHTCHILD
,
3847 &buffer
->rightChild
);
3849 StorageUtl_ReadDWord(
3852 &buffer
->dirRootEntry
);
3854 StorageUtl_ReadGUID(
3859 StorageUtl_ReadDWord(
3862 &buffer
->ctime
.dwLowDateTime
);
3864 StorageUtl_ReadDWord(
3866 OFFSET_PS_CTIMEHIGH
,
3867 &buffer
->ctime
.dwHighDateTime
);
3869 StorageUtl_ReadDWord(
3872 &buffer
->mtime
.dwLowDateTime
);
3874 StorageUtl_ReadDWord(
3876 OFFSET_PS_MTIMEHIGH
,
3877 &buffer
->mtime
.dwHighDateTime
);
3879 StorageUtl_ReadDWord(
3881 OFFSET_PS_STARTBLOCK
,
3882 &buffer
->startingBlock
);
3884 StorageUtl_ReadDWord(
3887 &buffer
->size
.u
.LowPart
);
3889 buffer
->size
.u
.HighPart
= 0;
3895 /*********************************************************************
3896 * Write the specified directory entry to the file
3898 HRESULT
StorageImpl_WriteDirEntry(
3901 const DirEntry
* buffer
)
3903 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3906 UpdateRawDirEntry(currentEntry
, buffer
);
3908 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3912 static BOOL
StorageImpl_ReadBigBlock(
3917 ULARGE_INTEGER ulOffset
;
3920 ulOffset
.u
.HighPart
= 0;
3921 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3923 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3925 if (read
&& read
< This
->bigBlockSize
)
3927 /* File ends during this block; fill the rest with 0's. */
3928 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
3934 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3940 ULARGE_INTEGER ulOffset
;
3944 ulOffset
.u
.HighPart
= 0;
3945 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3946 ulOffset
.u
.LowPart
+= offset
;
3948 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3949 *value
= lendian32toh(tmp
);
3950 return (read
== sizeof(DWORD
));
3953 static BOOL
StorageImpl_WriteBigBlock(
3958 ULARGE_INTEGER ulOffset
;
3961 ulOffset
.u
.HighPart
= 0;
3962 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3964 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3965 return (wrote
== This
->bigBlockSize
);
3968 static BOOL
StorageImpl_WriteDWordToBigBlock(
3974 ULARGE_INTEGER ulOffset
;
3977 ulOffset
.u
.HighPart
= 0;
3978 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3979 ulOffset
.u
.LowPart
+= offset
;
3981 value
= htole32(value
);
3982 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3983 return (wrote
== sizeof(DWORD
));
3986 /******************************************************************************
3987 * Storage32Impl_SmallBlocksToBigBlocks
3989 * This method will convert a small block chain to a big block chain.
3990 * The small block chain will be destroyed.
3992 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3994 SmallBlockChainStream
** ppsbChain
)
3996 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3997 ULARGE_INTEGER size
, offset
;
3998 ULONG cbRead
, cbWritten
;
3999 ULARGE_INTEGER cbTotalRead
;
4000 DirRef streamEntryRef
;
4001 HRESULT resWrite
= S_OK
;
4003 DirEntry streamEntry
;
4005 BlockChainStream
*bbTempChain
= NULL
;
4006 BlockChainStream
*bigBlockChain
= NULL
;
4009 * Create a temporary big block chain that doesn't have
4010 * an associated directory entry. This temporary chain will be
4011 * used to copy data from small blocks to big blocks.
4013 bbTempChain
= BlockChainStream_Construct(This
,
4016 if(!bbTempChain
) return NULL
;
4018 * Grow the big block chain.
4020 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4021 BlockChainStream_SetSize(bbTempChain
, size
);
4024 * Copy the contents of the small block chain to the big block chain
4025 * by small block size increments.
4027 offset
.u
.LowPart
= 0;
4028 offset
.u
.HighPart
= 0;
4029 cbTotalRead
.QuadPart
= 0;
4031 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4034 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4036 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4039 if (FAILED(resRead
))
4044 cbTotalRead
.QuadPart
+= cbRead
;
4046 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4052 if (FAILED(resWrite
))
4055 offset
.u
.LowPart
+= cbRead
;
4059 resRead
= STG_E_READFAULT
;
4062 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4063 HeapFree(GetProcessHeap(),0,buffer
);
4065 size
.u
.HighPart
= 0;
4068 if (FAILED(resRead
) || FAILED(resWrite
))
4070 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4071 BlockChainStream_SetSize(bbTempChain
, size
);
4072 BlockChainStream_Destroy(bbTempChain
);
4077 * Destroy the small block chain.
4079 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4080 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4081 SmallBlockChainStream_Destroy(*ppsbChain
);
4085 * Change the directory entry. This chain is now a big block chain
4086 * and it doesn't reside in the small blocks chain anymore.
4088 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4090 streamEntry
.startingBlock
= bbHeadOfChain
;
4092 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4095 * Destroy the temporary entryless big block chain.
4096 * Create a new big block chain associated with this entry.
4098 BlockChainStream_Destroy(bbTempChain
);
4099 bigBlockChain
= BlockChainStream_Construct(This
,
4103 return bigBlockChain
;
4106 /******************************************************************************
4107 * Storage32Impl_BigBlocksToSmallBlocks
4109 * This method will convert a big block chain to a small block chain.
4110 * The big block chain will be destroyed on success.
4112 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4114 BlockChainStream
** ppbbChain
)
4116 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4117 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4118 DirRef streamEntryRef
;
4119 HRESULT resWrite
= S_OK
, resRead
;
4120 DirEntry streamEntry
;
4122 SmallBlockChainStream
* sbTempChain
;
4124 TRACE("%p %p\n", This
, ppbbChain
);
4126 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4132 size
= BlockChainStream_GetSize(*ppbbChain
);
4133 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4135 offset
.u
.HighPart
= 0;
4136 offset
.u
.LowPart
= 0;
4137 cbTotalRead
.QuadPart
= 0;
4138 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4141 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4142 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4150 cbTotalRead
.QuadPart
+= cbRead
;
4152 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4153 cbRead
, buffer
, &cbWritten
);
4155 if(FAILED(resWrite
))
4158 offset
.u
.LowPart
+= cbRead
;
4162 resRead
= STG_E_READFAULT
;
4165 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
4166 HeapFree(GetProcessHeap(), 0, buffer
);
4168 size
.u
.HighPart
= 0;
4171 if(FAILED(resRead
) || FAILED(resWrite
))
4173 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4174 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4175 SmallBlockChainStream_Destroy(sbTempChain
);
4179 /* destroy the original big block chain */
4180 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4181 BlockChainStream_SetSize(*ppbbChain
, size
);
4182 BlockChainStream_Destroy(*ppbbChain
);
4185 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4186 streamEntry
.startingBlock
= sbHeadOfChain
;
4187 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4189 SmallBlockChainStream_Destroy(sbTempChain
);
4190 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4193 static HRESULT
StorageBaseImpl_CopyStream(
4194 StorageBaseImpl
*dst
, DirRef dst_entry
,
4195 StorageBaseImpl
*src
, DirRef src_entry
)
4200 ULARGE_INTEGER bytes_copied
;
4201 ULONG bytestocopy
, bytesread
, byteswritten
;
4203 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4207 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4209 bytes_copied
.QuadPart
= 0;
4210 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4212 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4214 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4216 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4219 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4220 data
, &byteswritten
);
4223 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4224 bytes_copied
.QuadPart
+= byteswritten
;
4232 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4234 DirRef result
=This
->firstFreeEntry
;
4236 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4239 if (result
== This
->entries_size
)
4241 ULONG new_size
= This
->entries_size
* 2;
4242 TransactedDirEntry
*new_entries
;
4244 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4245 if (!new_entries
) return DIRENTRY_NULL
;
4247 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4248 HeapFree(GetProcessHeap(), 0, This
->entries
);
4250 This
->entries
= new_entries
;
4251 This
->entries_size
= new_size
;
4254 This
->entries
[result
].inuse
= 1;
4256 This
->firstFreeEntry
= result
+1;
4261 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4262 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4264 DirRef stubEntryRef
;
4265 TransactedDirEntry
*entry
;
4267 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4269 if (stubEntryRef
!= DIRENTRY_NULL
)
4271 entry
= &This
->entries
[stubEntryRef
];
4273 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4278 return stubEntryRef
;
4281 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4282 TransactedSnapshotImpl
*This
, DirRef entry
)
4287 if (!This
->entries
[entry
].read
)
4289 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4290 This
->entries
[entry
].transactedParentEntry
,
4293 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4295 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4297 if (data
.leftChild
== DIRENTRY_NULL
)
4301 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4303 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4305 if (data
.rightChild
== DIRENTRY_NULL
)
4309 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4311 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4313 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4319 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4320 This
->entries
[entry
].read
= 1;
4327 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4328 TransactedSnapshotImpl
*This
, DirRef entry
)
4332 if (!This
->entries
[entry
].stream_dirty
)
4334 DirEntry new_entrydata
;
4336 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4337 new_entrydata
.name
[0] = 'S';
4338 new_entrydata
.sizeOfNameString
= 1;
4339 new_entrydata
.stgType
= STGTY_STREAM
;
4340 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4341 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4342 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4343 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4345 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4346 &This
->entries
[entry
].stream_entry
);
4348 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4350 hr
= StorageBaseImpl_CopyStream(
4351 This
->scratch
, This
->entries
[entry
].stream_entry
,
4352 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4355 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4359 This
->entries
[entry
].stream_dirty
= 1;
4361 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4363 /* Since this entry is modified, and we aren't using its stream data, we
4364 * no longer care about the original entry. */
4366 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4368 if (delete_ref
!= DIRENTRY_NULL
)
4369 This
->entries
[delete_ref
].deleted
= 1;
4371 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4378 /* Find the first entry in a depth-first traversal. */
4379 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4380 TransactedSnapshotImpl
* This
, DirRef parent
)
4382 DirRef cursor
, prev
;
4383 TransactedDirEntry
*entry
;
4386 entry
= &This
->entries
[cursor
];
4389 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4392 cursor
= entry
->data
.leftChild
;
4393 entry
= &This
->entries
[cursor
];
4394 entry
->parent
= prev
;
4396 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4399 cursor
= entry
->data
.rightChild
;
4400 entry
= &This
->entries
[cursor
];
4401 entry
->parent
= prev
;
4403 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4406 cursor
= entry
->data
.dirRootEntry
;
4407 entry
= &This
->entries
[cursor
];
4408 entry
->parent
= prev
;
4417 /* Find the next entry in a depth-first traversal. */
4418 static DirRef
TransactedSnapshotImpl_FindNextChild(
4419 TransactedSnapshotImpl
* This
, DirRef current
)
4422 TransactedDirEntry
*parent_entry
;
4424 parent
= This
->entries
[current
].parent
;
4425 parent_entry
= &This
->entries
[parent
];
4427 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4429 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4431 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4432 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
4435 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4437 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
4438 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
4445 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4446 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
4447 TransactedSnapshotImpl
* This
, DirRef entry
)
4449 return entry
!= DIRENTRY_NULL
&&
4450 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
4453 /* Destroy the entries created by CopyTree. */
4454 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4455 TransactedSnapshotImpl
* This
, DirRef stop
)
4458 TransactedDirEntry
*entry
;
4459 ULARGE_INTEGER zero
;
4463 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
4466 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
4468 if (cursor
== DIRENTRY_NULL
)
4471 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
4473 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
4475 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
4477 entry
= &This
->entries
[cursor
];
4479 if (entry
->stream_dirty
)
4480 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4481 entry
->newTransactedParentEntry
, zero
);
4483 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4484 entry
->newTransactedParentEntry
);
4486 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4489 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4493 /* Make a copy of our edited tree that we can use in the parent. */
4494 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
4497 TransactedDirEntry
*entry
;
4500 cursor
= This
->base
.storageDirEntry
;
4501 entry
= &This
->entries
[cursor
];
4502 entry
->parent
= DIRENTRY_NULL
;
4503 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4505 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4508 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
4510 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
4511 entry
= &This
->entries
[cursor
];
4513 while (cursor
!= DIRENTRY_NULL
)
4515 /* Make a copy of this entry in the transacted parent. */
4517 (!entry
->dirty
&& !entry
->stream_dirty
&&
4518 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
4519 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
4520 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
4521 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
4526 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
4528 newData
.size
.QuadPart
= 0;
4529 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
4531 if (newData
.leftChild
!= DIRENTRY_NULL
)
4532 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
4534 if (newData
.rightChild
!= DIRENTRY_NULL
)
4535 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
4537 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
4538 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
4540 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
4541 &entry
->newTransactedParentEntry
);
4544 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4548 if (entry
->stream_dirty
)
4550 hr
= StorageBaseImpl_CopyStream(
4551 This
->transactedParent
, entry
->newTransactedParentEntry
,
4552 This
->scratch
, entry
->stream_entry
);
4554 else if (entry
->data
.size
.QuadPart
)
4556 hr
= StorageBaseImpl_StreamLink(
4557 This
->transactedParent
, entry
->newTransactedParentEntry
,
4558 entry
->transactedParentEntry
);
4563 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4564 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
4569 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
4570 entry
= &This
->entries
[cursor
];
4576 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4578 DWORD grfCommitFlags
) /* [in] */
4580 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4581 TransactedDirEntry
*root_entry
;
4582 DirRef i
, dir_root_ref
;
4584 ULARGE_INTEGER zero
;
4589 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4591 /* Cannot commit a read-only transacted storage */
4592 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4593 return STG_E_ACCESSDENIED
;
4595 /* To prevent data loss, we create the new structure in the file before we
4596 * delete the old one, so that in case of errors the old data is intact. We
4597 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4598 * needed in the rare situation where we have just enough free disk space to
4599 * overwrite the existing data. */
4601 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
4603 if (!root_entry
->read
)
4606 hr
= TransactedSnapshotImpl_CopyTree(This
);
4607 if (FAILED(hr
)) return hr
;
4609 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
4610 dir_root_ref
= DIRENTRY_NULL
;
4612 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
4614 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4616 /* Update the storage to use the new data in one step. */
4618 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4619 root_entry
->transactedParentEntry
, &data
);
4623 data
.dirRootEntry
= dir_root_ref
;
4624 data
.clsid
= root_entry
->data
.clsid
;
4625 data
.ctime
= root_entry
->data
.ctime
;
4626 data
.mtime
= root_entry
->data
.mtime
;
4628 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4629 root_entry
->transactedParentEntry
, &data
);
4632 /* Try to flush after updating the root storage, but if the flush fails, keep
4633 * going, on the theory that it'll either succeed later or the subsequent
4634 * writes will fail. */
4635 StorageBaseImpl_Flush(This
->transactedParent
);
4639 /* Destroy the old now-orphaned data. */
4640 for (i
=0; i
<This
->entries_size
; i
++)
4642 TransactedDirEntry
*entry
= &This
->entries
[i
];
4647 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
4648 entry
->transactedParentEntry
, zero
);
4649 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4650 entry
->transactedParentEntry
);
4651 memset(entry
, 0, sizeof(TransactedDirEntry
));
4652 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
4654 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
4656 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
4657 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
4658 entry
->transactedParentEntry
);
4659 if (entry
->stream_dirty
)
4661 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
4662 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
4663 entry
->stream_dirty
= 0;
4666 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
4673 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
4677 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
4682 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4685 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4686 ULARGE_INTEGER zero
;
4689 TRACE("(%p)\n", iface
);
4691 /* Destroy the open objects. */
4692 StorageBaseImpl_DeleteAll(&This
->base
);
4694 /* Clear out the scratch file. */
4696 for (i
=0; i
<This
->entries_size
; i
++)
4698 if (This
->entries
[i
].stream_dirty
)
4700 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
4703 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
4707 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
4709 This
->firstFreeEntry
= 0;
4710 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
4715 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4717 if (!This
->reverted
)
4719 TRACE("Storage invalidated (stg=%p)\n", This
);
4723 StorageBaseImpl_DeleteAll(This
);
4727 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4729 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4731 TransactedSnapshotImpl_Revert((IStorage
*)iface
);
4733 IStorage_Release((IStorage
*)This
->transactedParent
);
4735 IStorage_Release((IStorage
*)This
->scratch
);
4737 HeapFree(GetProcessHeap(), 0, This
->entries
);
4739 HeapFree(GetProcessHeap(), 0, This
);
4742 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
4744 /* We only need to flush when committing. */
4748 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
4750 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4752 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
4755 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4756 const DirEntry
*newData
, DirRef
*index
)
4758 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4760 TransactedDirEntry
*new_entry
;
4762 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
4763 if (new_ref
== DIRENTRY_NULL
)
4764 return E_OUTOFMEMORY
;
4766 new_entry
= &This
->entries
[new_ref
];
4768 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
4769 new_entry
->read
= 1;
4770 new_entry
->dirty
= 1;
4771 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
4775 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
4780 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4781 DirRef index
, const DirEntry
*data
)
4783 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4786 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4788 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4789 if (FAILED(hr
)) return hr
;
4791 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
4793 if (index
!= This
->base
.storageDirEntry
)
4795 This
->entries
[index
].dirty
= 1;
4797 if (data
->size
.QuadPart
== 0 &&
4798 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4800 /* Since this entry is modified, and we aren't using its stream data, we
4801 * no longer care about the original entry. */
4803 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4805 if (delete_ref
!= DIRENTRY_NULL
)
4806 This
->entries
[delete_ref
].deleted
= 1;
4808 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4815 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4816 DirRef index
, DirEntry
*data
)
4818 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4821 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4822 if (FAILED(hr
)) return hr
;
4824 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
4826 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
4831 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4834 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4836 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
4837 This
->entries
[index
].data
.size
.QuadPart
!= 0)
4839 /* If we deleted this entry while it has stream data. We must have left the
4840 * data because some other entry is using it, and we need to leave the
4841 * original entry alone. */
4842 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
4843 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
4847 This
->entries
[index
].deleted
= 1;
4853 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4854 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4856 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4858 if (This
->entries
[index
].stream_dirty
)
4860 return StorageBaseImpl_StreamReadAt(This
->scratch
,
4861 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
4863 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
4865 /* This stream doesn't live in the parent, and we haven't allocated storage
4872 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
4873 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
4877 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4878 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4880 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4883 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4884 if (FAILED(hr
)) return hr
;
4886 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4887 if (FAILED(hr
)) return hr
;
4889 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
4890 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
4892 if (SUCCEEDED(hr
) && size
!= 0)
4893 This
->entries
[index
].data
.size
.QuadPart
= max(
4894 This
->entries
[index
].data
.size
.QuadPart
,
4895 offset
.QuadPart
+ size
);
4900 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4901 DirRef index
, ULARGE_INTEGER newsize
)
4903 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4906 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
4907 if (FAILED(hr
)) return hr
;
4909 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
4912 if (newsize
.QuadPart
== 0)
4914 /* Destroy any parent references or entries in the scratch file. */
4915 if (This
->entries
[index
].stream_dirty
)
4917 ULARGE_INTEGER zero
;
4919 StorageBaseImpl_StreamSetSize(This
->scratch
,
4920 This
->entries
[index
].stream_entry
, zero
);
4921 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
4922 This
->entries
[index
].stream_entry
);
4923 This
->entries
[index
].stream_dirty
= 0;
4925 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
4928 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
4930 if (delete_ref
!= DIRENTRY_NULL
)
4931 This
->entries
[delete_ref
].deleted
= 1;
4933 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
4938 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
4939 if (FAILED(hr
)) return hr
;
4941 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
4942 This
->entries
[index
].stream_entry
, newsize
);
4946 This
->entries
[index
].data
.size
= newsize
;
4951 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
4952 DirRef dst
, DirRef src
)
4954 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4956 TransactedDirEntry
*dst_entry
, *src_entry
;
4958 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
4959 if (FAILED(hr
)) return hr
;
4961 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
4962 if (FAILED(hr
)) return hr
;
4964 dst_entry
= &This
->entries
[dst
];
4965 src_entry
= &This
->entries
[src
];
4967 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
4968 dst_entry
->stream_entry
= src_entry
->stream_entry
;
4969 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
4970 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
4971 dst_entry
->data
.size
= src_entry
->data
.size
;
4976 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4978 StorageBaseImpl_QueryInterface
,
4979 StorageBaseImpl_AddRef
,
4980 StorageBaseImpl_Release
,
4981 StorageBaseImpl_CreateStream
,
4982 StorageBaseImpl_OpenStream
,
4983 StorageBaseImpl_CreateStorage
,
4984 StorageBaseImpl_OpenStorage
,
4985 StorageBaseImpl_CopyTo
,
4986 StorageBaseImpl_MoveElementTo
,
4987 TransactedSnapshotImpl_Commit
,
4988 TransactedSnapshotImpl_Revert
,
4989 StorageBaseImpl_EnumElements
,
4990 StorageBaseImpl_DestroyElement
,
4991 StorageBaseImpl_RenameElement
,
4992 StorageBaseImpl_SetElementTimes
,
4993 StorageBaseImpl_SetClass
,
4994 StorageBaseImpl_SetStateBits
,
4995 StorageBaseImpl_Stat
4998 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5000 TransactedSnapshotImpl_Destroy
,
5001 TransactedSnapshotImpl_Invalidate
,
5002 TransactedSnapshotImpl_Flush
,
5003 TransactedSnapshotImpl_GetFilename
,
5004 TransactedSnapshotImpl_CreateDirEntry
,
5005 TransactedSnapshotImpl_WriteDirEntry
,
5006 TransactedSnapshotImpl_ReadDirEntry
,
5007 TransactedSnapshotImpl_DestroyDirEntry
,
5008 TransactedSnapshotImpl_StreamReadAt
,
5009 TransactedSnapshotImpl_StreamWriteAt
,
5010 TransactedSnapshotImpl_StreamSetSize
,
5011 TransactedSnapshotImpl_StreamLink
5014 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5015 TransactedSnapshotImpl
** result
)
5019 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5022 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5024 /* This is OK because the property set storage functions use the IStorage functions. */
5025 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
5027 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5029 list_init(&(*result
)->base
.strmHead
);
5031 list_init(&(*result
)->base
.storageHead
);
5033 (*result
)->base
.ref
= 1;
5035 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5037 /* Create a new temporary storage to act as the scratch file. */
5038 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
,
5039 0, (IStorage
**)&(*result
)->scratch
);
5043 ULONG num_entries
= 20;
5045 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5047 (*result
)->entries_size
= num_entries
;
5049 (*result
)->firstFreeEntry
= 0;
5051 if ((*result
)->entries
)
5053 /* parentStorage already has 1 reference, which we take over here. */
5054 (*result
)->transactedParent
= parentStorage
;
5056 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
5058 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5062 IStorage_Release((IStorage
*)(*result
)->scratch
);
5068 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
5073 return E_OUTOFMEMORY
;
5076 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
5077 StorageBaseImpl
** result
)
5081 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
5083 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
5086 return TransactedSnapshotImpl_Construct(parentStorage
,
5087 (TransactedSnapshotImpl
**)result
);
5090 static HRESULT
Storage_Construct(
5098 StorageBaseImpl
** result
)
5100 StorageImpl
*newStorage
;
5101 StorageBaseImpl
*newTransactedStorage
;
5104 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
5105 if (FAILED(hr
)) goto end
;
5107 if (openFlags
& STGM_TRANSACTED
)
5109 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
5111 IStorage_Release((IStorage
*)newStorage
);
5113 *result
= newTransactedStorage
;
5116 *result
= &newStorage
->base
;
5122 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
5124 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5126 if (!This
->base
.reverted
)
5128 TRACE("Storage invalidated (stg=%p)\n", This
);
5130 This
->base
.reverted
= 1;
5132 This
->parentStorage
= NULL
;
5134 StorageBaseImpl_DeleteAll(&This
->base
);
5136 list_remove(&This
->ParentListEntry
);
5140 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
5142 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5144 StorageInternalImpl_Invalidate(&This
->base
);
5146 HeapFree(GetProcessHeap(), 0, This
);
5149 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
5151 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5153 return StorageBaseImpl_Flush(This
->parentStorage
);
5156 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5158 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
5160 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
5163 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
5164 const DirEntry
*newData
, DirRef
*index
)
5166 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5168 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
5172 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
5173 DirRef index
, const DirEntry
*data
)
5175 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5177 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
5181 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
5182 DirRef index
, DirEntry
*data
)
5184 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5186 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5190 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5193 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5195 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
5199 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
5200 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5202 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5204 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
5205 index
, offset
, size
, buffer
, bytesRead
);
5208 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
5209 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5211 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5213 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
5214 index
, offset
, size
, buffer
, bytesWritten
);
5217 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
5218 DirRef index
, ULARGE_INTEGER newsize
)
5220 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5222 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
5226 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
5227 DirRef dst
, DirRef src
)
5229 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
5231 return StorageBaseImpl_StreamLink(This
->parentStorage
,
5235 /******************************************************************************
5237 ** Storage32InternalImpl_Commit
5240 static HRESULT WINAPI
StorageInternalImpl_Commit(
5242 DWORD grfCommitFlags
) /* [in] */
5244 StorageBaseImpl
* base
= (StorageBaseImpl
*) iface
;
5245 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5246 return StorageBaseImpl_Flush(base
);
5249 /******************************************************************************
5251 ** Storage32InternalImpl_Revert
5254 static HRESULT WINAPI
StorageInternalImpl_Revert(
5257 FIXME("(%p): stub\n", iface
);
5261 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
5263 IStorage_Release((IStorage
*)This
->parentStorage
);
5264 HeapFree(GetProcessHeap(), 0, This
);
5267 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
5268 IEnumSTATSTG
* iface
,
5272 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5275 return E_INVALIDARG
;
5279 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
5280 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
5283 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
5287 return E_NOINTERFACE
;
5290 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
5291 IEnumSTATSTG
* iface
)
5293 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5294 return InterlockedIncrement(&This
->ref
);
5297 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
5298 IEnumSTATSTG
* iface
)
5300 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5304 newRef
= InterlockedDecrement(&This
->ref
);
5308 IEnumSTATSTGImpl_Destroy(This
);
5314 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
5315 IEnumSTATSTGImpl
* This
,
5318 DirRef result
= DIRENTRY_NULL
;
5322 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
5324 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5325 This
->parentStorage
->storageDirEntry
, &entry
);
5326 searchNode
= entry
.dirRootEntry
;
5328 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
5330 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
5334 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
5338 searchNode
= entry
.rightChild
;
5342 result
= searchNode
;
5343 memcpy(result_name
, entry
.name
, sizeof(result_name
));
5344 searchNode
= entry
.leftChild
;
5352 if (result
!= DIRENTRY_NULL
)
5353 memcpy(This
->name
, result_name
, sizeof(result_name
));
5359 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
5360 IEnumSTATSTG
* iface
,
5363 ULONG
* pceltFetched
)
5365 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5367 DirEntry currentEntry
;
5368 STATSTG
* currentReturnStruct
= rgelt
;
5369 ULONG objectFetched
= 0;
5370 DirRef currentSearchNode
;
5373 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
5374 return E_INVALIDARG
;
5376 if (This
->parentStorage
->reverted
)
5377 return STG_E_REVERTED
;
5380 * To avoid the special case, get another pointer to a ULONG value if
5381 * the caller didn't supply one.
5383 if (pceltFetched
==0)
5384 pceltFetched
= &objectFetched
;
5387 * Start the iteration, we will iterate until we hit the end of the
5388 * linked list or until we hit the number of items to iterate through
5392 while ( *pceltFetched
< celt
)
5394 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5396 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5400 * Read the entry from the storage.
5402 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
5407 * Copy the information to the return buffer.
5409 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
5410 currentReturnStruct
,
5415 * Step to the next item in the iteration
5418 currentReturnStruct
++;
5421 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
5428 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
5429 IEnumSTATSTG
* iface
,
5432 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5434 ULONG objectFetched
= 0;
5435 DirRef currentSearchNode
;
5438 if (This
->parentStorage
->reverted
)
5439 return STG_E_REVERTED
;
5441 while ( (objectFetched
< celt
) )
5443 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
5445 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
5451 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
5457 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
5458 IEnumSTATSTG
* iface
)
5460 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5462 if (This
->parentStorage
->reverted
)
5463 return STG_E_REVERTED
;
5470 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
5471 IEnumSTATSTG
* iface
,
5472 IEnumSTATSTG
** ppenum
)
5474 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
5476 IEnumSTATSTGImpl
* newClone
;
5478 if (This
->parentStorage
->reverted
)
5479 return STG_E_REVERTED
;
5482 * Perform a sanity check on the parameters.
5485 return E_INVALIDARG
;
5487 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
5488 This
->storageDirEntry
);
5492 * The new clone enumeration must point to the same current node as
5495 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
5497 *ppenum
= (IEnumSTATSTG
*)newClone
;
5500 * Don't forget to nail down a reference to the clone before
5503 IEnumSTATSTGImpl_AddRef(*ppenum
);
5509 * Virtual function table for the IEnumSTATSTGImpl class.
5511 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
5513 IEnumSTATSTGImpl_QueryInterface
,
5514 IEnumSTATSTGImpl_AddRef
,
5515 IEnumSTATSTGImpl_Release
,
5516 IEnumSTATSTGImpl_Next
,
5517 IEnumSTATSTGImpl_Skip
,
5518 IEnumSTATSTGImpl_Reset
,
5519 IEnumSTATSTGImpl_Clone
5522 /******************************************************************************
5523 ** IEnumSTATSTGImpl implementation
5526 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
5527 StorageBaseImpl
* parentStorage
,
5528 DirRef storageDirEntry
)
5530 IEnumSTATSTGImpl
* newEnumeration
;
5532 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
5534 if (newEnumeration
!=0)
5537 * Set-up the virtual function table and reference count.
5539 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
5540 newEnumeration
->ref
= 0;
5543 * We want to nail-down the reference to the storage in case the
5544 * enumeration out-lives the storage in the client application.
5546 newEnumeration
->parentStorage
= parentStorage
;
5547 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
5549 newEnumeration
->storageDirEntry
= storageDirEntry
;
5552 * Make sure the current node of the iterator is the first one.
5554 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
5557 return newEnumeration
;
5561 * Virtual function table for the Storage32InternalImpl class.
5563 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
5565 StorageBaseImpl_QueryInterface
,
5566 StorageBaseImpl_AddRef
,
5567 StorageBaseImpl_Release
,
5568 StorageBaseImpl_CreateStream
,
5569 StorageBaseImpl_OpenStream
,
5570 StorageBaseImpl_CreateStorage
,
5571 StorageBaseImpl_OpenStorage
,
5572 StorageBaseImpl_CopyTo
,
5573 StorageBaseImpl_MoveElementTo
,
5574 StorageInternalImpl_Commit
,
5575 StorageInternalImpl_Revert
,
5576 StorageBaseImpl_EnumElements
,
5577 StorageBaseImpl_DestroyElement
,
5578 StorageBaseImpl_RenameElement
,
5579 StorageBaseImpl_SetElementTimes
,
5580 StorageBaseImpl_SetClass
,
5581 StorageBaseImpl_SetStateBits
,
5582 StorageBaseImpl_Stat
5585 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
5587 StorageInternalImpl_Destroy
,
5588 StorageInternalImpl_Invalidate
,
5589 StorageInternalImpl_Flush
,
5590 StorageInternalImpl_GetFilename
,
5591 StorageInternalImpl_CreateDirEntry
,
5592 StorageInternalImpl_WriteDirEntry
,
5593 StorageInternalImpl_ReadDirEntry
,
5594 StorageInternalImpl_DestroyDirEntry
,
5595 StorageInternalImpl_StreamReadAt
,
5596 StorageInternalImpl_StreamWriteAt
,
5597 StorageInternalImpl_StreamSetSize
,
5598 StorageInternalImpl_StreamLink
5601 /******************************************************************************
5602 ** Storage32InternalImpl implementation
5605 static StorageInternalImpl
* StorageInternalImpl_Construct(
5606 StorageBaseImpl
* parentStorage
,
5608 DirRef storageDirEntry
)
5610 StorageInternalImpl
* newStorage
;
5612 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
5616 list_init(&newStorage
->base
.strmHead
);
5618 list_init(&newStorage
->base
.storageHead
);
5621 * Initialize the virtual function table.
5623 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
5624 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
5625 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
5626 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
5628 newStorage
->base
.reverted
= 0;
5630 newStorage
->base
.ref
= 1;
5632 newStorage
->parentStorage
= parentStorage
;
5635 * Keep a reference to the directory entry of this storage
5637 newStorage
->base
.storageDirEntry
= storageDirEntry
;
5639 newStorage
->base
.create
= 0;
5647 /******************************************************************************
5648 ** StorageUtl implementation
5651 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
5655 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
5656 *value
= lendian16toh(tmp
);
5659 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
5661 value
= htole16(value
);
5662 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
5665 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
5669 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
5670 *value
= lendian32toh(tmp
);
5673 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
5675 value
= htole32(value
);
5676 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
5679 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
5680 ULARGE_INTEGER
* value
)
5682 #ifdef WORDS_BIGENDIAN
5685 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5686 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
5687 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
5689 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
5693 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
5694 const ULARGE_INTEGER
*value
)
5696 #ifdef WORDS_BIGENDIAN
5699 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
5700 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
5701 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
5703 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
5707 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
5709 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
5710 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
5711 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
5713 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
5716 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
5718 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
5719 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
5720 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
5722 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
5725 void StorageUtl_CopyDirEntryToSTATSTG(
5726 StorageBaseImpl
* storage
,
5727 STATSTG
* destination
,
5728 const DirEntry
* source
,
5732 * The copy of the string occurs only when the flag is not set
5734 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
5736 /* Use the filename for the root storage. */
5737 destination
->pwcsName
= 0;
5738 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
5740 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
5741 (source
->name
[0] == 0) )
5743 destination
->pwcsName
= 0;
5747 destination
->pwcsName
=
5748 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
5750 strcpyW(destination
->pwcsName
, source
->name
);
5753 switch (source
->stgType
)
5757 destination
->type
= STGTY_STORAGE
;
5760 destination
->type
= STGTY_STREAM
;
5763 destination
->type
= STGTY_STREAM
;
5767 destination
->cbSize
= source
->size
;
5769 currentReturnStruct->mtime = {0}; TODO
5770 currentReturnStruct->ctime = {0};
5771 currentReturnStruct->atime = {0};
5773 destination
->grfMode
= 0;
5774 destination
->grfLocksSupported
= 0;
5775 destination
->clsid
= source
->clsid
;
5776 destination
->grfStateBits
= 0;
5777 destination
->reserved
= 0;
5780 /******************************************************************************
5781 ** BlockChainStream implementation
5784 /* Read and save the index of all blocks in this stream. */
5785 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5787 ULONG next_sector
, next_offset
;
5789 struct BlockChainRun
*last_run
;
5791 if (This
->indexCacheLen
== 0)
5795 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5799 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5800 next_offset
= last_run
->lastOffset
+1;
5801 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5802 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5804 if (FAILED(hr
)) return hr
;
5807 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5809 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5811 /* Add the current block to the cache. */
5812 if (This
->indexCacheSize
== 0)
5814 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5815 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5816 This
->indexCacheSize
= 16;
5818 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5820 struct BlockChainRun
*new_cache
;
5823 new_size
= This
->indexCacheSize
* 2;
5824 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5825 if (!new_cache
) return E_OUTOFMEMORY
;
5826 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5828 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5829 This
->indexCache
= new_cache
;
5830 This
->indexCacheSize
= new_size
;
5833 This
->indexCacheLen
++;
5834 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5835 last_run
->firstSector
= next_sector
;
5836 last_run
->firstOffset
= next_offset
;
5839 last_run
->lastOffset
= next_offset
;
5841 /* Find the next block. */
5843 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5844 if (FAILED(hr
)) return hr
;
5847 if (This
->indexCacheLen
)
5849 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5850 This
->numBlocks
= last_run
->lastOffset
+1;
5854 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5855 This
->numBlocks
= 0;
5861 /* Locate the nth block in this stream. */
5862 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5864 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5865 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5867 if (offset
>= This
->numBlocks
)
5868 return BLOCK_END_OF_CHAIN
;
5870 while (min_run
< max_run
)
5872 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5873 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5875 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5876 max_run
= run_to_check
-1;
5878 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5880 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5881 min_run
= run_to_check
+1;
5884 /* Block is in this run. */
5885 min_run
= max_run
= run_to_check
;
5888 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5891 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
5892 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
5894 BlockChainBlock
*result
=NULL
;
5898 if (This
->cachedBlocks
[i
].index
== index
)
5900 *sector
= This
->cachedBlocks
[i
].sector
;
5901 *block
= &This
->cachedBlocks
[i
];
5905 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
5906 if (*sector
== BLOCK_END_OF_CHAIN
)
5907 return STG_E_DOCFILECORRUPT
;
5911 if (This
->cachedBlocks
[0].index
== 0xffffffff)
5912 result
= &This
->cachedBlocks
[0];
5913 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
5914 result
= &This
->cachedBlocks
[1];
5917 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
5918 if (This
->blockToEvict
== 2)
5919 This
->blockToEvict
= 0;
5924 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
5925 return STG_E_WRITEFAULT
;
5930 result
->index
= index
;
5931 result
->sector
= *sector
;
5938 BlockChainStream
* BlockChainStream_Construct(
5939 StorageImpl
* parentStorage
,
5940 ULONG
* headOfStreamPlaceHolder
,
5943 BlockChainStream
* newStream
;
5945 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5947 newStream
->parentStorage
= parentStorage
;
5948 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5949 newStream
->ownerDirEntry
= dirEntry
;
5950 newStream
->indexCache
= NULL
;
5951 newStream
->indexCacheLen
= 0;
5952 newStream
->indexCacheSize
= 0;
5953 newStream
->cachedBlocks
[0].index
= 0xffffffff;
5954 newStream
->cachedBlocks
[0].dirty
= 0;
5955 newStream
->cachedBlocks
[1].index
= 0xffffffff;
5956 newStream
->cachedBlocks
[1].dirty
= 0;
5957 newStream
->blockToEvict
= 0;
5959 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
5961 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
5962 HeapFree(GetProcessHeap(), 0, newStream
);
5969 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
5972 if (!This
) return S_OK
;
5975 if (This
->cachedBlocks
[i
].dirty
)
5977 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
5978 This
->cachedBlocks
[i
].dirty
= 0;
5980 return STG_E_WRITEFAULT
;
5986 void BlockChainStream_Destroy(BlockChainStream
* This
)
5990 BlockChainStream_Flush(This
);
5991 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5993 HeapFree(GetProcessHeap(), 0, This
);
5996 /******************************************************************************
5997 * BlockChainStream_GetHeadOfChain
5999 * Returns the head of this stream chain.
6000 * Some special chains don't have directory entries, their heads are kept in
6001 * This->headOfStreamPlaceHolder.
6004 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6006 DirEntry chainEntry
;
6009 if (This
->headOfStreamPlaceHolder
!= 0)
6010 return *(This
->headOfStreamPlaceHolder
);
6012 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6014 hr
= StorageImpl_ReadDirEntry(
6015 This
->parentStorage
,
6016 This
->ownerDirEntry
,
6021 return chainEntry
.startingBlock
;
6025 return BLOCK_END_OF_CHAIN
;
6028 /******************************************************************************
6029 * BlockChainStream_GetCount
6031 * Returns the number of blocks that comprises this chain.
6032 * This is not the size of the stream as the last block may not be full!
6034 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
6036 return This
->numBlocks
;
6039 /******************************************************************************
6040 * BlockChainStream_ReadAt
6042 * Reads a specified number of bytes from this chain at the specified offset.
6043 * bytesRead may be NULL.
6044 * Failure will be returned if the specified number of bytes has not been read.
6046 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
6047 ULARGE_INTEGER offset
,
6052 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6053 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6054 ULONG bytesToReadInBuffer
;
6057 ULARGE_INTEGER stream_size
;
6059 BlockChainBlock
*cachedBlock
;
6061 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
6064 * Find the first block in the stream that contains part of the buffer.
6066 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
6070 stream_size
= BlockChainStream_GetSize(This
);
6071 if (stream_size
.QuadPart
> offset
.QuadPart
)
6072 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6077 * Start reading the buffer.
6079 bufferWalker
= buffer
;
6083 ULARGE_INTEGER ulOffset
;
6087 * Calculate how many bytes we can copy from this big block.
6089 bytesToReadInBuffer
=
6090 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6092 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
6099 /* Not in cache, and we're going to read past the end of the block. */
6100 ulOffset
.u
.HighPart
= 0;
6101 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6104 StorageImpl_ReadAt(This
->parentStorage
,
6107 bytesToReadInBuffer
,
6112 if (!cachedBlock
->read
)
6114 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6115 return STG_E_READFAULT
;
6117 cachedBlock
->read
= 1;
6120 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
6121 bytesReadAt
= bytesToReadInBuffer
;
6124 blockNoInSequence
++;
6125 bufferWalker
+= bytesReadAt
;
6126 size
-= bytesReadAt
;
6127 *bytesRead
+= bytesReadAt
;
6128 offsetInBlock
= 0; /* There is no offset on the next block */
6130 if (bytesToReadInBuffer
!= bytesReadAt
)
6137 /******************************************************************************
6138 * BlockChainStream_WriteAt
6140 * Writes the specified number of bytes to this chain at the specified offset.
6141 * Will fail if not all specified number of bytes have been written.
6143 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
6144 ULARGE_INTEGER offset
,
6147 ULONG
* bytesWritten
)
6149 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6150 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
6153 const BYTE
* bufferWalker
;
6155 BlockChainBlock
*cachedBlock
;
6158 bufferWalker
= buffer
;
6162 ULARGE_INTEGER ulOffset
;
6163 DWORD bytesWrittenAt
;
6166 * Calculate how many bytes we can copy to this big block.
6169 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
6171 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
6173 /* BlockChainStream_SetSize should have already been called to ensure we have
6174 * enough blocks in the chain to write into */
6177 ERR("not enough blocks in chain to write data\n");
6183 /* Not in cache, and we're going to write past the end of the block. */
6184 ulOffset
.u
.HighPart
= 0;
6185 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
6188 StorageImpl_WriteAt(This
->parentStorage
,
6196 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
6198 if (!StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
))
6199 return STG_E_READFAULT
;
6202 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
6203 bytesWrittenAt
= bytesToWrite
;
6204 cachedBlock
->read
= 1;
6205 cachedBlock
->dirty
= 1;
6208 blockNoInSequence
++;
6209 bufferWalker
+= bytesWrittenAt
;
6210 size
-= bytesWrittenAt
;
6211 *bytesWritten
+= bytesWrittenAt
;
6212 offsetInBlock
= 0; /* There is no offset on the next block */
6214 if (bytesWrittenAt
!= bytesToWrite
)
6218 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6221 /******************************************************************************
6222 * BlockChainStream_Shrink
6224 * Shrinks this chain in the big block depot.
6226 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
6227 ULARGE_INTEGER newSize
)
6234 * Figure out how many blocks are needed to contain the new size
6236 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6238 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6244 * Go to the new end of chain
6246 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
6248 /* Mark the new end of chain */
6249 StorageImpl_SetNextBlockInChain(
6250 This
->parentStorage
,
6252 BLOCK_END_OF_CHAIN
);
6254 This
->tailIndex
= blockIndex
;
6258 if (This
->headOfStreamPlaceHolder
!= 0)
6260 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
6264 DirEntry chainEntry
;
6265 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6267 StorageImpl_ReadDirEntry(
6268 This
->parentStorage
,
6269 This
->ownerDirEntry
,
6272 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6274 StorageImpl_WriteDirEntry(
6275 This
->parentStorage
,
6276 This
->ownerDirEntry
,
6280 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6283 This
->numBlocks
= numBlocks
;
6286 * Mark the extra blocks as free
6288 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
6290 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6291 StorageImpl_FreeBigBlock(This
->parentStorage
,
6292 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
6293 if (last_run
->lastOffset
== last_run
->firstOffset
)
6294 This
->indexCacheLen
--;
6296 last_run
->lastOffset
--;
6300 * Reset the last accessed block cache.
6304 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
6306 This
->cachedBlocks
[i
].index
= 0xffffffff;
6307 This
->cachedBlocks
[i
].dirty
= 0;
6314 /******************************************************************************
6315 * BlockChainStream_Enlarge
6317 * Grows this chain in the big block depot.
6319 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
6320 ULARGE_INTEGER newSize
)
6322 ULONG blockIndex
, currentBlock
;
6324 ULONG oldNumBlocks
= 0;
6326 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
6329 * Empty chain. Create the head.
6331 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6333 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6334 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
6336 BLOCK_END_OF_CHAIN
);
6338 if (This
->headOfStreamPlaceHolder
!= 0)
6340 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6344 DirEntry chainEntry
;
6345 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
6347 StorageImpl_ReadDirEntry(
6348 This
->parentStorage
,
6349 This
->ownerDirEntry
,
6352 chainEntry
.startingBlock
= blockIndex
;
6354 StorageImpl_WriteDirEntry(
6355 This
->parentStorage
,
6356 This
->ownerDirEntry
,
6360 This
->tailIndex
= blockIndex
;
6361 This
->numBlocks
= 1;
6365 * Figure out how many blocks are needed to contain this stream
6367 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
6369 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
6373 * Go to the current end of chain
6375 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
6377 currentBlock
= blockIndex
;
6379 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6382 currentBlock
= blockIndex
;
6384 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
6389 This
->tailIndex
= currentBlock
;
6392 currentBlock
= This
->tailIndex
;
6393 oldNumBlocks
= This
->numBlocks
;
6396 * Add new blocks to the chain
6398 if (oldNumBlocks
< newNumBlocks
)
6400 while (oldNumBlocks
< newNumBlocks
)
6402 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
6404 StorageImpl_SetNextBlockInChain(
6405 This
->parentStorage
,
6409 StorageImpl_SetNextBlockInChain(
6410 This
->parentStorage
,
6412 BLOCK_END_OF_CHAIN
);
6414 currentBlock
= blockIndex
;
6418 This
->tailIndex
= blockIndex
;
6419 This
->numBlocks
= newNumBlocks
;
6422 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
6428 /******************************************************************************
6429 * BlockChainStream_SetSize
6431 * Sets the size of this stream. The big block depot will be updated.
6432 * The file will grow if we grow the chain.
6434 * TODO: Free the actual blocks in the file when we shrink the chain.
6435 * Currently, the blocks are still in the file. So the file size
6436 * doesn't shrink even if we shrink streams.
6438 BOOL
BlockChainStream_SetSize(
6439 BlockChainStream
* This
,
6440 ULARGE_INTEGER newSize
)
6442 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
6444 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6447 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6449 BlockChainStream_Shrink(This
, newSize
);
6453 BlockChainStream_Enlarge(This
, newSize
);
6459 /******************************************************************************
6460 * BlockChainStream_GetSize
6462 * Returns the size of this chain.
6463 * Will return the block count if this chain doesn't have a directory entry.
6465 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
6467 DirEntry chainEntry
;
6469 if(This
->headOfStreamPlaceHolder
== NULL
)
6472 * This chain has a directory entry so use the size value from there.
6474 StorageImpl_ReadDirEntry(
6475 This
->parentStorage
,
6476 This
->ownerDirEntry
,
6479 return chainEntry
.size
;
6484 * this chain is a chain that does not have a directory entry, figure out the
6485 * size by making the product number of used blocks times the
6488 ULARGE_INTEGER result
;
6489 result
.u
.HighPart
= 0;
6492 BlockChainStream_GetCount(This
) *
6493 This
->parentStorage
->bigBlockSize
;
6499 /******************************************************************************
6500 ** SmallBlockChainStream implementation
6503 SmallBlockChainStream
* SmallBlockChainStream_Construct(
6504 StorageImpl
* parentStorage
,
6505 ULONG
* headOfStreamPlaceHolder
,
6508 SmallBlockChainStream
* newStream
;
6510 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
6512 newStream
->parentStorage
= parentStorage
;
6513 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6514 newStream
->ownerDirEntry
= dirEntry
;
6519 void SmallBlockChainStream_Destroy(
6520 SmallBlockChainStream
* This
)
6522 HeapFree(GetProcessHeap(), 0, This
);
6525 /******************************************************************************
6526 * SmallBlockChainStream_GetHeadOfChain
6528 * Returns the head of this chain of small blocks.
6530 static ULONG
SmallBlockChainStream_GetHeadOfChain(
6531 SmallBlockChainStream
* This
)
6533 DirEntry chainEntry
;
6536 if (This
->headOfStreamPlaceHolder
!= NULL
)
6537 return *(This
->headOfStreamPlaceHolder
);
6539 if (This
->ownerDirEntry
)
6541 hr
= StorageImpl_ReadDirEntry(
6542 This
->parentStorage
,
6543 This
->ownerDirEntry
,
6548 return chainEntry
.startingBlock
;
6553 return BLOCK_END_OF_CHAIN
;
6556 /******************************************************************************
6557 * SmallBlockChainStream_GetNextBlockInChain
6559 * Returns the index of the next small block in this chain.
6562 * - BLOCK_END_OF_CHAIN: end of this chain
6563 * - BLOCK_UNUSED: small block 'blockIndex' is free
6565 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
6566 SmallBlockChainStream
* This
,
6568 ULONG
* nextBlockInChain
)
6570 ULARGE_INTEGER offsetOfBlockInDepot
;
6575 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
6577 offsetOfBlockInDepot
.u
.HighPart
= 0;
6578 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6581 * Read those bytes in the buffer from the small block file.
6583 res
= BlockChainStream_ReadAt(
6584 This
->parentStorage
->smallBlockDepotChain
,
6585 offsetOfBlockInDepot
,
6590 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
6591 res
= STG_E_READFAULT
;
6595 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
6602 /******************************************************************************
6603 * SmallBlockChainStream_SetNextBlockInChain
6605 * Writes the index of the next block of the specified block in the small
6607 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6608 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6610 static void SmallBlockChainStream_SetNextBlockInChain(
6611 SmallBlockChainStream
* This
,
6615 ULARGE_INTEGER offsetOfBlockInDepot
;
6619 offsetOfBlockInDepot
.u
.HighPart
= 0;
6620 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6622 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
6625 * Read those bytes in the buffer from the small block file.
6627 BlockChainStream_WriteAt(
6628 This
->parentStorage
->smallBlockDepotChain
,
6629 offsetOfBlockInDepot
,
6635 /******************************************************************************
6636 * SmallBlockChainStream_FreeBlock
6638 * Flag small block 'blockIndex' as free in the small block depot.
6640 static void SmallBlockChainStream_FreeBlock(
6641 SmallBlockChainStream
* This
,
6644 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
6647 /******************************************************************************
6648 * SmallBlockChainStream_GetNextFreeBlock
6650 * Returns the index of a free small block. The small block depot will be
6651 * enlarged if necessary. The small block chain will also be enlarged if
6654 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
6655 SmallBlockChainStream
* This
)
6657 ULARGE_INTEGER offsetOfBlockInDepot
;
6660 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
6661 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
6663 ULONG smallBlocksPerBigBlock
;
6665 ULONG blocksRequired
;
6666 ULARGE_INTEGER old_size
, size_required
;
6668 offsetOfBlockInDepot
.u
.HighPart
= 0;
6671 * Scan the small block depot for a free block
6673 while (nextBlockIndex
!= BLOCK_UNUSED
)
6675 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
6677 res
= BlockChainStream_ReadAt(
6678 This
->parentStorage
->smallBlockDepotChain
,
6679 offsetOfBlockInDepot
,
6685 * If we run out of space for the small block depot, enlarge it
6687 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
6689 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
6691 if (nextBlockIndex
!= BLOCK_UNUSED
)
6697 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
6699 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
6700 ULARGE_INTEGER newSize
, offset
;
6703 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
6704 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
6707 * Initialize all the small blocks to free
6709 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
6710 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
6711 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
6712 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
6714 StorageImpl_SaveFileHeader(This
->parentStorage
);
6718 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
6720 smallBlocksPerBigBlock
=
6721 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
6724 * Verify if we have to allocate big blocks to contain small blocks
6726 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
6728 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
6730 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
6732 if (size_required
.QuadPart
> old_size
.QuadPart
)
6734 BlockChainStream_SetSize(
6735 This
->parentStorage
->smallBlockRootChain
,
6738 StorageImpl_ReadDirEntry(
6739 This
->parentStorage
,
6740 This
->parentStorage
->base
.storageDirEntry
,
6743 rootEntry
.size
= size_required
;
6745 StorageImpl_WriteDirEntry(
6746 This
->parentStorage
,
6747 This
->parentStorage
->base
.storageDirEntry
,
6754 /******************************************************************************
6755 * SmallBlockChainStream_ReadAt
6757 * Reads a specified number of bytes from this chain at the specified offset.
6758 * bytesRead may be NULL.
6759 * Failure will be returned if the specified number of bytes has not been read.
6761 HRESULT
SmallBlockChainStream_ReadAt(
6762 SmallBlockChainStream
* This
,
6763 ULARGE_INTEGER offset
,
6769 ULARGE_INTEGER offsetInBigBlockFile
;
6770 ULONG blockNoInSequence
=
6771 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6773 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6774 ULONG bytesToReadInBuffer
;
6776 ULONG bytesReadFromBigBlockFile
;
6778 ULARGE_INTEGER stream_size
;
6781 * This should never happen on a small block file.
6783 assert(offset
.u
.HighPart
==0);
6787 stream_size
= SmallBlockChainStream_GetSize(This
);
6788 if (stream_size
.QuadPart
> offset
.QuadPart
)
6789 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
6794 * Find the first block in the stream that contains part of the buffer.
6796 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6798 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6800 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6803 blockNoInSequence
--;
6807 * Start reading the buffer.
6809 bufferWalker
= buffer
;
6811 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6814 * Calculate how many bytes we can copy from this small block.
6816 bytesToReadInBuffer
=
6817 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6820 * Calculate the offset of the small block in the small block file.
6822 offsetInBigBlockFile
.u
.HighPart
= 0;
6823 offsetInBigBlockFile
.u
.LowPart
=
6824 blockIndex
* This
->parentStorage
->smallBlockSize
;
6826 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6829 * Read those bytes in the buffer from the small block file.
6830 * The small block has already been identified so it shouldn't fail
6831 * unless the file is corrupt.
6833 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
6834 offsetInBigBlockFile
,
6835 bytesToReadInBuffer
,
6837 &bytesReadFromBigBlockFile
);
6842 if (!bytesReadFromBigBlockFile
)
6843 return STG_E_DOCFILECORRUPT
;
6846 * Step to the next big block.
6848 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
6850 return STG_E_DOCFILECORRUPT
;
6852 bufferWalker
+= bytesReadFromBigBlockFile
;
6853 size
-= bytesReadFromBigBlockFile
;
6854 *bytesRead
+= bytesReadFromBigBlockFile
;
6855 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6861 /******************************************************************************
6862 * SmallBlockChainStream_WriteAt
6864 * Writes the specified number of bytes to this chain at the specified offset.
6865 * Will fail if not all specified number of bytes have been written.
6867 HRESULT
SmallBlockChainStream_WriteAt(
6868 SmallBlockChainStream
* This
,
6869 ULARGE_INTEGER offset
,
6872 ULONG
* bytesWritten
)
6874 ULARGE_INTEGER offsetInBigBlockFile
;
6875 ULONG blockNoInSequence
=
6876 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6878 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6879 ULONG bytesToWriteInBuffer
;
6881 ULONG bytesWrittenToBigBlockFile
;
6882 const BYTE
* bufferWalker
;
6886 * This should never happen on a small block file.
6888 assert(offset
.u
.HighPart
==0);
6891 * Find the first block in the stream that contains part of the buffer.
6893 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6895 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6897 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6898 return STG_E_DOCFILECORRUPT
;
6899 blockNoInSequence
--;
6903 * Start writing the buffer.
6906 bufferWalker
= buffer
;
6907 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6910 * Calculate how many bytes we can copy to this small block.
6912 bytesToWriteInBuffer
=
6913 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6916 * Calculate the offset of the small block in the small block file.
6918 offsetInBigBlockFile
.u
.HighPart
= 0;
6919 offsetInBigBlockFile
.u
.LowPart
=
6920 blockIndex
* This
->parentStorage
->smallBlockSize
;
6922 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6925 * Write those bytes in the buffer to the small block file.
6927 res
= BlockChainStream_WriteAt(
6928 This
->parentStorage
->smallBlockRootChain
,
6929 offsetInBigBlockFile
,
6930 bytesToWriteInBuffer
,
6932 &bytesWrittenToBigBlockFile
);
6937 * Step to the next big block.
6939 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6942 bufferWalker
+= bytesWrittenToBigBlockFile
;
6943 size
-= bytesWrittenToBigBlockFile
;
6944 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6945 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6948 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6951 /******************************************************************************
6952 * SmallBlockChainStream_Shrink
6954 * Shrinks this chain in the small block depot.
6956 static BOOL
SmallBlockChainStream_Shrink(
6957 SmallBlockChainStream
* This
,
6958 ULARGE_INTEGER newSize
)
6960 ULONG blockIndex
, extraBlock
;
6964 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6966 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6969 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6972 * Go to the new end of chain
6974 while (count
< numBlocks
)
6976 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6983 * If the count is 0, we have a special case, the head of the chain was
6988 DirEntry chainEntry
;
6990 StorageImpl_ReadDirEntry(This
->parentStorage
,
6991 This
->ownerDirEntry
,
6994 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6996 StorageImpl_WriteDirEntry(This
->parentStorage
,
6997 This
->ownerDirEntry
,
7001 * We start freeing the chain at the head block.
7003 extraBlock
= blockIndex
;
7007 /* Get the next block before marking the new end */
7008 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7012 /* Mark the new end of chain */
7013 SmallBlockChainStream_SetNextBlockInChain(
7016 BLOCK_END_OF_CHAIN
);
7020 * Mark the extra blocks as free
7022 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7024 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7027 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7028 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7029 extraBlock
= blockIndex
;
7035 /******************************************************************************
7036 * SmallBlockChainStream_Enlarge
7038 * Grows this chain in the small block depot.
7040 static BOOL
SmallBlockChainStream_Enlarge(
7041 SmallBlockChainStream
* This
,
7042 ULARGE_INTEGER newSize
)
7044 ULONG blockIndex
, currentBlock
;
7046 ULONG oldNumBlocks
= 0;
7048 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7051 * Empty chain. Create the head.
7053 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7055 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7056 SmallBlockChainStream_SetNextBlockInChain(
7059 BLOCK_END_OF_CHAIN
);
7061 if (This
->headOfStreamPlaceHolder
!= NULL
)
7063 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7067 DirEntry chainEntry
;
7069 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7072 chainEntry
.startingBlock
= blockIndex
;
7074 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
7079 currentBlock
= blockIndex
;
7082 * Figure out how many blocks are needed to contain this stream
7084 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7086 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7090 * Go to the current end of chain
7092 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7095 currentBlock
= blockIndex
;
7096 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
7101 * Add new blocks to the chain
7103 while (oldNumBlocks
< newNumBlocks
)
7105 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
7106 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
7108 SmallBlockChainStream_SetNextBlockInChain(
7111 BLOCK_END_OF_CHAIN
);
7113 currentBlock
= blockIndex
;
7120 /******************************************************************************
7121 * SmallBlockChainStream_SetSize
7123 * Sets the size of this stream.
7124 * The file will grow if we grow the chain.
7126 * TODO: Free the actual blocks in the file when we shrink the chain.
7127 * Currently, the blocks are still in the file. So the file size
7128 * doesn't shrink even if we shrink streams.
7130 BOOL
SmallBlockChainStream_SetSize(
7131 SmallBlockChainStream
* This
,
7132 ULARGE_INTEGER newSize
)
7134 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
7136 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
7139 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
7141 SmallBlockChainStream_Shrink(This
, newSize
);
7145 SmallBlockChainStream_Enlarge(This
, newSize
);
7151 /******************************************************************************
7152 * SmallBlockChainStream_GetCount
7154 * Returns the number of small blocks that comprises this chain.
7155 * This is not the size of the stream as the last block may not be full!
7158 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
7163 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7165 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
7169 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
7170 blockIndex
, &blockIndex
)))
7177 /******************************************************************************
7178 * SmallBlockChainStream_GetSize
7180 * Returns the size of this chain.
7182 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
7184 DirEntry chainEntry
;
7186 if(This
->headOfStreamPlaceHolder
!= NULL
)
7188 ULARGE_INTEGER result
;
7189 result
.u
.HighPart
= 0;
7191 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
7192 This
->parentStorage
->smallBlockSize
;
7197 StorageImpl_ReadDirEntry(
7198 This
->parentStorage
,
7199 This
->ownerDirEntry
,
7202 return chainEntry
.size
;
7205 static HRESULT
create_storagefile(
7209 STGOPTIONS
* pStgOptions
,
7213 StorageBaseImpl
* newStorage
= 0;
7214 HANDLE hFile
= INVALID_HANDLE_VALUE
;
7215 HRESULT hr
= STG_E_INVALIDFLAG
;
7219 DWORD fileAttributes
;
7220 WCHAR tempFileName
[MAX_PATH
];
7223 return STG_E_INVALIDPOINTER
;
7225 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
7226 return STG_E_INVALIDPARAMETER
;
7228 /* if no share mode given then DENY_NONE is the default */
7229 if (STGM_SHARE_MODE(grfMode
) == 0)
7230 grfMode
|= STGM_SHARE_DENY_NONE
;
7232 if ( FAILED( validateSTGM(grfMode
) ))
7235 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7236 switch(STGM_ACCESS_MODE(grfMode
))
7239 case STGM_READWRITE
:
7245 /* in direct mode, can only use SHARE_EXCLUSIVE */
7246 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
7249 /* but in transacted mode, any share mode is valid */
7252 * Generate a unique name.
7256 WCHAR tempPath
[MAX_PATH
];
7257 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
7259 memset(tempPath
, 0, sizeof(tempPath
));
7260 memset(tempFileName
, 0, sizeof(tempFileName
));
7262 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
7265 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
7266 pwcsName
= tempFileName
;
7269 hr
= STG_E_INSUFFICIENTMEMORY
;
7273 creationMode
= TRUNCATE_EXISTING
;
7277 creationMode
= GetCreationModeFromSTGM(grfMode
);
7281 * Interpret the STGM value grfMode
7283 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7284 accessMode
= GetAccessModeFromSTGM(grfMode
);
7286 if (grfMode
& STGM_DELETEONRELEASE
)
7287 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
7289 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
7291 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
7295 FIXME("Storage share mode not implemented.\n");
7300 hFile
= CreateFileW(pwcsName
,
7308 if (hFile
== INVALID_HANDLE_VALUE
)
7310 if(GetLastError() == ERROR_FILE_EXISTS
)
7311 hr
= STG_E_FILEALREADYEXISTS
;
7318 * Allocate and initialize the new IStorage32object.
7320 hr
= Storage_Construct(
7327 pStgOptions
->ulSectorSize
,
7335 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
7337 IStorage_Release((IStorage
*)newStorage
);
7340 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
7345 /******************************************************************************
7346 * StgCreateDocfile [OLE32.@]
7347 * Creates a new compound file storage object
7350 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7351 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7352 * reserved [ ?] unused?, usually 0
7353 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7356 * S_OK if the file was successfully created
7357 * some STG_E_ value if error
7359 * if pwcsName is NULL, create file with new unique name
7360 * the function can returns
7361 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7364 HRESULT WINAPI
StgCreateDocfile(
7368 IStorage
**ppstgOpen
)
7370 STGOPTIONS stgoptions
= {1, 0, 512};
7372 TRACE("(%s, %x, %d, %p)\n",
7373 debugstr_w(pwcsName
), grfMode
,
7374 reserved
, ppstgOpen
);
7377 return STG_E_INVALIDPOINTER
;
7379 return STG_E_INVALIDPARAMETER
;
7381 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
7384 /******************************************************************************
7385 * StgCreateStorageEx [OLE32.@]
7387 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7389 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7390 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7392 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
7394 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7395 return STG_E_INVALIDPARAMETER
;
7398 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7400 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7401 return STG_E_INVALIDPARAMETER
;
7404 if (stgfmt
== STGFMT_FILE
)
7406 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7407 return STG_E_INVALIDPARAMETER
;
7410 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
7412 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
7416 ERR("Invalid stgfmt argument\n");
7417 return STG_E_INVALIDPARAMETER
;
7420 /******************************************************************************
7421 * StgCreatePropSetStg [OLE32.@]
7423 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
7424 IPropertySetStorage
**ppPropSetStg
)
7428 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
7430 hr
= STG_E_INVALIDPARAMETER
;
7432 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
7433 (void**)ppPropSetStg
);
7437 /******************************************************************************
7438 * StgOpenStorageEx [OLE32.@]
7440 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
7442 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
7443 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
7445 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
7447 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7448 return STG_E_INVALIDPARAMETER
;
7454 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7455 return STG_E_INVALIDPARAMETER
;
7457 case STGFMT_STORAGE
:
7460 case STGFMT_DOCFILE
:
7461 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
7463 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7464 return STG_E_INVALIDPARAMETER
;
7466 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7470 WARN("STGFMT_ANY assuming storage\n");
7474 return STG_E_INVALIDPARAMETER
;
7477 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
7481 /******************************************************************************
7482 * StgOpenStorage [OLE32.@]
7484 HRESULT WINAPI
StgOpenStorage(
7485 const OLECHAR
*pwcsName
,
7486 IStorage
*pstgPriority
,
7490 IStorage
**ppstgOpen
)
7492 StorageBaseImpl
* newStorage
= 0;
7498 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7499 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
7500 snbExclude
, reserved
, ppstgOpen
);
7504 hr
= STG_E_INVALIDNAME
;
7510 hr
= STG_E_INVALIDPOINTER
;
7516 hr
= STG_E_INVALIDPARAMETER
;
7520 if (grfMode
& STGM_PRIORITY
)
7522 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
7523 return STG_E_INVALIDFLAG
;
7524 if (grfMode
& STGM_DELETEONRELEASE
)
7525 return STG_E_INVALIDFUNCTION
;
7526 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
7527 return STG_E_INVALIDFLAG
;
7528 grfMode
&= ~0xf0; /* remove the existing sharing mode */
7529 grfMode
|= STGM_SHARE_DENY_NONE
;
7531 /* STGM_PRIORITY stops other IStorage objects on the same file from
7532 * committing until the STGM_PRIORITY IStorage is closed. it also
7533 * stops non-transacted mode StgOpenStorage calls with write access from
7534 * succeeding. obviously, both of these cannot be achieved through just
7535 * file share flags */
7536 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7540 * Validate the sharing mode
7542 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
7543 switch(STGM_SHARE_MODE(grfMode
))
7545 case STGM_SHARE_EXCLUSIVE
:
7546 case STGM_SHARE_DENY_WRITE
:
7549 hr
= STG_E_INVALIDFLAG
;
7553 if ( FAILED( validateSTGM(grfMode
) ) ||
7554 (grfMode
&STGM_CREATE
))
7556 hr
= STG_E_INVALIDFLAG
;
7560 /* shared reading requires transacted mode */
7561 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
7562 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
7563 !(grfMode
&STGM_TRANSACTED
) )
7565 hr
= STG_E_INVALIDFLAG
;
7570 * Interpret the STGM value grfMode
7572 shareMode
= GetShareModeFromSTGM(grfMode
);
7573 accessMode
= GetAccessModeFromSTGM(grfMode
);
7577 hFile
= CreateFileW( pwcsName
,
7582 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
7585 if (hFile
==INVALID_HANDLE_VALUE
)
7587 DWORD last_error
= GetLastError();
7593 case ERROR_FILE_NOT_FOUND
:
7594 hr
= STG_E_FILENOTFOUND
;
7597 case ERROR_PATH_NOT_FOUND
:
7598 hr
= STG_E_PATHNOTFOUND
;
7601 case ERROR_ACCESS_DENIED
:
7602 case ERROR_WRITE_PROTECT
:
7603 hr
= STG_E_ACCESSDENIED
;
7606 case ERROR_SHARING_VIOLATION
:
7607 hr
= STG_E_SHAREVIOLATION
;
7618 * Refuse to open the file if it's too small to be a structured storage file
7619 * FIXME: verify the file when reading instead of here
7621 if (GetFileSize(hFile
, NULL
) < 0x100)
7624 hr
= STG_E_FILEALREADYEXISTS
;
7629 * Allocate and initialize the new IStorage32object.
7631 hr
= Storage_Construct(
7644 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7646 if(hr
== STG_E_INVALIDHEADER
)
7647 hr
= STG_E_FILEALREADYEXISTS
;
7652 * Get an "out" pointer for the caller.
7654 *ppstgOpen
= (IStorage
*)newStorage
;
7657 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
7661 /******************************************************************************
7662 * StgCreateDocfileOnILockBytes [OLE32.@]
7664 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
7668 IStorage
** ppstgOpen
)
7670 StorageBaseImpl
* newStorage
= 0;
7673 if ((ppstgOpen
== 0) || (plkbyt
== 0))
7674 return STG_E_INVALIDPOINTER
;
7677 * Allocate and initialize the new IStorage object.
7679 hr
= Storage_Construct(
7695 * Get an "out" pointer for the caller.
7697 *ppstgOpen
= (IStorage
*)newStorage
;
7702 /******************************************************************************
7703 * StgOpenStorageOnILockBytes [OLE32.@]
7705 HRESULT WINAPI
StgOpenStorageOnILockBytes(
7707 IStorage
*pstgPriority
,
7711 IStorage
**ppstgOpen
)
7713 StorageBaseImpl
* newStorage
= 0;
7716 if ((plkbyt
== 0) || (ppstgOpen
== 0))
7717 return STG_E_INVALIDPOINTER
;
7719 if ( FAILED( validateSTGM(grfMode
) ))
7720 return STG_E_INVALIDFLAG
;
7725 * Allocate and initialize the new IStorage object.
7727 hr
= Storage_Construct(
7743 * Get an "out" pointer for the caller.
7745 *ppstgOpen
= (IStorage
*)newStorage
;
7750 /******************************************************************************
7751 * StgSetTimes [ole32.@]
7752 * StgSetTimes [OLE32.@]
7756 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
7757 FILETIME
const *patime
, FILETIME
const *pmtime
)
7759 IStorage
*stg
= NULL
;
7762 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
7764 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
7768 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
7769 IStorage_Release(stg
);
7775 /******************************************************************************
7776 * StgIsStorageILockBytes [OLE32.@]
7778 * Determines if the ILockBytes contains a storage object.
7780 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
7783 ULARGE_INTEGER offset
;
7785 offset
.u
.HighPart
= 0;
7786 offset
.u
.LowPart
= 0;
7788 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
7790 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
7796 /******************************************************************************
7797 * WriteClassStg [OLE32.@]
7799 * This method will store the specified CLSID in the specified storage object
7801 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
7806 return E_INVALIDARG
;
7809 return STG_E_INVALIDPOINTER
;
7811 hRes
= IStorage_SetClass(pStg
, rclsid
);
7816 /***********************************************************************
7817 * ReadClassStg (OLE32.@)
7819 * This method reads the CLSID previously written to a storage object with
7820 * the WriteClassStg.
7823 * pstg [I] IStorage pointer
7824 * pclsid [O] Pointer to where the CLSID is written
7828 * Failure: HRESULT code.
7830 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
7835 TRACE("(%p, %p)\n", pstg
, pclsid
);
7837 if(!pstg
|| !pclsid
)
7838 return E_INVALIDARG
;
7841 * read a STATSTG structure (contains the clsid) from the storage
7843 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
7846 *pclsid
=pstatstg
.clsid
;
7851 /***********************************************************************
7852 * OleLoadFromStream (OLE32.@)
7854 * This function loads an object from stream
7856 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
7860 LPPERSISTSTREAM xstm
;
7862 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
7864 res
=ReadClassStm(pStm
,&clsid
);
7867 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7870 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7872 IUnknown_Release((IUnknown
*)*ppvObj
);
7875 res
=IPersistStream_Load(xstm
,pStm
);
7876 IPersistStream_Release(xstm
);
7877 /* FIXME: all refcounts ok at this point? I think they should be:
7880 * xstm : 0 (released)
7885 /***********************************************************************
7886 * OleSaveToStream (OLE32.@)
7888 * This function saves an object with the IPersistStream interface on it
7889 * to the specified stream.
7891 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7897 TRACE("(%p,%p)\n",pPStm
,pStm
);
7899 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7901 if (SUCCEEDED(res
)){
7903 res
=WriteClassStm(pStm
,&clsid
);
7907 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7910 TRACE("Finished Save\n");
7914 /****************************************************************************
7915 * This method validate a STGM parameter that can contain the values below
7917 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7918 * The stgm values contained in 0xffff0000 are bitmasks.
7920 * STGM_DIRECT 0x00000000
7921 * STGM_TRANSACTED 0x00010000
7922 * STGM_SIMPLE 0x08000000
7924 * STGM_READ 0x00000000
7925 * STGM_WRITE 0x00000001
7926 * STGM_READWRITE 0x00000002
7928 * STGM_SHARE_DENY_NONE 0x00000040
7929 * STGM_SHARE_DENY_READ 0x00000030
7930 * STGM_SHARE_DENY_WRITE 0x00000020
7931 * STGM_SHARE_EXCLUSIVE 0x00000010
7933 * STGM_PRIORITY 0x00040000
7934 * STGM_DELETEONRELEASE 0x04000000
7936 * STGM_CREATE 0x00001000
7937 * STGM_CONVERT 0x00020000
7938 * STGM_FAILIFTHERE 0x00000000
7940 * STGM_NOSCRATCH 0x00100000
7941 * STGM_NOSNAPSHOT 0x00200000
7943 static HRESULT
validateSTGM(DWORD stgm
)
7945 DWORD access
= STGM_ACCESS_MODE(stgm
);
7946 DWORD share
= STGM_SHARE_MODE(stgm
);
7947 DWORD create
= STGM_CREATE_MODE(stgm
);
7949 if (stgm
&~STGM_KNOWN_FLAGS
)
7951 ERR("unknown flags %08x\n", stgm
);
7959 case STGM_READWRITE
:
7967 case STGM_SHARE_DENY_NONE
:
7968 case STGM_SHARE_DENY_READ
:
7969 case STGM_SHARE_DENY_WRITE
:
7970 case STGM_SHARE_EXCLUSIVE
:
7979 case STGM_FAILIFTHERE
:
7986 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7988 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7992 * STGM_CREATE | STGM_CONVERT
7993 * if both are false, STGM_FAILIFTHERE is set to TRUE
7995 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7999 * STGM_NOSCRATCH requires STGM_TRANSACTED
8001 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8005 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8006 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8008 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8009 (!(stgm
& STGM_TRANSACTED
) ||
8010 share
== STGM_SHARE_EXCLUSIVE
||
8011 share
== STGM_SHARE_DENY_WRITE
) )
8017 /****************************************************************************
8018 * GetShareModeFromSTGM
8020 * This method will return a share mode flag from a STGM value.
8021 * The STGM value is assumed valid.
8023 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8025 switch (STGM_SHARE_MODE(stgm
))
8027 case STGM_SHARE_DENY_NONE
:
8028 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8029 case STGM_SHARE_DENY_READ
:
8030 return FILE_SHARE_WRITE
;
8031 case STGM_SHARE_DENY_WRITE
:
8032 return FILE_SHARE_READ
;
8033 case STGM_SHARE_EXCLUSIVE
:
8036 ERR("Invalid share mode!\n");
8041 /****************************************************************************
8042 * GetAccessModeFromSTGM
8044 * This method will return an access mode flag from a STGM value.
8045 * The STGM value is assumed valid.
8047 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
8049 switch (STGM_ACCESS_MODE(stgm
))
8052 return GENERIC_READ
;
8054 case STGM_READWRITE
:
8055 return GENERIC_READ
| GENERIC_WRITE
;
8057 ERR("Invalid access mode!\n");
8062 /****************************************************************************
8063 * GetCreationModeFromSTGM
8065 * This method will return a creation mode flag from a STGM value.
8066 * The STGM value is assumed valid.
8068 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
8070 switch(STGM_CREATE_MODE(stgm
))
8073 return CREATE_ALWAYS
;
8075 FIXME("STGM_CONVERT not implemented!\n");
8077 case STGM_FAILIFTHERE
:
8080 ERR("Invalid create mode!\n");
8086 /*************************************************************************
8087 * OLECONVERT_LoadOLE10 [Internal]
8089 * Loads the OLE10 STREAM to memory
8092 * pOleStream [I] The OLESTREAM
8093 * pData [I] Data Structure for the OLESTREAM Data
8097 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8098 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8101 * This function is used by OleConvertOLESTREAMToIStorage only.
8103 * Memory allocated for pData must be freed by the caller
8105 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
8108 HRESULT hRes
= S_OK
;
8112 pData
->pData
= NULL
;
8113 pData
->pstrOleObjFileName
= NULL
;
8115 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
8118 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8119 if(dwSize
!= sizeof(pData
->dwOleID
))
8121 hRes
= CONVERT10_E_OLESTREAM_GET
;
8123 else if(pData
->dwOleID
!= OLESTREAM_ID
)
8125 hRes
= CONVERT10_E_OLESTREAM_FMT
;
8136 /* Get the TypeID... more info needed for this field */
8137 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8138 if(dwSize
!= sizeof(pData
->dwTypeID
))
8140 hRes
= CONVERT10_E_OLESTREAM_GET
;
8145 if(pData
->dwTypeID
!= 0)
8147 /* Get the length of the OleTypeName */
8148 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8149 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8151 hRes
= CONVERT10_E_OLESTREAM_GET
;
8156 if(pData
->dwOleTypeNameLength
> 0)
8158 /* Get the OleTypeName */
8159 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8160 if(dwSize
!= pData
->dwOleTypeNameLength
)
8162 hRes
= CONVERT10_E_OLESTREAM_GET
;
8168 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
8169 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
8171 hRes
= CONVERT10_E_OLESTREAM_GET
;
8175 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
8176 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
8177 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
8178 if(pData
->pstrOleObjFileName
)
8180 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
8181 if(dwSize
!= pData
->dwOleObjFileNameLength
)
8183 hRes
= CONVERT10_E_OLESTREAM_GET
;
8187 hRes
= CONVERT10_E_OLESTREAM_GET
;
8192 /* Get the Width of the Metafile */
8193 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8194 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8196 hRes
= CONVERT10_E_OLESTREAM_GET
;
8200 /* Get the Height of the Metafile */
8201 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8202 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8204 hRes
= CONVERT10_E_OLESTREAM_GET
;
8210 /* Get the Length of the Data */
8211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8212 if(dwSize
!= sizeof(pData
->dwDataLength
))
8214 hRes
= CONVERT10_E_OLESTREAM_GET
;
8218 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
8220 if(!bStrem1
) /* if it is a second OLE stream data */
8222 pData
->dwDataLength
-= 8;
8223 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
8224 if(dwSize
!= sizeof(pData
->strUnknown
))
8226 hRes
= CONVERT10_E_OLESTREAM_GET
;
8232 if(pData
->dwDataLength
> 0)
8234 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
8236 /* Get Data (ex. IStorage, Metafile, or BMP) */
8239 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
8240 if(dwSize
!= pData
->dwDataLength
)
8242 hRes
= CONVERT10_E_OLESTREAM_GET
;
8247 hRes
= CONVERT10_E_OLESTREAM_GET
;
8256 /*************************************************************************
8257 * OLECONVERT_SaveOLE10 [Internal]
8259 * Saves the OLE10 STREAM From memory
8262 * pData [I] Data Structure for the OLESTREAM Data
8263 * pOleStream [I] The OLESTREAM to save
8267 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8270 * This function is used by OleConvertIStorageToOLESTREAM only.
8273 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
8276 HRESULT hRes
= S_OK
;
8280 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
8281 if(dwSize
!= sizeof(pData
->dwOleID
))
8283 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8288 /* Set the TypeID */
8289 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
8290 if(dwSize
!= sizeof(pData
->dwTypeID
))
8292 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8296 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
8298 /* Set the Length of the OleTypeName */
8299 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
8300 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
8302 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8307 if(pData
->dwOleTypeNameLength
> 0)
8309 /* Set the OleTypeName */
8310 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
8311 if(dwSize
!= pData
->dwOleTypeNameLength
)
8313 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8320 /* Set the width of the Metafile */
8321 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
8322 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
8324 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8330 /* Set the height of the Metafile */
8331 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
8332 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
8334 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8340 /* Set the length of the Data */
8341 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
8342 if(dwSize
!= sizeof(pData
->dwDataLength
))
8344 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8350 if(pData
->dwDataLength
> 0)
8352 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8353 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
8354 if(dwSize
!= pData
->dwDataLength
)
8356 hRes
= CONVERT10_E_OLESTREAM_PUT
;
8364 /*************************************************************************
8365 * OLECONVERT_GetOLE20FromOLE10[Internal]
8367 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8368 * opens it, and copies the content to the dest IStorage for
8369 * OleConvertOLESTREAMToIStorage
8373 * pDestStorage [I] The IStorage to copy the data to
8374 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8375 * nBufferLength [I] The size of the buffer
8384 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
8388 IStorage
*pTempStorage
;
8389 DWORD dwNumOfBytesWritten
;
8390 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8391 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8393 /* Create a temp File */
8394 GetTempPathW(MAX_PATH
, wstrTempDir
);
8395 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8396 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
8398 if(hFile
!= INVALID_HANDLE_VALUE
)
8400 /* Write IStorage Data to File */
8401 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
8404 /* Open and copy temp storage to the Dest Storage */
8405 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
8408 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
8409 IStorage_Release(pTempStorage
);
8411 DeleteFileW(wstrTempFile
);
8416 /*************************************************************************
8417 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8419 * Saves the OLE10 STREAM From memory
8422 * pStorage [I] The Src IStorage to copy
8423 * pData [I] The Dest Memory to write to.
8426 * The size in bytes allocated for pData
8429 * Memory allocated for pData must be freed by the caller
8431 * Used by OleConvertIStorageToOLESTREAM only.
8434 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
8438 DWORD nDataLength
= 0;
8439 IStorage
*pTempStorage
;
8440 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
8441 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
8445 /* Create temp Storage */
8446 GetTempPathW(MAX_PATH
, wstrTempDir
);
8447 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
8448 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
8452 /* Copy Src Storage to the Temp Storage */
8453 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
8454 IStorage_Release(pTempStorage
);
8456 /* Open Temp Storage as a file and copy to memory */
8457 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8458 if(hFile
!= INVALID_HANDLE_VALUE
)
8460 nDataLength
= GetFileSize(hFile
, NULL
);
8461 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
8462 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
8465 DeleteFileW(wstrTempFile
);
8470 /*************************************************************************
8471 * OLECONVERT_CreateOleStream [Internal]
8473 * Creates the "\001OLE" stream in the IStorage if necessary.
8476 * pStorage [I] Dest storage to create the stream in
8482 * This function is used by OleConvertOLESTREAMToIStorage only.
8484 * This stream is still unknown, MS Word seems to have extra data
8485 * but since the data is stored in the OLESTREAM there should be
8486 * no need to recreate the stream. If the stream is manually
8487 * deleted it will create it with this default data.
8490 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
8494 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
8495 BYTE pOleStreamHeader
[] =
8497 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8499 0x00, 0x00, 0x00, 0x00
8502 /* Create stream if not present */
8503 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8504 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8508 /* Write default Data */
8509 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
8510 IStream_Release(pStream
);
8514 /* write a string to a stream, preceded by its length */
8515 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
8522 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
8523 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
8528 str
= CoTaskMemAlloc( len
);
8529 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
8530 r
= IStream_Write( stm
, str
, len
, NULL
);
8531 CoTaskMemFree( str
);
8535 /* read a string preceded by its length from a stream */
8536 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
8539 DWORD len
, count
= 0;
8543 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
8546 if( count
!= sizeof(len
) )
8547 return E_OUTOFMEMORY
;
8549 TRACE("%d bytes\n",len
);
8551 str
= CoTaskMemAlloc( len
);
8553 return E_OUTOFMEMORY
;
8555 r
= IStream_Read( stm
, str
, len
, &count
);
8560 CoTaskMemFree( str
);
8561 return E_OUTOFMEMORY
;
8564 TRACE("Read string %s\n",debugstr_an(str
,len
));
8566 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
8567 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
8569 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
8570 CoTaskMemFree( str
);
8578 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
8579 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
8583 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8585 static const BYTE unknown1
[12] =
8586 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8587 0xFF, 0xFF, 0xFF, 0xFF};
8588 static const BYTE unknown2
[16] =
8589 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8592 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
8593 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
8594 debugstr_w(szProgIDName
));
8596 /* Create a CompObj stream */
8597 r
= IStorage_CreateStream(pstg
, szwStreamName
,
8598 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
8602 /* Write CompObj Structure to stream */
8603 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
8605 if( SUCCEEDED( r
) )
8606 r
= WriteClassStm( pstm
, clsid
);
8608 if( SUCCEEDED( r
) )
8609 r
= STREAM_WriteString( pstm
, lpszUserType
);
8610 if( SUCCEEDED( r
) )
8611 r
= STREAM_WriteString( pstm
, szClipName
);
8612 if( SUCCEEDED( r
) )
8613 r
= STREAM_WriteString( pstm
, szProgIDName
);
8614 if( SUCCEEDED( r
) )
8615 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
8617 IStream_Release( pstm
);
8622 /***********************************************************************
8623 * WriteFmtUserTypeStg (OLE32.@)
8625 HRESULT WINAPI
WriteFmtUserTypeStg(
8626 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
8629 WCHAR szwClipName
[0x40];
8630 CLSID clsid
= CLSID_NULL
;
8631 LPWSTR wstrProgID
= NULL
;
8634 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
8636 /* get the clipboard format name */
8637 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
8640 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
8642 /* FIXME: There's room to save a CLSID and its ProgID, but
8643 the CLSID is not looked up in the registry and in all the
8644 tests I wrote it was CLSID_NULL. Where does it come from?
8647 /* get the real program ID. This may fail, but that's fine */
8648 ProgIDFromCLSID(&clsid
, &wstrProgID
);
8650 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
8652 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
8653 lpszUserType
, szwClipName
, wstrProgID
);
8655 CoTaskMemFree(wstrProgID
);
8661 /******************************************************************************
8662 * ReadFmtUserTypeStg [OLE32.@]
8664 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
8668 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
8669 unsigned char unknown1
[12];
8670 unsigned char unknown2
[16];
8672 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
8675 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
8677 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
8678 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
8681 WARN("Failed to open stream r = %08x\n", r
);
8685 /* read the various parts of the structure */
8686 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
8687 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
8689 r
= ReadClassStm( stm
, &clsid
);
8693 r
= STREAM_ReadString( stm
, &szCLSIDName
);
8697 r
= STREAM_ReadString( stm
, &szOleTypeName
);
8701 r
= STREAM_ReadString( stm
, &szProgIDName
);
8705 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
8706 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
8709 /* ok, success... now we just need to store what we found */
8711 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
8712 CoTaskMemFree( szOleTypeName
);
8714 if( lplpszUserType
)
8715 *lplpszUserType
= szCLSIDName
;
8716 CoTaskMemFree( szProgIDName
);
8719 IStream_Release( stm
);
8725 /*************************************************************************
8726 * OLECONVERT_CreateCompObjStream [Internal]
8728 * Creates a "\001CompObj" is the destination IStorage if necessary.
8731 * pStorage [I] The dest IStorage to create the CompObj Stream
8733 * strOleTypeName [I] The ProgID
8737 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8740 * This function is used by OleConvertOLESTREAMToIStorage only.
8742 * The stream data is stored in the OLESTREAM and there should be
8743 * no need to recreate the stream. If the stream is manually
8744 * deleted it will attempt to create it by querying the registry.
8748 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
8751 HRESULT hStorageRes
, hRes
= S_OK
;
8752 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
8753 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8754 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
8756 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8757 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
8759 /* Initialize the CompObj structure */
8760 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
8761 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
8762 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
8765 /* Create a CompObj stream if it doesn't exist */
8766 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8767 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8768 if(hStorageRes
== S_OK
)
8770 /* copy the OleTypeName to the compobj struct */
8771 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
8772 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
8774 /* copy the OleTypeName to the compobj struct */
8775 /* Note: in the test made, these were Identical */
8776 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
8777 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
8780 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
8781 bufferW
, OLESTREAM_MAX_STR_LEN
);
8782 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
8788 /* Get the CLSID Default Name from the Registry */
8789 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
8790 if(hErr
== ERROR_SUCCESS
)
8792 char strTemp
[OLESTREAM_MAX_STR_LEN
];
8793 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
8794 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
8795 if(hErr
== ERROR_SUCCESS
)
8797 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
8803 /* Write CompObj Structure to stream */
8804 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
8806 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
8808 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
8809 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
8811 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
8813 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
8814 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
8816 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
8818 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
8819 if(IStorageCompObj
.dwProgIDNameLength
> 0)
8821 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
8823 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
8824 IStream_Release(pStream
);
8830 /*************************************************************************
8831 * OLECONVERT_CreateOlePresStream[Internal]
8833 * Creates the "\002OlePres000" Stream with the Metafile data
8836 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8837 * dwExtentX [I] Width of the Metafile
8838 * dwExtentY [I] Height of the Metafile
8839 * pData [I] Metafile data
8840 * dwDataLength [I] Size of the Metafile data
8844 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8847 * This function is used by OleConvertOLESTREAMToIStorage only.
8850 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
8854 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8855 BYTE pOlePresStreamHeader
[] =
8857 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8858 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8859 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8860 0x00, 0x00, 0x00, 0x00
8863 BYTE pOlePresStreamHeaderEmpty
[] =
8865 0x00, 0x00, 0x00, 0x00,
8866 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8867 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8868 0x00, 0x00, 0x00, 0x00
8871 /* Create the OlePres000 Stream */
8872 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8873 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8878 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8880 memset(&OlePres
, 0, sizeof(OlePres
));
8881 /* Do we have any metafile data to save */
8882 if(dwDataLength
> 0)
8884 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8885 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8889 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8890 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8892 /* Set width and height of the metafile */
8893 OlePres
.dwExtentX
= dwExtentX
;
8894 OlePres
.dwExtentY
= -dwExtentY
;
8896 /* Set Data and Length */
8897 if(dwDataLength
> sizeof(METAFILEPICT16
))
8899 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8900 OlePres
.pData
= &(pData
[8]);
8902 /* Save OlePres000 Data to Stream */
8903 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8904 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8905 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8906 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8907 if(OlePres
.dwSize
> 0)
8909 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8911 IStream_Release(pStream
);
8915 /*************************************************************************
8916 * OLECONVERT_CreateOle10NativeStream [Internal]
8918 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8921 * pStorage [I] Dest storage to create the stream in
8922 * pData [I] Ole10 Native Data (ex. bmp)
8923 * dwDataLength [I] Size of the Ole10 Native Data
8929 * This function is used by OleConvertOLESTREAMToIStorage only.
8931 * Might need to verify the data and return appropriate error message
8934 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8938 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8940 /* Create the Ole10Native Stream */
8941 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8942 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8946 /* Write info to stream */
8947 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8948 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8949 IStream_Release(pStream
);
8954 /*************************************************************************
8955 * OLECONVERT_GetOLE10ProgID [Internal]
8957 * Finds the ProgID (or OleTypeID) from the IStorage
8960 * pStorage [I] The Src IStorage to get the ProgID
8961 * strProgID [I] the ProgID string to get
8962 * dwSize [I] the size of the string
8966 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8969 * This function is used by OleConvertIStorageToOLESTREAM only.
8973 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8977 LARGE_INTEGER iSeekPos
;
8978 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8979 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8981 /* Open the CompObj Stream */
8982 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8983 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8987 /*Get the OleType from the CompObj Stream */
8988 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8989 iSeekPos
.u
.HighPart
= 0;
8991 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8992 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8993 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8994 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8995 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8996 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8997 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8999 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9002 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9004 IStream_Release(pStream
);
9009 LPOLESTR wstrProgID
;
9011 /* Get the OleType from the registry */
9012 REFCLSID clsid
= &(stat
.clsid
);
9013 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
9014 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
9017 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
9024 /*************************************************************************
9025 * OLECONVERT_GetOle10PresData [Internal]
9027 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9030 * pStorage [I] Src IStroage
9031 * pOleStream [I] Dest OleStream Mem Struct
9037 * This function is used by OleConvertIStorageToOLESTREAM only.
9039 * Memory allocated for pData must be freed by the caller
9043 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9048 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9050 /* Initialize Default data for OLESTREAM */
9051 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9052 pOleStreamData
[0].dwTypeID
= 2;
9053 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9054 pOleStreamData
[1].dwTypeID
= 0;
9055 pOleStreamData
[0].dwMetaFileWidth
= 0;
9056 pOleStreamData
[0].dwMetaFileHeight
= 0;
9057 pOleStreamData
[0].pData
= NULL
;
9058 pOleStreamData
[1].pData
= NULL
;
9060 /* Open Ole10Native Stream */
9061 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9062 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9066 /* Read Size and Data */
9067 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
9068 if(pOleStreamData
->dwDataLength
> 0)
9070 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
9071 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
9073 IStream_Release(pStream
);
9079 /*************************************************************************
9080 * OLECONVERT_GetOle20PresData[Internal]
9082 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9085 * pStorage [I] Src IStroage
9086 * pOleStreamData [I] Dest OleStream Mem Struct
9092 * This function is used by OleConvertIStorageToOLESTREAM only.
9094 * Memory allocated for pData must be freed by the caller
9096 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
9100 OLECONVERT_ISTORAGE_OLEPRES olePress
;
9101 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9103 /* Initialize Default data for OLESTREAM */
9104 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
9105 pOleStreamData
[0].dwTypeID
= 2;
9106 pOleStreamData
[0].dwMetaFileWidth
= 0;
9107 pOleStreamData
[0].dwMetaFileHeight
= 0;
9108 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
9109 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
9110 pOleStreamData
[1].dwTypeID
= 0;
9111 pOleStreamData
[1].dwOleTypeNameLength
= 0;
9112 pOleStreamData
[1].strOleTypeName
[0] = 0;
9113 pOleStreamData
[1].dwMetaFileWidth
= 0;
9114 pOleStreamData
[1].dwMetaFileHeight
= 0;
9115 pOleStreamData
[1].pData
= NULL
;
9116 pOleStreamData
[1].dwDataLength
= 0;
9119 /* Open OlePress000 stream */
9120 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9121 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9124 LARGE_INTEGER iSeekPos
;
9125 METAFILEPICT16 MetaFilePict
;
9126 static const char strMetafilePictName
[] = "METAFILEPICT";
9128 /* Set the TypeID for a Metafile */
9129 pOleStreamData
[1].dwTypeID
= 5;
9131 /* Set the OleTypeName to Metafile */
9132 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
9133 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
9135 iSeekPos
.u
.HighPart
= 0;
9136 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
9138 /* Get Presentation Data */
9139 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9140 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
9141 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
9142 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
9144 /*Set width and Height */
9145 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
9146 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
9147 if(olePress
.dwSize
> 0)
9150 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
9152 /* Set MetaFilePict struct */
9153 MetaFilePict
.mm
= 8;
9154 MetaFilePict
.xExt
= olePress
.dwExtentX
;
9155 MetaFilePict
.yExt
= olePress
.dwExtentY
;
9156 MetaFilePict
.hMF
= 0;
9158 /* Get Metafile Data */
9159 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
9160 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
9161 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
9163 IStream_Release(pStream
);
9167 /*************************************************************************
9168 * OleConvertOLESTREAMToIStorage [OLE32.@]
9173 * DVTARGETDEVICE parameter is not handled
9174 * Still unsure of some mem fields for OLE 10 Stream
9175 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9176 * and "\001OLE" streams
9179 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
9180 LPOLESTREAM pOleStream
,
9182 const DVTARGETDEVICE
* ptd
)
9186 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9188 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
9190 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9194 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9197 if(pstg
== NULL
|| pOleStream
== NULL
)
9199 hRes
= E_INVALIDARG
;
9204 /* Load the OLESTREAM to Memory */
9205 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
9210 /* Load the OLESTREAM to Memory (part 2)*/
9211 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
9217 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
9219 /* Do we have the IStorage Data in the OLESTREAM */
9220 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
9222 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9223 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
9227 /* It must be an original OLE 1.0 source */
9228 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9233 /* It must be an original OLE 1.0 source */
9234 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
9237 /* Create CompObj Stream if necessary */
9238 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
9241 /*Create the Ole Stream if necessary */
9242 OLECONVERT_CreateOleStream(pstg
);
9247 /* Free allocated memory */
9248 for(i
=0; i
< 2; i
++)
9250 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9251 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
9252 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
9257 /*************************************************************************
9258 * OleConvertIStorageToOLESTREAM [OLE32.@]
9265 * Still unsure of some mem fields for OLE 10 Stream
9266 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9267 * and "\001OLE" streams.
9270 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
9272 LPOLESTREAM pOleStream
)
9275 HRESULT hRes
= S_OK
;
9277 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
9278 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9280 TRACE("%p %p\n", pstg
, pOleStream
);
9282 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
9284 if(pstg
== NULL
|| pOleStream
== NULL
)
9286 hRes
= E_INVALIDARG
;
9290 /* Get the ProgID */
9291 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
9292 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
9296 /* Was it originally Ole10 */
9297 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9300 IStream_Release(pStream
);
9301 /* Get Presentation Data for Ole10Native */
9302 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
9306 /* Get Presentation Data (OLE20) */
9307 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
9310 /* Save OLESTREAM */
9311 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
9314 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
9319 /* Free allocated memory */
9320 for(i
=0; i
< 2; i
++)
9322 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
9328 /***********************************************************************
9329 * GetConvertStg (OLE32.@)
9331 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
9332 FIXME("unimplemented stub!\n");
9336 /******************************************************************************
9337 * StgIsStorageFile [OLE32.@]
9338 * Verify if the file contains a storage object
9344 * S_OK if file has magic bytes as a storage object
9345 * S_FALSE if file is not storage
9348 StgIsStorageFile(LPCOLESTR fn
)
9354 TRACE("%s\n", debugstr_w(fn
));
9355 hf
= CreateFileW(fn
, GENERIC_READ
,
9356 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
9357 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9359 if (hf
== INVALID_HANDLE_VALUE
)
9360 return STG_E_FILENOTFOUND
;
9362 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
9364 WARN(" unable to read file\n");
9371 if (bytes_read
!= 8) {
9372 TRACE(" too short\n");
9376 if (!memcmp(magic
,STORAGE_magic
,8)) {
9381 TRACE(" -> Invalid header.\n");
9385 /***********************************************************************
9386 * WriteClassStm (OLE32.@)
9388 * Writes a CLSID to a stream.
9391 * pStm [I] Stream to write to.
9392 * rclsid [I] CLSID to write.
9396 * Failure: HRESULT code.
9398 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
9400 TRACE("(%p,%p)\n",pStm
,rclsid
);
9402 if (!pStm
|| !rclsid
)
9403 return E_INVALIDARG
;
9405 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
9408 /***********************************************************************
9409 * ReadClassStm (OLE32.@)
9411 * Reads a CLSID from a stream.
9414 * pStm [I] Stream to read from.
9415 * rclsid [O] CLSID to read.
9419 * Failure: HRESULT code.
9421 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
9426 TRACE("(%p,%p)\n",pStm
,pclsid
);
9428 if (!pStm
|| !pclsid
)
9429 return E_INVALIDARG
;
9431 /* clear the output args */
9432 *pclsid
= CLSID_NULL
;
9434 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
9439 if (nbByte
!= sizeof(CLSID
))
9440 return STG_E_READFAULT
;