2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry
;
86 StorageBaseImpl
*parentStorage
;
88 typedef struct StorageInternalImpl StorageInternalImpl
;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
92 DWORD openFlags
, DirRef storageDirEntry
);
93 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
94 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
95 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
96 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
98 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
99 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
101 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
102 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
103 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
104 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
107 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
108 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
109 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
111 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
112 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
113 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
114 ULONG blockIndex
, ULONG offset
, DWORD value
);
115 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
116 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
118 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
119 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base
;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl
*snapshot
;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl
*transactedParent
;
137 } TransactedSnapshotImpl
;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength
;
149 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
150 CHAR
*pstrOleObjFileName
;
151 DWORD dwOleObjFileNameLength
;
152 DWORD dwMetaFileWidth
;
153 DWORD dwMetaFileHeight
;
154 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA
;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength
;
166 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
167 DWORD dwOleTypeNameLength
;
168 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
169 DWORD dwProgIDNameLength
;
170 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
172 }OLECONVERT_ISTORAGE_COMPOBJ
;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES
;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT
deleteStorageContents(
192 StorageBaseImpl
*parentStorage
,
193 DirRef indexToDelete
,
194 DirEntry entryDataToDelete
);
196 static HRESULT
deleteStreamContents(
197 StorageBaseImpl
*parentStorage
,
198 DirRef indexToDelete
,
199 DirEntry entryDataToDelete
);
201 static HRESULT
removeFromTree(
202 StorageBaseImpl
*This
,
203 DirRef parentStorageIndex
,
204 DirRef deletedIndex
);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT
insertIntoTree(
211 StorageBaseImpl
*This
,
212 DirRef parentStorageIndex
,
213 DirRef newEntryIndex
);
215 static LONG
entryNameCmp(
216 const OLECHAR
*name1
,
217 const OLECHAR
*name2
);
219 static DirRef
findElement(
220 StorageBaseImpl
*storage
,
225 static HRESULT
findTreeParent(
226 StorageBaseImpl
*storage
,
228 const OLECHAR
*childName
,
229 DirEntry
*parentData
,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT
validateSTGM(DWORD stgmValue
);
238 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
239 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
240 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref
; /* Reference count */
258 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
259 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
261 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
265 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
268 /************************************************************************
272 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
274 return (index
+1) * This
->bigBlockSize
;
277 /************************************************************************
278 ** Storage32BaseImpl implementation
280 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
281 ULARGE_INTEGER offset
,
286 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
289 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
290 ULARGE_INTEGER offset
,
295 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
298 /************************************************************************
299 * Storage32BaseImpl_QueryInterface (IUnknown)
301 * This method implements the common QueryInterface for all IStorage32
302 * implementations contained in this file.
304 * See Windows documentation for more details on IUnknown methods.
306 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
311 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
313 if ( (This
==0) || (ppvObject
==0) )
318 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
319 IsEqualGUID(&IID_IStorage
, riid
))
323 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
325 *ppvObject
= &This
->pssVtbl
;
329 return E_NOINTERFACE
;
331 IStorage_AddRef(iface
);
336 /************************************************************************
337 * Storage32BaseImpl_AddRef (IUnknown)
339 * This method implements the common AddRef for all IStorage32
340 * implementations contained in this file.
342 * See Windows documentation for more details on IUnknown methods.
344 static ULONG WINAPI
StorageBaseImpl_AddRef(
347 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
348 ULONG ref
= InterlockedIncrement(&This
->ref
);
350 TRACE("(%p) AddRef to %d\n", This
, ref
);
355 /************************************************************************
356 * Storage32BaseImpl_Release (IUnknown)
358 * This method implements the common Release for all IStorage32
359 * implementations contained in this file.
361 * See Windows documentation for more details on IUnknown methods.
363 static ULONG WINAPI
StorageBaseImpl_Release(
366 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
368 ULONG ref
= InterlockedDecrement(&This
->ref
);
370 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
375 * Since we are using a system of base-classes, we want to call the
376 * destructor of the appropriate derived class. To do this, we are
377 * using virtual functions to implement the destructor.
379 StorageBaseImpl_Destroy(This
);
385 /************************************************************************
386 * Storage32BaseImpl_OpenStream (IStorage)
388 * This method will open the specified stream object from the current storage.
390 * See Windows documentation for more details on IStorage methods.
392 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
394 const OLECHAR
* pwcsName
, /* [string][in] */
395 void* reserved1
, /* [unique][in] */
396 DWORD grfMode
, /* [in] */
397 DWORD reserved2
, /* [in] */
398 IStream
** ppstm
) /* [out] */
400 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
401 StgStreamImpl
* newStream
;
402 DirEntry currentEntry
;
403 DirRef streamEntryRef
;
404 HRESULT res
= STG_E_UNKNOWN
;
406 TRACE("(%p, %s, %p, %x, %d, %p)\n",
407 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
409 if ( (pwcsName
==NULL
) || (ppstm
==0) )
417 if ( FAILED( validateSTGM(grfMode
) ) ||
418 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
420 res
= STG_E_INVALIDFLAG
;
427 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
429 res
= STG_E_INVALIDFUNCTION
;
435 res
= STG_E_REVERTED
;
440 * Check that we're compatible with the parent's storage mode, but
441 * only if we are not in transacted mode
443 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
444 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
446 res
= STG_E_INVALIDFLAG
;
452 * Search for the element with the given name
454 streamEntryRef
= findElement(
456 This
->storageDirEntry
,
461 * If it was found, construct the stream object and return a pointer to it.
463 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
464 (currentEntry
.stgType
==STGTY_STREAM
) )
466 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
468 /* A single stream cannot be opened a second time. */
469 res
= STG_E_ACCESSDENIED
;
473 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
477 newStream
->grfMode
= grfMode
;
478 *ppstm
= (IStream
*)newStream
;
480 IStream_AddRef(*ppstm
);
490 res
= STG_E_FILENOTFOUND
;
494 TRACE("<-- IStream %p\n", *ppstm
);
495 TRACE("<-- %08x\n", res
);
499 /************************************************************************
500 * Storage32BaseImpl_OpenStorage (IStorage)
502 * This method will open a new storage object from the current storage.
504 * See Windows documentation for more details on IStorage methods.
506 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
508 const OLECHAR
* pwcsName
, /* [string][unique][in] */
509 IStorage
* pstgPriority
, /* [unique][in] */
510 DWORD grfMode
, /* [in] */
511 SNB snbExclude
, /* [unique][in] */
512 DWORD reserved
, /* [in] */
513 IStorage
** ppstg
) /* [out] */
515 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
516 StorageInternalImpl
* newStorage
;
517 StorageBaseImpl
* newTransactedStorage
;
518 DirEntry currentEntry
;
519 DirRef storageEntryRef
;
520 HRESULT res
= STG_E_UNKNOWN
;
522 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
523 iface
, debugstr_w(pwcsName
), pstgPriority
,
524 grfMode
, snbExclude
, reserved
, ppstg
);
526 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
532 if (This
->openFlags
& STGM_SIMPLE
)
534 res
= STG_E_INVALIDFUNCTION
;
539 if (snbExclude
!= NULL
)
541 res
= STG_E_INVALIDPARAMETER
;
545 if ( FAILED( validateSTGM(grfMode
) ))
547 res
= STG_E_INVALIDFLAG
;
554 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
555 (grfMode
& STGM_DELETEONRELEASE
) ||
556 (grfMode
& STGM_PRIORITY
) )
558 res
= STG_E_INVALIDFUNCTION
;
563 return STG_E_REVERTED
;
566 * Check that we're compatible with the parent's storage mode,
567 * but only if we are not transacted
569 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
570 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
572 res
= STG_E_ACCESSDENIED
;
579 storageEntryRef
= findElement(
581 This
->storageDirEntry
,
585 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
586 (currentEntry
.stgType
==STGTY_STORAGE
) )
588 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
590 /* A single storage cannot be opened a second time. */
591 res
= STG_E_ACCESSDENIED
;
595 newStorage
= StorageInternalImpl_Construct(
602 if (grfMode
& STGM_TRANSACTED
)
604 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
608 HeapFree(GetProcessHeap(), 0, newStorage
);
612 *ppstg
= (IStorage
*)newTransactedStorage
;
616 *ppstg
= (IStorage
*)newStorage
;
619 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
625 res
= STG_E_INSUFFICIENTMEMORY
;
629 res
= STG_E_FILENOTFOUND
;
632 TRACE("<-- %08x\n", res
);
636 /************************************************************************
637 * Storage32BaseImpl_EnumElements (IStorage)
639 * This method will create an enumerator object that can be used to
640 * retrieve information about all the elements in the storage object.
642 * See Windows documentation for more details on IStorage methods.
644 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
646 DWORD reserved1
, /* [in] */
647 void* reserved2
, /* [size_is][unique][in] */
648 DWORD reserved3
, /* [in] */
649 IEnumSTATSTG
** ppenum
) /* [out] */
651 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
652 IEnumSTATSTGImpl
* newEnum
;
654 TRACE("(%p, %d, %p, %d, %p)\n",
655 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
657 if ( (This
==0) || (ppenum
==0))
661 return STG_E_REVERTED
;
663 newEnum
= IEnumSTATSTGImpl_Construct(
665 This
->storageDirEntry
);
669 *ppenum
= (IEnumSTATSTG
*)newEnum
;
671 IEnumSTATSTG_AddRef(*ppenum
);
676 return E_OUTOFMEMORY
;
679 /************************************************************************
680 * Storage32BaseImpl_Stat (IStorage)
682 * This method will retrieve information about this storage object.
684 * See Windows documentation for more details on IStorage methods.
686 static HRESULT WINAPI
StorageBaseImpl_Stat(
688 STATSTG
* pstatstg
, /* [out] */
689 DWORD grfStatFlag
) /* [in] */
691 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
692 DirEntry currentEntry
;
693 HRESULT res
= STG_E_UNKNOWN
;
695 TRACE("(%p, %p, %x)\n",
696 iface
, pstatstg
, grfStatFlag
);
698 if ( (This
==0) || (pstatstg
==0))
706 res
= STG_E_REVERTED
;
710 res
= StorageBaseImpl_ReadDirEntry(
712 This
->storageDirEntry
,
717 StorageUtl_CopyDirEntryToSTATSTG(
723 pstatstg
->grfMode
= This
->openFlags
;
724 pstatstg
->grfStateBits
= This
->stateBits
;
730 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
);
732 TRACE("<-- %08x\n", res
);
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
745 const OLECHAR
* pwcsOldName
, /* [in] */
746 const OLECHAR
* pwcsNewName
) /* [in] */
748 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
749 DirEntry currentEntry
;
750 DirRef currentEntryRef
;
752 TRACE("(%p, %s, %s)\n",
753 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
756 return STG_E_REVERTED
;
758 currentEntryRef
= findElement(This
,
759 This
->storageDirEntry
,
763 if (currentEntryRef
!= DIRENTRY_NULL
)
766 * There is already an element with the new name
768 return STG_E_FILEALREADYEXISTS
;
772 * Search for the old element name
774 currentEntryRef
= findElement(This
,
775 This
->storageDirEntry
,
779 if (currentEntryRef
!= DIRENTRY_NULL
)
781 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
782 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
784 WARN("Element is already open; cannot rename.\n");
785 return STG_E_ACCESSDENIED
;
788 /* Remove the element from its current position in the tree */
789 removeFromTree(This
, This
->storageDirEntry
,
792 /* Change the name of the element */
793 strcpyW(currentEntry
.name
, pwcsNewName
);
795 /* Delete any sibling links */
796 currentEntry
.leftChild
= DIRENTRY_NULL
;
797 currentEntry
.rightChild
= DIRENTRY_NULL
;
799 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
802 /* Insert the element in a new position in the tree */
803 insertIntoTree(This
, This
->storageDirEntry
,
809 * There is no element with the old name
811 return STG_E_FILENOTFOUND
;
817 /************************************************************************
818 * Storage32BaseImpl_CreateStream (IStorage)
820 * This method will create a stream object within this storage
822 * See Windows documentation for more details on IStorage methods.
824 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
826 const OLECHAR
* pwcsName
, /* [string][in] */
827 DWORD grfMode
, /* [in] */
828 DWORD reserved1
, /* [in] */
829 DWORD reserved2
, /* [in] */
830 IStream
** ppstm
) /* [out] */
832 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
833 StgStreamImpl
* newStream
;
834 DirEntry currentEntry
, newStreamEntry
;
835 DirRef currentEntryRef
, newStreamEntryRef
;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface
, debugstr_w(pwcsName
), grfMode
,
840 reserved1
, reserved2
, ppstm
);
843 return STG_E_INVALIDPOINTER
;
846 return STG_E_INVALIDNAME
;
848 if (reserved1
|| reserved2
)
849 return STG_E_INVALIDPARAMETER
;
851 if ( FAILED( validateSTGM(grfMode
) ))
852 return STG_E_INVALIDFLAG
;
854 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
855 return STG_E_INVALIDFLAG
;
858 return STG_E_REVERTED
;
863 if ((grfMode
& STGM_DELETEONRELEASE
) ||
864 (grfMode
& STGM_TRANSACTED
))
865 return STG_E_INVALIDFUNCTION
;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
874 return STG_E_ACCESSDENIED
;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
878 return STG_E_ACCESSDENIED
;
881 if(This
->openFlags
& STGM_SIMPLE
)
882 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
886 currentEntryRef
= findElement(This
,
887 This
->storageDirEntry
,
891 if (currentEntryRef
!= DIRENTRY_NULL
)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
898 IStorage_DestroyElement(iface
, pwcsName
);
901 return STG_E_FILEALREADYEXISTS
;
905 * memset the empty entry
907 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
909 newStreamEntry
.sizeOfNameString
=
910 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
912 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
913 return STG_E_INVALIDNAME
;
915 strcpyW(newStreamEntry
.name
, pwcsName
);
917 newStreamEntry
.stgType
= STGTY_STREAM
;
918 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
919 newStreamEntry
.size
.u
.LowPart
= 0;
920 newStreamEntry
.size
.u
.HighPart
= 0;
922 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
923 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
924 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
926 /* call CoFileTime to get the current time
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
941 * Insert the new entry in the parent storage's tree.
945 This
->storageDirEntry
,
949 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
954 * Open the stream to return it.
956 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
960 *ppstm
= (IStream
*)newStream
;
962 IStream_AddRef(*ppstm
);
966 return STG_E_INSUFFICIENTMEMORY
;
972 /************************************************************************
973 * Storage32BaseImpl_SetClass (IStorage)
975 * This method will write the specified CLSID in the directory entry of this
978 * See Windows documentation for more details on IStorage methods.
980 static HRESULT WINAPI
StorageBaseImpl_SetClass(
982 REFCLSID clsid
) /* [in] */
984 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
986 DirEntry currentEntry
;
988 TRACE("(%p, %p)\n", iface
, clsid
);
991 return STG_E_REVERTED
;
993 hRes
= StorageBaseImpl_ReadDirEntry(This
,
994 This
->storageDirEntry
,
998 currentEntry
.clsid
= *clsid
;
1000 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1001 This
->storageDirEntry
,
1008 /************************************************************************
1009 ** Storage32Impl implementation
1012 /************************************************************************
1013 * Storage32BaseImpl_CreateStorage (IStorage)
1015 * This method will create the storage object within the provided storage.
1017 * See Windows documentation for more details on IStorage methods.
1019 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1021 const OLECHAR
*pwcsName
, /* [string][in] */
1022 DWORD grfMode
, /* [in] */
1023 DWORD reserved1
, /* [in] */
1024 DWORD reserved2
, /* [in] */
1025 IStorage
**ppstg
) /* [out] */
1027 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1029 DirEntry currentEntry
;
1031 DirRef currentEntryRef
;
1035 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1036 iface
, debugstr_w(pwcsName
), grfMode
,
1037 reserved1
, reserved2
, ppstg
);
1040 return STG_E_INVALIDPOINTER
;
1042 if (This
->openFlags
& STGM_SIMPLE
)
1044 return STG_E_INVALIDFUNCTION
;
1048 return STG_E_INVALIDNAME
;
1052 if ( FAILED( validateSTGM(grfMode
) ) ||
1053 (grfMode
& STGM_DELETEONRELEASE
) )
1055 WARN("bad grfMode: 0x%x\n", grfMode
);
1056 return STG_E_INVALIDFLAG
;
1060 return STG_E_REVERTED
;
1063 * Check that we're compatible with the parent's storage mode
1065 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1066 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1068 WARN("access denied\n");
1069 return STG_E_ACCESSDENIED
;
1072 currentEntryRef
= findElement(This
,
1073 This
->storageDirEntry
,
1077 if (currentEntryRef
!= DIRENTRY_NULL
)
1080 * An element with this name already exists
1082 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1083 ((This
->openFlags
& STGM_TRANSACTED
) ||
1084 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1086 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1092 WARN("file already exists\n");
1093 return STG_E_FILEALREADYEXISTS
;
1096 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1097 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1099 WARN("read-only storage\n");
1100 return STG_E_ACCESSDENIED
;
1103 memset(&newEntry
, 0, sizeof(DirEntry
));
1105 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1107 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1109 FIXME("name too long\n");
1110 return STG_E_INVALIDNAME
;
1113 strcpyW(newEntry
.name
, pwcsName
);
1115 newEntry
.stgType
= STGTY_STORAGE
;
1116 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1117 newEntry
.size
.u
.LowPart
= 0;
1118 newEntry
.size
.u
.HighPart
= 0;
1120 newEntry
.leftChild
= DIRENTRY_NULL
;
1121 newEntry
.rightChild
= DIRENTRY_NULL
;
1122 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1124 /* call CoFileTime to get the current time
1129 /* newEntry.clsid */
1132 * Create a new directory entry for the storage
1134 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1139 * Insert the new directory entry into the parent storage's tree
1141 hr
= insertIntoTree(
1143 This
->storageDirEntry
,
1147 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1152 * Open it to get a pointer to return.
1154 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1156 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1166 /***************************************************************************
1170 * Reserve a directory entry in the file and initialize it.
1172 static HRESULT
StorageImpl_CreateDirEntry(
1173 StorageBaseImpl
*base
,
1174 const DirEntry
*newData
,
1177 StorageImpl
*storage
= (StorageImpl
*)base
;
1178 ULONG currentEntryIndex
= 0;
1179 ULONG newEntryIndex
= DIRENTRY_NULL
;
1181 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1182 WORD sizeOfNameString
;
1186 hr
= StorageImpl_ReadRawDirEntry(storage
,
1192 StorageUtl_ReadWord(
1194 OFFSET_PS_NAMELENGTH
,
1197 if (sizeOfNameString
== 0)
1200 * The entry exists and is available, we found it.
1202 newEntryIndex
= currentEntryIndex
;
1208 * We exhausted the directory entries, we will create more space below
1210 newEntryIndex
= currentEntryIndex
;
1212 currentEntryIndex
++;
1214 } while (newEntryIndex
== DIRENTRY_NULL
);
1217 * grow the directory stream
1221 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1222 ULARGE_INTEGER newSize
;
1224 ULONG lastEntry
= 0;
1225 ULONG blockCount
= 0;
1228 * obtain the new count of blocks in the directory stream
1230 blockCount
= BlockChainStream_GetCount(
1231 storage
->rootBlockChain
)+1;
1234 * initialize the size used by the directory stream
1236 newSize
.u
.HighPart
= 0;
1237 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1240 * add a block to the directory stream
1242 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1245 * memset the empty entry in order to initialize the unused newly
1248 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1253 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1256 entryIndex
= newEntryIndex
+ 1;
1257 entryIndex
< lastEntry
;
1260 StorageImpl_WriteRawDirEntry(
1267 UpdateRawDirEntry(currentData
, newData
);
1269 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1272 *index
= newEntryIndex
;
1277 /***************************************************************************
1281 * Mark a directory entry in the file as free.
1283 static HRESULT
StorageImpl_DestroyDirEntry(
1284 StorageBaseImpl
*base
,
1288 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1289 StorageImpl
*storage
= (StorageImpl
*)base
;
1291 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1293 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1299 /***************************************************************************
1303 * Destroy an entry, its attached data, and all entries reachable from it.
1305 static HRESULT
DestroyReachableEntries(
1306 StorageBaseImpl
*base
,
1311 ULARGE_INTEGER zero
;
1315 if (index
!= DIRENTRY_NULL
)
1317 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1320 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1323 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1326 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1329 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1332 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1339 /****************************************************************************
1343 * Case insensitive comparison of DirEntry.name by first considering
1346 * Returns <0 when name1 < name2
1347 * >0 when name1 > name2
1348 * 0 when name1 == name2
1350 static LONG
entryNameCmp(
1351 const OLECHAR
*name1
,
1352 const OLECHAR
*name2
)
1354 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1356 while (diff
== 0 && *name1
!= 0)
1359 * We compare the string themselves only when they are of the same length
1361 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1367 /****************************************************************************
1371 * Add a directory entry to a storage
1373 static HRESULT
insertIntoTree(
1374 StorageBaseImpl
*This
,
1375 DirRef parentStorageIndex
,
1376 DirRef newEntryIndex
)
1378 DirEntry currentEntry
;
1382 * Read the inserted entry
1384 StorageBaseImpl_ReadDirEntry(This
,
1389 * Read the storage entry
1391 StorageBaseImpl_ReadDirEntry(This
,
1395 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1398 * The root storage contains some element, therefore, start the research
1399 * for the appropriate location.
1402 DirRef current
, next
, previous
, currentEntryId
;
1405 * Keep a reference to the root of the storage's element tree
1407 currentEntryId
= currentEntry
.dirRootEntry
;
1412 StorageBaseImpl_ReadDirEntry(This
,
1413 currentEntry
.dirRootEntry
,
1416 previous
= currentEntry
.leftChild
;
1417 next
= currentEntry
.rightChild
;
1418 current
= currentEntryId
;
1422 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1426 if (previous
!= DIRENTRY_NULL
)
1428 StorageBaseImpl_ReadDirEntry(This
,
1435 currentEntry
.leftChild
= newEntryIndex
;
1436 StorageBaseImpl_WriteDirEntry(This
,
1444 if (next
!= DIRENTRY_NULL
)
1446 StorageBaseImpl_ReadDirEntry(This
,
1453 currentEntry
.rightChild
= newEntryIndex
;
1454 StorageBaseImpl_WriteDirEntry(This
,
1463 * Trying to insert an item with the same name in the
1464 * subtree structure.
1466 return STG_E_FILEALREADYEXISTS
;
1469 previous
= currentEntry
.leftChild
;
1470 next
= currentEntry
.rightChild
;
1476 * The storage is empty, make the new entry the root of its element tree
1478 currentEntry
.dirRootEntry
= newEntryIndex
;
1479 StorageBaseImpl_WriteDirEntry(This
,
1487 /****************************************************************************
1491 * Find and read the element of a storage with the given name.
1493 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1494 const OLECHAR
*name
, DirEntry
*data
)
1496 DirRef currentEntry
;
1498 /* Read the storage entry to find the root of the tree. */
1499 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1501 currentEntry
= data
->dirRootEntry
;
1503 while (currentEntry
!= DIRENTRY_NULL
)
1507 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1509 cmp
= entryNameCmp(name
, data
->name
);
1516 currentEntry
= data
->leftChild
;
1519 currentEntry
= data
->rightChild
;
1522 return currentEntry
;
1525 /****************************************************************************
1529 * Find and read the binary tree parent of the element with the given name.
1531 * If there is no such element, find a place where it could be inserted and
1532 * return STG_E_FILENOTFOUND.
1534 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1535 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1541 /* Read the storage entry to find the root of the tree. */
1542 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1544 *parentEntry
= storageEntry
;
1545 *relation
= DIRENTRY_RELATION_DIR
;
1547 childEntry
= parentData
->dirRootEntry
;
1549 while (childEntry
!= DIRENTRY_NULL
)
1553 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1555 cmp
= entryNameCmp(childName
, childData
.name
);
1563 *parentData
= childData
;
1564 *parentEntry
= childEntry
;
1565 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1567 childEntry
= parentData
->leftChild
;
1572 *parentData
= childData
;
1573 *parentEntry
= childEntry
;
1574 *relation
= DIRENTRY_RELATION_NEXT
;
1576 childEntry
= parentData
->rightChild
;
1580 if (childEntry
== DIRENTRY_NULL
)
1581 return STG_E_FILENOTFOUND
;
1587 /*************************************************************************
1590 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1592 DWORD ciidExclude
, /* [in] */
1593 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1594 SNB snbExclude
, /* [unique][in] */
1595 IStorage
* pstgDest
) /* [unique][in] */
1597 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1599 IEnumSTATSTG
*elements
= 0;
1600 STATSTG curElement
, strStat
;
1602 IStorage
*pstgTmp
, *pstgChild
;
1603 IStream
*pstrTmp
, *pstrChild
;
1606 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1609 TRACE("(%p, %d, %p, %p, %p)\n",
1610 iface
, ciidExclude
, rgiidExclude
,
1611 snbExclude
, pstgDest
);
1613 if ( pstgDest
== 0 )
1614 return STG_E_INVALIDPOINTER
;
1617 * Enumerate the elements
1619 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1627 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1628 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1630 for(i
= 0; i
< ciidExclude
; ++i
)
1632 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1633 skip_storage
= TRUE
;
1634 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1637 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1643 * Obtain the next element
1645 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1647 if ( hr
== S_FALSE
)
1649 hr
= S_OK
; /* done, every element has been copied */
1655 WCHAR
**snb
= snbExclude
;
1657 while ( *snb
!= NULL
&& !skip
)
1659 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1668 if (curElement
.type
== STGTY_STORAGE
)
1674 * open child source storage
1676 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1677 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1678 NULL
, 0, &pstgChild
);
1684 * create a new storage in destination storage
1686 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1687 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1691 * if it already exist, don't create a new one use this one
1693 if (hr
== STG_E_FILEALREADYEXISTS
)
1695 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1696 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1697 NULL
, 0, &pstgTmp
);
1703 * do the copy recursively
1705 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1708 IStorage_Release( pstgTmp
);
1711 IStorage_Release( pstgChild
);
1713 else if (curElement
.type
== STGTY_STREAM
)
1719 * create a new stream in destination storage. If the stream already
1720 * exist, it will be deleted and a new one will be created.
1722 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1723 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1730 * open child stream storage. This operation must succeed even if the
1731 * stream is already open, so we use internal functions to do it.
1733 srcEntryRef
= findElement( This
, This
->storageDirEntry
, curElement
.pwcsName
,
1737 ERR("source stream not found\n");
1738 hr
= STG_E_DOCFILECORRUPT
;
1743 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntryRef
);
1745 IStream_AddRef(pstrChild
);
1753 * Get the size of the source stream
1755 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1758 * Set the size of the destination stream.
1760 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1765 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1768 IStream_Release( pstrChild
);
1771 IStream_Release( pstrTmp
);
1775 WARN("unknown element type: %d\n", curElement
.type
);
1779 CoTaskMemFree(curElement
.pwcsName
);
1780 } while (hr
== S_OK
);
1785 IEnumSTATSTG_Release(elements
);
1790 /*************************************************************************
1791 * MoveElementTo (IStorage)
1793 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1795 const OLECHAR
*pwcsName
, /* [string][in] */
1796 IStorage
*pstgDest
, /* [unique][in] */
1797 const OLECHAR
*pwcsNewName
,/* [string][in] */
1798 DWORD grfFlags
) /* [in] */
1800 FIXME("(%p %s %p %s %u): stub\n", iface
,
1801 debugstr_w(pwcsName
), pstgDest
,
1802 debugstr_w(pwcsNewName
), grfFlags
);
1806 /*************************************************************************
1809 * Ensures that any changes made to a storage object open in transacted mode
1810 * are reflected in the parent storage
1813 * Wine doesn't implement transacted mode, which seems to be a basic
1814 * optimization, so we can ignore this stub for now.
1816 static HRESULT WINAPI
StorageImpl_Commit(
1818 DWORD grfCommitFlags
)/* [in] */
1820 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1824 /*************************************************************************
1827 * Discard all changes that have been made since the last commit operation
1829 static HRESULT WINAPI
StorageImpl_Revert(
1832 TRACE("(%p)\n", iface
);
1836 /*************************************************************************
1837 * DestroyElement (IStorage)
1839 * Strategy: This implementation is built this way for simplicity not for speed.
1840 * I always delete the topmost element of the enumeration and adjust
1841 * the deleted element pointer all the time. This takes longer to
1842 * do but allow to reinvoke DestroyElement whenever we encounter a
1843 * storage object. The optimisation resides in the usage of another
1844 * enumeration strategy that would give all the leaves of a storage
1845 * first. (postfix order)
1847 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1849 const OLECHAR
*pwcsName
)/* [string][in] */
1851 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1854 DirEntry entryToDelete
;
1855 DirRef entryToDeleteRef
;
1858 iface
, debugstr_w(pwcsName
));
1861 return STG_E_INVALIDPOINTER
;
1864 return STG_E_REVERTED
;
1866 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1867 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1868 return STG_E_ACCESSDENIED
;
1870 entryToDeleteRef
= findElement(
1872 This
->storageDirEntry
,
1876 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1878 return STG_E_FILENOTFOUND
;
1881 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1883 hr
= deleteStorageContents(
1888 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1890 hr
= deleteStreamContents(
1900 * Remove the entry from its parent storage
1902 hr
= removeFromTree(
1904 This
->storageDirEntry
,
1908 * Invalidate the entry
1911 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1917 /******************************************************************************
1918 * Internal stream list handlers
1921 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1923 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1924 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1927 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1929 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1930 list_remove(&(strm
->StrmListEntry
));
1933 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1935 StgStreamImpl
*strm
;
1937 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1939 if (strm
->dirEntry
== streamEntry
)
1948 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1950 StorageInternalImpl
*childstg
;
1952 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1954 if (childstg
->base
.storageDirEntry
== storageEntry
)
1963 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1965 struct list
*cur
, *cur2
;
1966 StgStreamImpl
*strm
=NULL
;
1967 StorageInternalImpl
*childstg
=NULL
;
1969 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1970 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1971 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1972 strm
->parentStorage
= NULL
;
1976 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1977 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1978 StorageBaseImpl_Invalidate( &childstg
->base
);
1981 if (stg
->transactedChild
)
1983 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1985 stg
->transactedChild
= NULL
;
1990 /*********************************************************************
1994 * Delete the contents of a storage entry.
1997 static HRESULT
deleteStorageContents(
1998 StorageBaseImpl
*parentStorage
,
1999 DirRef indexToDelete
,
2000 DirEntry entryDataToDelete
)
2002 IEnumSTATSTG
*elements
= 0;
2003 IStorage
*childStorage
= 0;
2004 STATSTG currentElement
;
2006 HRESULT destroyHr
= S_OK
;
2007 StorageInternalImpl
*stg
, *stg2
;
2009 /* Invalidate any open storage objects. */
2010 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2012 if (stg
->base
.storageDirEntry
== indexToDelete
)
2014 StorageBaseImpl_Invalidate(&stg
->base
);
2019 * Open the storage and enumerate it
2021 hr
= StorageBaseImpl_OpenStorage(
2022 (IStorage
*)parentStorage
,
2023 entryDataToDelete
.name
,
2025 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2036 * Enumerate the elements
2038 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2043 * Obtain the next element
2045 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2048 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2050 CoTaskMemFree(currentElement
.pwcsName
);
2054 * We need to Reset the enumeration every time because we delete elements
2055 * and the enumeration could be invalid
2057 IEnumSTATSTG_Reset(elements
);
2059 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2061 IStorage_Release(childStorage
);
2062 IEnumSTATSTG_Release(elements
);
2067 /*********************************************************************
2071 * Perform the deletion of a stream's data
2074 static HRESULT
deleteStreamContents(
2075 StorageBaseImpl
*parentStorage
,
2076 DirRef indexToDelete
,
2077 DirEntry entryDataToDelete
)
2081 ULARGE_INTEGER size
;
2082 StgStreamImpl
*strm
, *strm2
;
2084 /* Invalidate any open stream objects. */
2085 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2087 if (strm
->dirEntry
== indexToDelete
)
2089 TRACE("Stream deleted %p\n", strm
);
2090 strm
->parentStorage
= NULL
;
2091 list_remove(&strm
->StrmListEntry
);
2095 size
.u
.HighPart
= 0;
2098 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2099 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2109 hr
= IStream_SetSize(pis
, size
);
2117 * Release the stream object.
2119 IStream_Release(pis
);
2124 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2128 case DIRENTRY_RELATION_PREVIOUS
:
2129 entry
->leftChild
= new_target
;
2131 case DIRENTRY_RELATION_NEXT
:
2132 entry
->rightChild
= new_target
;
2134 case DIRENTRY_RELATION_DIR
:
2135 entry
->dirRootEntry
= new_target
;
2142 /*************************************************************************
2146 * This method removes a directory entry from its parent storage tree without
2147 * freeing any resources attached to it.
2149 static HRESULT
removeFromTree(
2150 StorageBaseImpl
*This
,
2151 DirRef parentStorageIndex
,
2152 DirRef deletedIndex
)
2155 DirEntry entryToDelete
;
2156 DirEntry parentEntry
;
2157 DirRef parentEntryRef
;
2158 ULONG typeOfRelation
;
2160 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2166 * Find the element that links to the one we want to delete.
2168 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2169 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2174 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2177 * Replace the deleted entry with its left child
2179 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2181 hr
= StorageBaseImpl_WriteDirEntry(
2190 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2193 * We need to reinsert the right child somewhere. We already know it and
2194 * its children are greater than everything in the left tree, so we
2195 * insert it at the rightmost point in the left tree.
2197 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2198 DirEntry newRightChildParentEntry
;
2202 hr
= StorageBaseImpl_ReadDirEntry(
2204 newRightChildParent
,
2205 &newRightChildParentEntry
);
2211 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2212 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2213 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2215 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2217 hr
= StorageBaseImpl_WriteDirEntry(
2219 newRightChildParent
,
2220 &newRightChildParentEntry
);
2230 * Replace the deleted entry with its right child
2232 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2234 hr
= StorageBaseImpl_WriteDirEntry(
2248 /******************************************************************************
2249 * SetElementTimes (IStorage)
2251 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2253 const OLECHAR
*pwcsName
,/* [string][in] */
2254 const FILETIME
*pctime
, /* [in] */
2255 const FILETIME
*patime
, /* [in] */
2256 const FILETIME
*pmtime
) /* [in] */
2258 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2262 /******************************************************************************
2263 * SetStateBits (IStorage)
2265 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2267 DWORD grfStateBits
,/* [in] */
2268 DWORD grfMask
) /* [in] */
2270 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2273 return STG_E_REVERTED
;
2275 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2279 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2280 DirRef index
, const DirEntry
*data
)
2282 StorageImpl
*This
= (StorageImpl
*)base
;
2283 return StorageImpl_WriteDirEntry(This
, index
, data
);
2286 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2287 DirRef index
, DirEntry
*data
)
2289 StorageImpl
*This
= (StorageImpl
*)base
;
2290 return StorageImpl_ReadDirEntry(This
, index
, data
);
2293 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2297 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2299 if (!This
->blockChainCache
[i
])
2301 return &This
->blockChainCache
[i
];
2305 i
= This
->blockChainToEvict
;
2307 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2308 This
->blockChainCache
[i
] = NULL
;
2310 This
->blockChainToEvict
++;
2311 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2312 This
->blockChainToEvict
= 0;
2314 return &This
->blockChainCache
[i
];
2317 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2320 int i
, free_index
=-1;
2322 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2324 if (!This
->blockChainCache
[i
])
2326 if (free_index
== -1) free_index
= i
;
2328 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2330 return &This
->blockChainCache
[i
];
2334 if (free_index
== -1)
2336 free_index
= This
->blockChainToEvict
;
2338 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2339 This
->blockChainCache
[free_index
] = NULL
;
2341 This
->blockChainToEvict
++;
2342 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2343 This
->blockChainToEvict
= 0;
2346 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2347 return &This
->blockChainCache
[free_index
];
2350 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2351 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2353 StorageImpl
*This
= (StorageImpl
*)base
;
2358 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2359 if (FAILED(hr
)) return hr
;
2361 if (data
.size
.QuadPart
== 0)
2367 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2369 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2376 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2378 SmallBlockChainStream
*stream
;
2380 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2381 if (!stream
) return E_OUTOFMEMORY
;
2383 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2385 SmallBlockChainStream_Destroy(stream
);
2391 BlockChainStream
*stream
= NULL
;
2393 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2394 if (!stream
) return E_OUTOFMEMORY
;
2396 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2402 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2403 ULARGE_INTEGER newsize
)
2405 StorageImpl
*This
= (StorageImpl
*)base
;
2408 SmallBlockChainStream
*smallblock
=NULL
;
2409 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2411 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2412 if (FAILED(hr
)) return hr
;
2414 /* In simple mode keep the stream size above the small block limit */
2415 if (This
->base
.openFlags
& STGM_SIMPLE
)
2416 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2418 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2421 /* Create a block chain object of the appropriate type */
2422 if (data
.size
.QuadPart
== 0)
2424 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2426 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2427 if (!smallblock
) return E_OUTOFMEMORY
;
2431 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2432 bigblock
= *pbigblock
;
2433 if (!bigblock
) return E_OUTOFMEMORY
;
2436 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2438 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2439 if (!smallblock
) return E_OUTOFMEMORY
;
2443 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2444 bigblock
= *pbigblock
;
2445 if (!bigblock
) return E_OUTOFMEMORY
;
2448 /* Change the block chain type if necessary. */
2449 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2451 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2454 SmallBlockChainStream_Destroy(smallblock
);
2458 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2459 *pbigblock
= bigblock
;
2461 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2463 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2468 /* Set the size of the block chain. */
2471 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2472 SmallBlockChainStream_Destroy(smallblock
);
2476 BlockChainStream_SetSize(bigblock
, newsize
);
2479 /* Set the size in the directory entry. */
2480 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2483 data
.size
= newsize
;
2485 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2490 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2491 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2493 StorageImpl
*This
= (StorageImpl
*)base
;
2496 ULARGE_INTEGER newSize
;
2498 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2499 if (FAILED(hr
)) return hr
;
2501 /* Grow the stream if necessary */
2502 newSize
.QuadPart
= 0;
2503 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2505 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2507 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2511 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2512 if (FAILED(hr
)) return hr
;
2515 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2517 SmallBlockChainStream
*stream
;
2519 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2520 if (!stream
) return E_OUTOFMEMORY
;
2522 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2524 SmallBlockChainStream_Destroy(stream
);
2530 BlockChainStream
*stream
;
2532 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2533 if (!stream
) return E_OUTOFMEMORY
;
2535 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2542 * Virtual function table for the IStorage32Impl class.
2544 static const IStorageVtbl Storage32Impl_Vtbl
=
2546 StorageBaseImpl_QueryInterface
,
2547 StorageBaseImpl_AddRef
,
2548 StorageBaseImpl_Release
,
2549 StorageBaseImpl_CreateStream
,
2550 StorageBaseImpl_OpenStream
,
2551 StorageBaseImpl_CreateStorage
,
2552 StorageBaseImpl_OpenStorage
,
2553 StorageBaseImpl_CopyTo
,
2554 StorageBaseImpl_MoveElementTo
,
2557 StorageBaseImpl_EnumElements
,
2558 StorageBaseImpl_DestroyElement
,
2559 StorageBaseImpl_RenameElement
,
2560 StorageBaseImpl_SetElementTimes
,
2561 StorageBaseImpl_SetClass
,
2562 StorageBaseImpl_SetStateBits
,
2563 StorageBaseImpl_Stat
2566 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2568 StorageImpl_Destroy
,
2569 StorageImpl_Invalidate
,
2570 StorageImpl_CreateDirEntry
,
2571 StorageImpl_BaseWriteDirEntry
,
2572 StorageImpl_BaseReadDirEntry
,
2573 StorageImpl_DestroyDirEntry
,
2574 StorageImpl_StreamReadAt
,
2575 StorageImpl_StreamWriteAt
,
2576 StorageImpl_StreamSetSize
2579 static HRESULT
StorageImpl_Construct(
2586 StorageImpl
** result
)
2590 DirEntry currentEntry
;
2591 DirRef currentEntryRef
;
2592 WCHAR fullpath
[MAX_PATH
];
2594 if ( FAILED( validateSTGM(openFlags
) ))
2595 return STG_E_INVALIDFLAG
;
2597 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2599 return E_OUTOFMEMORY
;
2601 memset(This
, 0, sizeof(StorageImpl
));
2603 list_init(&This
->base
.strmHead
);
2605 list_init(&This
->base
.storageHead
);
2607 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2608 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2609 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2610 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2612 This
->base
.create
= create
;
2614 This
->base
.reverted
= 0;
2616 This
->hFile
= hFile
;
2619 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2621 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2623 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2624 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2625 if (!This
->pwcsName
)
2627 hr
= STG_E_INSUFFICIENTMEMORY
;
2630 strcpyW(This
->pwcsName
, fullpath
);
2631 This
->base
.filename
= This
->pwcsName
;
2635 * Initialize the big block cache.
2637 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2638 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2639 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2644 if (This
->bigBlockFile
== 0)
2652 ULARGE_INTEGER size
;
2653 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2656 * Initialize all header variables:
2657 * - The big block depot consists of one block and it is at block 0
2658 * - The directory table starts at block 1
2659 * - There is no small block depot
2661 memset( This
->bigBlockDepotStart
,
2663 sizeof(This
->bigBlockDepotStart
));
2665 This
->bigBlockDepotCount
= 1;
2666 This
->bigBlockDepotStart
[0] = 0;
2667 This
->rootStartBlock
= 1;
2668 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2669 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2670 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2671 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2672 This
->extBigBlockDepotCount
= 0;
2674 StorageImpl_SaveFileHeader(This
);
2677 * Add one block for the big block depot and one block for the directory table
2679 size
.u
.HighPart
= 0;
2680 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2681 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2684 * Initialize the big block depot
2686 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2687 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2688 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2689 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2694 * Load the header for the file.
2696 hr
= StorageImpl_LoadFileHeader(This
);
2705 * There is no block depot cached yet.
2707 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2710 * Start searching for free blocks with block 0.
2712 This
->prevFreeBlock
= 0;
2715 * Create the block chain abstractions.
2717 if(!(This
->rootBlockChain
=
2718 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2720 hr
= STG_E_READFAULT
;
2724 if(!(This
->smallBlockDepotChain
=
2725 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2728 hr
= STG_E_READFAULT
;
2733 * Write the root storage entry (memory only)
2739 * Initialize the directory table
2741 memset(&rootEntry
, 0, sizeof(rootEntry
));
2742 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2743 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2744 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2745 rootEntry
.stgType
= STGTY_ROOT
;
2746 rootEntry
.leftChild
= DIRENTRY_NULL
;
2747 rootEntry
.rightChild
= DIRENTRY_NULL
;
2748 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2749 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2750 rootEntry
.size
.u
.HighPart
= 0;
2751 rootEntry
.size
.u
.LowPart
= 0;
2753 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2757 * Find the ID of the root storage.
2759 currentEntryRef
= 0;
2763 hr
= StorageImpl_ReadDirEntry(
2770 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2771 (currentEntry
.stgType
== STGTY_ROOT
) )
2773 This
->base
.storageDirEntry
= currentEntryRef
;
2779 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2783 hr
= STG_E_READFAULT
;
2788 * Create the block chain abstraction for the small block root chain.
2790 if(!(This
->smallBlockRootChain
=
2791 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2793 hr
= STG_E_READFAULT
;
2799 IStorage_Release((IStorage
*)This
);
2808 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2810 StorageImpl
*This
= (StorageImpl
*) iface
;
2812 StorageBaseImpl_DeleteAll(&This
->base
);
2814 This
->base
.reverted
= 1;
2817 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2819 StorageImpl
*This
= (StorageImpl
*) iface
;
2821 TRACE("(%p)\n", This
);
2823 StorageImpl_Invalidate(iface
);
2825 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2827 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2828 BlockChainStream_Destroy(This
->rootBlockChain
);
2829 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2831 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2832 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2834 if (This
->bigBlockFile
)
2835 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2836 HeapFree(GetProcessHeap(), 0, This
);
2839 /******************************************************************************
2840 * Storage32Impl_GetNextFreeBigBlock
2842 * Returns the index of the next free big block.
2843 * If the big block depot is filled, this method will enlarge it.
2846 static ULONG
StorageImpl_GetNextFreeBigBlock(
2849 ULONG depotBlockIndexPos
;
2850 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2852 ULONG depotBlockOffset
;
2853 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2854 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2856 ULONG freeBlock
= BLOCK_UNUSED
;
2857 ULARGE_INTEGER neededSize
;
2859 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2860 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2863 * Scan the entire big block depot until we find a block marked free
2865 while (nextBlockIndex
!= BLOCK_UNUSED
)
2867 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2869 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2872 * Grow the primary depot.
2874 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2876 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2879 * Add a block depot.
2881 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2882 This
->bigBlockDepotCount
++;
2883 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2886 * Flag it as a block depot.
2888 StorageImpl_SetNextBlockInChain(This
,
2892 /* Save new header information.
2894 StorageImpl_SaveFileHeader(This
);
2899 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2901 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2904 * Grow the extended depot.
2906 ULONG extIndex
= BLOCK_UNUSED
;
2907 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2908 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2910 if (extBlockOffset
== 0)
2912 /* We need an extended block.
2914 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2915 This
->extBigBlockDepotCount
++;
2916 depotBlockIndexPos
= extIndex
+ 1;
2919 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2922 * Add a block depot and mark it in the extended block.
2924 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2925 This
->bigBlockDepotCount
++;
2926 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2928 /* Flag the block depot.
2930 StorageImpl_SetNextBlockInChain(This
,
2934 /* If necessary, flag the extended depot block.
2936 if (extIndex
!= BLOCK_UNUSED
)
2937 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2939 /* Save header information.
2941 StorageImpl_SaveFileHeader(This
);
2945 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2949 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2950 ( nextBlockIndex
!= BLOCK_UNUSED
))
2952 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2954 if (nextBlockIndex
== BLOCK_UNUSED
)
2956 freeBlock
= (depotIndex
* blocksPerDepot
) +
2957 (depotBlockOffset
/sizeof(ULONG
));
2960 depotBlockOffset
+= sizeof(ULONG
);
2965 depotBlockOffset
= 0;
2969 * make sure that the block physically exists before using it
2971 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
2972 BIGBLOCKFILE_Expand(This
->bigBlockFile
, neededSize
);
2974 This
->prevFreeBlock
= freeBlock
;
2979 /******************************************************************************
2980 * Storage32Impl_AddBlockDepot
2982 * This will create a depot block, essentially it is a block initialized
2985 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2987 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
2990 * Initialize blocks as free
2992 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2993 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2996 /******************************************************************************
2997 * Storage32Impl_GetExtDepotBlock
2999 * Returns the index of the block that corresponds to the specified depot
3000 * index. This method is only for depot indexes equal or greater than
3001 * COUNT_BBDEPOTINHEADER.
3003 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3005 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3006 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3007 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3008 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3009 ULONG blockIndex
= BLOCK_UNUSED
;
3010 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3012 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3014 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3015 return BLOCK_UNUSED
;
3017 while (extBlockCount
> 0)
3019 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3023 if (extBlockIndex
!= BLOCK_UNUSED
)
3024 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3025 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3030 /******************************************************************************
3031 * Storage32Impl_SetExtDepotBlock
3033 * Associates the specified block index to the specified depot index.
3034 * This method is only for depot indexes equal or greater than
3035 * COUNT_BBDEPOTINHEADER.
3037 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3039 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3040 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3041 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3042 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3043 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3045 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3047 while (extBlockCount
> 0)
3049 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3053 if (extBlockIndex
!= BLOCK_UNUSED
)
3055 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3056 extBlockOffset
* sizeof(ULONG
),
3061 /******************************************************************************
3062 * Storage32Impl_AddExtBlockDepot
3064 * Creates an extended depot block.
3066 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3068 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3069 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3070 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3071 ULONG index
= BLOCK_UNUSED
;
3072 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3073 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3074 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3076 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3077 blocksPerDepotBlock
;
3079 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3082 * The first extended block.
3084 This
->extBigBlockDepotStart
= index
;
3090 * Follow the chain to the last one.
3092 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3094 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3098 * Add the new extended block to the chain.
3100 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3105 * Initialize this block.
3107 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3108 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3113 /******************************************************************************
3114 * Storage32Impl_FreeBigBlock
3116 * This method will flag the specified block as free in the big block depot.
3118 static void StorageImpl_FreeBigBlock(
3122 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3124 if (blockIndex
< This
->prevFreeBlock
)
3125 This
->prevFreeBlock
= blockIndex
;
3128 /************************************************************************
3129 * Storage32Impl_GetNextBlockInChain
3131 * This method will retrieve the block index of the next big block in
3134 * Params: This - Pointer to the Storage object.
3135 * blockIndex - Index of the block to retrieve the chain
3137 * nextBlockIndex - receives the return value.
3139 * Returns: This method returns the index of the next block in the chain.
3140 * It will return the constants:
3141 * BLOCK_SPECIAL - If the block given was not part of a
3143 * BLOCK_END_OF_CHAIN - If the block given was the last in
3145 * BLOCK_UNUSED - If the block given was not past of a chain
3147 * BLOCK_EXTBBDEPOT - This block is part of the extended
3150 * See Windows documentation for more details on IStorage methods.
3152 static HRESULT
StorageImpl_GetNextBlockInChain(
3155 ULONG
* nextBlockIndex
)
3157 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3158 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3159 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3160 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3162 ULONG depotBlockIndexPos
;
3163 int index
, num_blocks
;
3165 *nextBlockIndex
= BLOCK_SPECIAL
;
3167 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3169 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3170 This
->bigBlockDepotCount
);
3171 return STG_E_READFAULT
;
3175 * Cache the currently accessed depot block.
3177 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3179 This
->indexBlockDepotCached
= depotBlockCount
;
3181 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3183 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3188 * We have to look in the extended depot.
3190 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3193 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3196 return STG_E_READFAULT
;
3198 num_blocks
= This
->bigBlockSize
/ 4;
3200 for (index
= 0; index
< num_blocks
; index
++)
3202 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3203 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3207 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3212 /******************************************************************************
3213 * Storage32Impl_GetNextExtendedBlock
3215 * Given an extended block this method will return the next extended block.
3218 * The last ULONG of an extended block is the block index of the next
3219 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3223 * - The index of the next extended block
3224 * - BLOCK_UNUSED: there is no next extended block.
3225 * - Any other return values denotes failure.
3227 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3229 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3230 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3232 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3235 return nextBlockIndex
;
3238 /******************************************************************************
3239 * Storage32Impl_SetNextBlockInChain
3241 * This method will write the index of the specified block's next block
3242 * in the big block depot.
3244 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3247 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3248 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3249 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3252 static void StorageImpl_SetNextBlockInChain(
3257 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3258 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3259 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3260 ULONG depotBlockIndexPos
;
3262 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3263 assert(blockIndex
!= nextBlock
);
3265 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3267 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3272 * We have to look in the extended depot.
3274 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3277 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3280 * Update the cached block depot, if necessary.
3282 if (depotBlockCount
== This
->indexBlockDepotCached
)
3284 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3288 /******************************************************************************
3289 * Storage32Impl_LoadFileHeader
3291 * This method will read in the file header
3293 static HRESULT
StorageImpl_LoadFileHeader(
3297 BYTE headerBigBlock
[HEADER_SIZE
];
3299 ULARGE_INTEGER offset
;
3304 * Get a pointer to the big block of data containing the header.
3306 offset
.u
.HighPart
= 0;
3307 offset
.u
.LowPart
= 0;
3308 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3309 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3310 hr
= STG_E_FILENOTFOUND
;
3313 * Extract the information from the header.
3318 * Check for the "magic number" signature and return an error if it is not
3321 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3323 return STG_E_OLDFORMAT
;
3326 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3328 return STG_E_INVALIDHEADER
;
3331 StorageUtl_ReadWord(
3333 OFFSET_BIGBLOCKSIZEBITS
,
3334 &This
->bigBlockSizeBits
);
3336 StorageUtl_ReadWord(
3338 OFFSET_SMALLBLOCKSIZEBITS
,
3339 &This
->smallBlockSizeBits
);
3341 StorageUtl_ReadDWord(
3343 OFFSET_BBDEPOTCOUNT
,
3344 &This
->bigBlockDepotCount
);
3346 StorageUtl_ReadDWord(
3348 OFFSET_ROOTSTARTBLOCK
,
3349 &This
->rootStartBlock
);
3351 StorageUtl_ReadDWord(
3353 OFFSET_SBDEPOTSTART
,
3354 &This
->smallBlockDepotStart
);
3356 StorageUtl_ReadDWord(
3358 OFFSET_EXTBBDEPOTSTART
,
3359 &This
->extBigBlockDepotStart
);
3361 StorageUtl_ReadDWord(
3363 OFFSET_EXTBBDEPOTCOUNT
,
3364 &This
->extBigBlockDepotCount
);
3366 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3368 StorageUtl_ReadDWord(
3370 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3371 &(This
->bigBlockDepotStart
[index
]));
3375 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3377 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3378 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3381 * Right now, the code is making some assumptions about the size of the
3382 * blocks, just make sure they are what we're expecting.
3384 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3385 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3387 WARN("Broken OLE storage file\n");
3388 hr
= STG_E_INVALIDHEADER
;
3397 /******************************************************************************
3398 * Storage32Impl_SaveFileHeader
3400 * This method will save to the file the header
3402 static void StorageImpl_SaveFileHeader(
3405 BYTE headerBigBlock
[HEADER_SIZE
];
3408 ULARGE_INTEGER offset
;
3409 DWORD bytes_read
, bytes_written
;
3412 * Get a pointer to the big block of data containing the header.
3414 offset
.u
.HighPart
= 0;
3415 offset
.u
.LowPart
= 0;
3416 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3417 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3418 hr
= STG_E_FILENOTFOUND
;
3421 * If the block read failed, the file is probably new.
3426 * Initialize for all unknown fields.
3428 memset(headerBigBlock
, 0, HEADER_SIZE
);
3431 * Initialize the magic number.
3433 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3436 * And a bunch of things we don't know what they mean
3438 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3439 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3440 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3441 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3445 * Write the information to the header.
3447 StorageUtl_WriteWord(
3449 OFFSET_BIGBLOCKSIZEBITS
,
3450 This
->bigBlockSizeBits
);
3452 StorageUtl_WriteWord(
3454 OFFSET_SMALLBLOCKSIZEBITS
,
3455 This
->smallBlockSizeBits
);
3457 StorageUtl_WriteDWord(
3459 OFFSET_BBDEPOTCOUNT
,
3460 This
->bigBlockDepotCount
);
3462 StorageUtl_WriteDWord(
3464 OFFSET_ROOTSTARTBLOCK
,
3465 This
->rootStartBlock
);
3467 StorageUtl_WriteDWord(
3469 OFFSET_SBDEPOTSTART
,
3470 This
->smallBlockDepotStart
);
3472 StorageUtl_WriteDWord(
3474 OFFSET_SBDEPOTCOUNT
,
3475 This
->smallBlockDepotChain
?
3476 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3478 StorageUtl_WriteDWord(
3480 OFFSET_EXTBBDEPOTSTART
,
3481 This
->extBigBlockDepotStart
);
3483 StorageUtl_WriteDWord(
3485 OFFSET_EXTBBDEPOTCOUNT
,
3486 This
->extBigBlockDepotCount
);
3488 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3490 StorageUtl_WriteDWord(
3492 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3493 (This
->bigBlockDepotStart
[index
]));
3497 * Write the big block back to the file.
3499 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3502 /******************************************************************************
3503 * StorageImpl_ReadRawDirEntry
3505 * This method will read the raw data from a directory entry in the file.
3507 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3509 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3511 ULARGE_INTEGER offset
;
3515 offset
.u
.HighPart
= 0;
3516 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3518 hr
= BlockChainStream_ReadAt(
3519 This
->rootBlockChain
,
3528 /******************************************************************************
3529 * StorageImpl_WriteRawDirEntry
3531 * This method will write the raw data from a directory entry in the file.
3533 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3535 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3537 ULARGE_INTEGER offset
;
3541 offset
.u
.HighPart
= 0;
3542 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3544 hr
= BlockChainStream_WriteAt(
3545 This
->rootBlockChain
,
3554 /******************************************************************************
3557 * Update raw directory entry data from the fields in newData.
3559 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3561 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3563 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3566 buffer
+ OFFSET_PS_NAME
,
3568 DIRENTRY_NAME_BUFFER_LEN
);
3570 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3572 StorageUtl_WriteWord(
3574 OFFSET_PS_NAMELENGTH
,
3575 newData
->sizeOfNameString
);
3577 StorageUtl_WriteDWord(
3579 OFFSET_PS_LEFTCHILD
,
3580 newData
->leftChild
);
3582 StorageUtl_WriteDWord(
3584 OFFSET_PS_RIGHTCHILD
,
3585 newData
->rightChild
);
3587 StorageUtl_WriteDWord(
3590 newData
->dirRootEntry
);
3592 StorageUtl_WriteGUID(
3597 StorageUtl_WriteDWord(
3600 newData
->ctime
.dwLowDateTime
);
3602 StorageUtl_WriteDWord(
3604 OFFSET_PS_CTIMEHIGH
,
3605 newData
->ctime
.dwHighDateTime
);
3607 StorageUtl_WriteDWord(
3610 newData
->mtime
.dwLowDateTime
);
3612 StorageUtl_WriteDWord(
3614 OFFSET_PS_MTIMEHIGH
,
3615 newData
->ctime
.dwHighDateTime
);
3617 StorageUtl_WriteDWord(
3619 OFFSET_PS_STARTBLOCK
,
3620 newData
->startingBlock
);
3622 StorageUtl_WriteDWord(
3625 newData
->size
.u
.LowPart
);
3628 /******************************************************************************
3629 * Storage32Impl_ReadDirEntry
3631 * This method will read the specified directory entry.
3633 HRESULT
StorageImpl_ReadDirEntry(
3638 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3641 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3643 if (SUCCEEDED(readRes
))
3645 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3648 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3649 DIRENTRY_NAME_BUFFER_LEN
);
3650 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3652 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3654 StorageUtl_ReadWord(
3656 OFFSET_PS_NAMELENGTH
,
3657 &buffer
->sizeOfNameString
);
3659 StorageUtl_ReadDWord(
3661 OFFSET_PS_LEFTCHILD
,
3662 &buffer
->leftChild
);
3664 StorageUtl_ReadDWord(
3666 OFFSET_PS_RIGHTCHILD
,
3667 &buffer
->rightChild
);
3669 StorageUtl_ReadDWord(
3672 &buffer
->dirRootEntry
);
3674 StorageUtl_ReadGUID(
3679 StorageUtl_ReadDWord(
3682 &buffer
->ctime
.dwLowDateTime
);
3684 StorageUtl_ReadDWord(
3686 OFFSET_PS_CTIMEHIGH
,
3687 &buffer
->ctime
.dwHighDateTime
);
3689 StorageUtl_ReadDWord(
3692 &buffer
->mtime
.dwLowDateTime
);
3694 StorageUtl_ReadDWord(
3696 OFFSET_PS_MTIMEHIGH
,
3697 &buffer
->mtime
.dwHighDateTime
);
3699 StorageUtl_ReadDWord(
3701 OFFSET_PS_STARTBLOCK
,
3702 &buffer
->startingBlock
);
3704 StorageUtl_ReadDWord(
3707 &buffer
->size
.u
.LowPart
);
3709 buffer
->size
.u
.HighPart
= 0;
3715 /*********************************************************************
3716 * Write the specified directory entry to the file
3718 HRESULT
StorageImpl_WriteDirEntry(
3721 const DirEntry
* buffer
)
3723 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3726 UpdateRawDirEntry(currentEntry
, buffer
);
3728 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3732 static BOOL
StorageImpl_ReadBigBlock(
3737 ULARGE_INTEGER ulOffset
;
3740 ulOffset
.u
.HighPart
= 0;
3741 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3743 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3744 return (read
== This
->bigBlockSize
);
3747 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3753 ULARGE_INTEGER ulOffset
;
3757 ulOffset
.u
.HighPart
= 0;
3758 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3759 ulOffset
.u
.LowPart
+= offset
;
3761 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3762 *value
= lendian32toh(tmp
);
3763 return (read
== sizeof(DWORD
));
3766 static BOOL
StorageImpl_WriteBigBlock(
3771 ULARGE_INTEGER ulOffset
;
3774 ulOffset
.u
.HighPart
= 0;
3775 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3777 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3778 return (wrote
== This
->bigBlockSize
);
3781 static BOOL
StorageImpl_WriteDWordToBigBlock(
3787 ULARGE_INTEGER ulOffset
;
3790 ulOffset
.u
.HighPart
= 0;
3791 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3792 ulOffset
.u
.LowPart
+= offset
;
3794 value
= htole32(value
);
3795 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3796 return (wrote
== sizeof(DWORD
));
3799 /******************************************************************************
3800 * Storage32Impl_SmallBlocksToBigBlocks
3802 * This method will convert a small block chain to a big block chain.
3803 * The small block chain will be destroyed.
3805 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3807 SmallBlockChainStream
** ppsbChain
)
3809 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3810 ULARGE_INTEGER size
, offset
;
3811 ULONG cbRead
, cbWritten
;
3812 ULARGE_INTEGER cbTotalRead
;
3813 DirRef streamEntryRef
;
3814 HRESULT resWrite
= S_OK
;
3816 DirEntry streamEntry
;
3818 BlockChainStream
*bbTempChain
= NULL
;
3819 BlockChainStream
*bigBlockChain
= NULL
;
3822 * Create a temporary big block chain that doesn't have
3823 * an associated directory entry. This temporary chain will be
3824 * used to copy data from small blocks to big blocks.
3826 bbTempChain
= BlockChainStream_Construct(This
,
3829 if(!bbTempChain
) return NULL
;
3831 * Grow the big block chain.
3833 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3834 BlockChainStream_SetSize(bbTempChain
, size
);
3837 * Copy the contents of the small block chain to the big block chain
3838 * by small block size increments.
3840 offset
.u
.LowPart
= 0;
3841 offset
.u
.HighPart
= 0;
3842 cbTotalRead
.QuadPart
= 0;
3844 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3847 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3849 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3852 if (FAILED(resRead
))
3857 cbTotalRead
.QuadPart
+= cbRead
;
3859 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3865 if (FAILED(resWrite
))
3868 offset
.u
.LowPart
+= cbRead
;
3870 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3871 HeapFree(GetProcessHeap(),0,buffer
);
3873 size
.u
.HighPart
= 0;
3876 if (FAILED(resRead
) || FAILED(resWrite
))
3878 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3879 BlockChainStream_SetSize(bbTempChain
, size
);
3880 BlockChainStream_Destroy(bbTempChain
);
3885 * Destroy the small block chain.
3887 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3888 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3889 SmallBlockChainStream_Destroy(*ppsbChain
);
3893 * Change the directory entry. This chain is now a big block chain
3894 * and it doesn't reside in the small blocks chain anymore.
3896 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3898 streamEntry
.startingBlock
= bbHeadOfChain
;
3900 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3903 * Destroy the temporary entryless big block chain.
3904 * Create a new big block chain associated with this entry.
3906 BlockChainStream_Destroy(bbTempChain
);
3907 bigBlockChain
= BlockChainStream_Construct(This
,
3911 return bigBlockChain
;
3914 /******************************************************************************
3915 * Storage32Impl_BigBlocksToSmallBlocks
3917 * This method will convert a big block chain to a small block chain.
3918 * The big block chain will be destroyed on success.
3920 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3922 BlockChainStream
** ppbbChain
)
3924 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3925 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3926 DirRef streamEntryRef
;
3927 HRESULT resWrite
= S_OK
, resRead
;
3928 DirEntry streamEntry
;
3930 SmallBlockChainStream
* sbTempChain
;
3932 TRACE("%p %p\n", This
, ppbbChain
);
3934 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3940 size
= BlockChainStream_GetSize(*ppbbChain
);
3941 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3943 offset
.u
.HighPart
= 0;
3944 offset
.u
.LowPart
= 0;
3945 cbTotalRead
.QuadPart
= 0;
3946 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3949 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3950 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3958 cbTotalRead
.QuadPart
+= cbRead
;
3960 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3961 cbRead
, buffer
, &cbWritten
);
3963 if(FAILED(resWrite
))
3966 offset
.u
.LowPart
+= cbRead
;
3968 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3969 HeapFree(GetProcessHeap(), 0, buffer
);
3971 size
.u
.HighPart
= 0;
3974 if(FAILED(resRead
) || FAILED(resWrite
))
3976 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3977 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3978 SmallBlockChainStream_Destroy(sbTempChain
);
3982 /* destroy the original big block chain */
3983 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3984 BlockChainStream_SetSize(*ppbbChain
, size
);
3985 BlockChainStream_Destroy(*ppbbChain
);
3988 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3989 streamEntry
.startingBlock
= sbHeadOfChain
;
3990 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3992 SmallBlockChainStream_Destroy(sbTempChain
);
3993 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3996 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3999 DirEntry parentData
, snapshotData
;
4001 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
4002 0, (IStorage
**)snapshot
);
4006 hr
= StorageBaseImpl_ReadDirEntry(original
,
4007 original
->storageDirEntry
, &parentData
);
4010 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
4011 (*snapshot
)->storageDirEntry
, &snapshotData
);
4015 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
4016 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
4017 snapshotData
.stgType
= parentData
.stgType
;
4018 snapshotData
.clsid
= parentData
.clsid
;
4019 snapshotData
.ctime
= parentData
.ctime
;
4020 snapshotData
.mtime
= parentData
.mtime
;
4021 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
4022 (*snapshot
)->storageDirEntry
, &snapshotData
);
4026 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
4027 (IStorage
*)(*snapshot
));
4029 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
4035 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4037 DWORD grfCommitFlags
) /* [in] */
4039 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4041 DirEntry data
, tempStorageData
, snapshotRootData
;
4042 DirRef tempStorageEntry
, oldDirRoot
;
4043 StorageInternalImpl
*tempStorage
;
4045 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4047 /* Cannot commit a read-only transacted storage */
4048 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4049 return STG_E_ACCESSDENIED
;
4051 /* To prevent data loss, we create the new structure in the file before we
4052 * delete the old one, so that in case of errors the old data is intact. We
4053 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4054 * needed in the rare situation where we have just enough free disk space to
4055 * overwrite the existing data. */
4057 /* Create an orphaned storage in the parent for the new directory structure. */
4058 memset(&data
, 0, sizeof(data
));
4060 data
.sizeOfNameString
= 1;
4061 data
.stgType
= STGTY_STORAGE
;
4062 data
.leftChild
= DIRENTRY_NULL
;
4063 data
.rightChild
= DIRENTRY_NULL
;
4064 data
.dirRootEntry
= DIRENTRY_NULL
;
4065 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4067 if (FAILED(hr
)) return hr
;
4069 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4070 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4073 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4074 (IStorage
*)tempStorage
);
4076 list_init(&tempStorage
->ParentListEntry
);
4078 IStorage_Release((IStorage
*) tempStorage
);
4085 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4089 /* Update the storage to use the new data in one step. */
4090 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4091 This
->transactedParent
->storageDirEntry
, &data
);
4095 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4096 tempStorageEntry
, &tempStorageData
);
4101 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4102 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4107 oldDirRoot
= data
.dirRootEntry
;
4108 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4109 data
.clsid
= snapshotRootData
.clsid
;
4110 data
.ctime
= snapshotRootData
.ctime
;
4111 data
.mtime
= snapshotRootData
.mtime
;
4113 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4114 This
->transactedParent
->storageDirEntry
, &data
);
4119 /* Destroy the old now-orphaned data. */
4120 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4121 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4125 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4131 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4134 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4135 StorageBaseImpl
*newSnapshot
;
4138 TRACE("(%p)\n", iface
);
4140 /* Create a new copy of the parent data. */
4141 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4142 if (FAILED(hr
)) return hr
;
4144 /* Destroy the open objects. */
4145 StorageBaseImpl_DeleteAll(&This
->base
);
4147 /* Replace our current snapshot. */
4148 IStorage_Release((IStorage
*)This
->snapshot
);
4149 This
->snapshot
= newSnapshot
;
4154 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4156 if (!This
->reverted
)
4158 TRACE("Storage invalidated (stg=%p)\n", This
);
4162 StorageBaseImpl_DeleteAll(This
);
4166 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4168 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4170 TransactedSnapshotImpl_Invalidate(iface
);
4172 IStorage_Release((IStorage
*)This
->transactedParent
);
4174 IStorage_Release((IStorage
*)This
->snapshot
);
4176 HeapFree(GetProcessHeap(), 0, This
);
4179 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4180 const DirEntry
*newData
, DirRef
*index
)
4182 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4184 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4188 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4189 DirRef index
, const DirEntry
*data
)
4191 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4193 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4197 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4198 DirRef index
, DirEntry
*data
)
4200 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4202 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4206 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4209 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4211 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4215 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4216 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4218 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4220 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4221 index
, offset
, size
, buffer
, bytesRead
);
4224 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4225 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4227 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4229 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4230 index
, offset
, size
, buffer
, bytesWritten
);
4233 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4234 DirRef index
, ULARGE_INTEGER newsize
)
4236 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4238 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4242 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4244 StorageBaseImpl_QueryInterface
,
4245 StorageBaseImpl_AddRef
,
4246 StorageBaseImpl_Release
,
4247 StorageBaseImpl_CreateStream
,
4248 StorageBaseImpl_OpenStream
,
4249 StorageBaseImpl_CreateStorage
,
4250 StorageBaseImpl_OpenStorage
,
4251 StorageBaseImpl_CopyTo
,
4252 StorageBaseImpl_MoveElementTo
,
4253 TransactedSnapshotImpl_Commit
,
4254 TransactedSnapshotImpl_Revert
,
4255 StorageBaseImpl_EnumElements
,
4256 StorageBaseImpl_DestroyElement
,
4257 StorageBaseImpl_RenameElement
,
4258 StorageBaseImpl_SetElementTimes
,
4259 StorageBaseImpl_SetClass
,
4260 StorageBaseImpl_SetStateBits
,
4261 StorageBaseImpl_Stat
4264 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4266 TransactedSnapshotImpl_Destroy
,
4267 TransactedSnapshotImpl_Invalidate
,
4268 TransactedSnapshotImpl_CreateDirEntry
,
4269 TransactedSnapshotImpl_WriteDirEntry
,
4270 TransactedSnapshotImpl_ReadDirEntry
,
4271 TransactedSnapshotImpl_DestroyDirEntry
,
4272 TransactedSnapshotImpl_StreamReadAt
,
4273 TransactedSnapshotImpl_StreamWriteAt
,
4274 TransactedSnapshotImpl_StreamSetSize
4277 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4278 TransactedSnapshotImpl
** result
)
4282 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4285 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4287 /* This is OK because the property set storage functions use the IStorage functions. */
4288 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4290 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4292 list_init(&(*result
)->base
.strmHead
);
4294 list_init(&(*result
)->base
.storageHead
);
4296 (*result
)->base
.ref
= 1;
4298 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4300 (*result
)->base
.filename
= parentStorage
->filename
;
4302 /* Create a new temporary storage to act as the snapshot */
4303 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4307 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4309 /* parentStorage already has 1 reference, which we take over here. */
4310 (*result
)->transactedParent
= parentStorage
;
4312 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4315 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4320 return E_OUTOFMEMORY
;
4323 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4324 StorageBaseImpl
** result
)
4328 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4330 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4333 return TransactedSnapshotImpl_Construct(parentStorage
,
4334 (TransactedSnapshotImpl
**)result
);
4337 static HRESULT
Storage_Construct(
4344 StorageBaseImpl
** result
)
4346 StorageImpl
*newStorage
;
4347 StorageBaseImpl
*newTransactedStorage
;
4350 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4351 if (FAILED(hr
)) goto end
;
4353 if (openFlags
& STGM_TRANSACTED
)
4355 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4357 IStorage_Release((IStorage
*)newStorage
);
4359 *result
= newTransactedStorage
;
4362 *result
= &newStorage
->base
;
4368 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4370 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4372 if (!This
->base
.reverted
)
4374 TRACE("Storage invalidated (stg=%p)\n", This
);
4376 This
->base
.reverted
= 1;
4378 This
->parentStorage
= NULL
;
4380 StorageBaseImpl_DeleteAll(&This
->base
);
4382 list_remove(&This
->ParentListEntry
);
4386 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4388 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4390 StorageInternalImpl_Invalidate(&This
->base
);
4392 HeapFree(GetProcessHeap(), 0, This
);
4395 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4396 const DirEntry
*newData
, DirRef
*index
)
4398 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4400 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4404 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4405 DirRef index
, const DirEntry
*data
)
4407 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4409 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4413 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4414 DirRef index
, DirEntry
*data
)
4416 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4418 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4422 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4425 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4427 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4431 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4432 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4434 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4436 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4437 index
, offset
, size
, buffer
, bytesRead
);
4440 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4441 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4443 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4445 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4446 index
, offset
, size
, buffer
, bytesWritten
);
4449 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4450 DirRef index
, ULARGE_INTEGER newsize
)
4452 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4454 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4458 /******************************************************************************
4460 ** Storage32InternalImpl_Commit
4463 static HRESULT WINAPI
StorageInternalImpl_Commit(
4465 DWORD grfCommitFlags
) /* [in] */
4467 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4471 /******************************************************************************
4473 ** Storage32InternalImpl_Revert
4476 static HRESULT WINAPI
StorageInternalImpl_Revert(
4479 FIXME("(%p): stub\n", iface
);
4483 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4485 IStorage_Release((IStorage
*)This
->parentStorage
);
4486 HeapFree(GetProcessHeap(), 0, This
);
4489 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4490 IEnumSTATSTG
* iface
,
4494 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4497 return E_INVALIDARG
;
4501 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4502 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4505 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4509 return E_NOINTERFACE
;
4512 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4513 IEnumSTATSTG
* iface
)
4515 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4516 return InterlockedIncrement(&This
->ref
);
4519 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4520 IEnumSTATSTG
* iface
)
4522 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4526 newRef
= InterlockedDecrement(&This
->ref
);
4530 IEnumSTATSTGImpl_Destroy(This
);
4536 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4537 IEnumSTATSTGImpl
* This
,
4540 DirRef result
= DIRENTRY_NULL
;
4544 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4546 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4547 This
->parentStorage
->storageDirEntry
, &entry
);
4548 searchNode
= entry
.dirRootEntry
;
4550 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4552 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4556 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4560 searchNode
= entry
.rightChild
;
4564 result
= searchNode
;
4565 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4566 searchNode
= entry
.leftChild
;
4574 if (result
!= DIRENTRY_NULL
)
4575 memcpy(This
->name
, result_name
, sizeof(result_name
));
4581 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4582 IEnumSTATSTG
* iface
,
4585 ULONG
* pceltFetched
)
4587 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4589 DirEntry currentEntry
;
4590 STATSTG
* currentReturnStruct
= rgelt
;
4591 ULONG objectFetched
= 0;
4592 DirRef currentSearchNode
;
4595 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4596 return E_INVALIDARG
;
4598 if (This
->parentStorage
->reverted
)
4599 return STG_E_REVERTED
;
4602 * To avoid the special case, get another pointer to a ULONG value if
4603 * the caller didn't supply one.
4605 if (pceltFetched
==0)
4606 pceltFetched
= &objectFetched
;
4609 * Start the iteration, we will iterate until we hit the end of the
4610 * linked list or until we hit the number of items to iterate through
4614 while ( *pceltFetched
< celt
)
4616 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4618 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4622 * Read the entry from the storage.
4624 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4629 * Copy the information to the return buffer.
4631 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4632 currentReturnStruct
,
4637 * Step to the next item in the iteration
4640 currentReturnStruct
++;
4643 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4650 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4651 IEnumSTATSTG
* iface
,
4654 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4656 ULONG objectFetched
= 0;
4657 DirRef currentSearchNode
;
4660 if (This
->parentStorage
->reverted
)
4661 return STG_E_REVERTED
;
4663 while ( (objectFetched
< celt
) )
4665 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4667 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4673 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4679 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4680 IEnumSTATSTG
* iface
)
4682 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4684 if (This
->parentStorage
->reverted
)
4685 return STG_E_REVERTED
;
4692 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4693 IEnumSTATSTG
* iface
,
4694 IEnumSTATSTG
** ppenum
)
4696 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4698 IEnumSTATSTGImpl
* newClone
;
4700 if (This
->parentStorage
->reverted
)
4701 return STG_E_REVERTED
;
4704 * Perform a sanity check on the parameters.
4707 return E_INVALIDARG
;
4709 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4710 This
->storageDirEntry
);
4714 * The new clone enumeration must point to the same current node as
4717 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4719 *ppenum
= (IEnumSTATSTG
*)newClone
;
4722 * Don't forget to nail down a reference to the clone before
4725 IEnumSTATSTGImpl_AddRef(*ppenum
);
4731 * Virtual function table for the IEnumSTATSTGImpl class.
4733 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4735 IEnumSTATSTGImpl_QueryInterface
,
4736 IEnumSTATSTGImpl_AddRef
,
4737 IEnumSTATSTGImpl_Release
,
4738 IEnumSTATSTGImpl_Next
,
4739 IEnumSTATSTGImpl_Skip
,
4740 IEnumSTATSTGImpl_Reset
,
4741 IEnumSTATSTGImpl_Clone
4744 /******************************************************************************
4745 ** IEnumSTATSTGImpl implementation
4748 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4749 StorageBaseImpl
* parentStorage
,
4750 DirRef storageDirEntry
)
4752 IEnumSTATSTGImpl
* newEnumeration
;
4754 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4756 if (newEnumeration
!=0)
4759 * Set-up the virtual function table and reference count.
4761 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4762 newEnumeration
->ref
= 0;
4765 * We want to nail-down the reference to the storage in case the
4766 * enumeration out-lives the storage in the client application.
4768 newEnumeration
->parentStorage
= parentStorage
;
4769 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4771 newEnumeration
->storageDirEntry
= storageDirEntry
;
4774 * Make sure the current node of the iterator is the first one.
4776 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4779 return newEnumeration
;
4783 * Virtual function table for the Storage32InternalImpl class.
4785 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4787 StorageBaseImpl_QueryInterface
,
4788 StorageBaseImpl_AddRef
,
4789 StorageBaseImpl_Release
,
4790 StorageBaseImpl_CreateStream
,
4791 StorageBaseImpl_OpenStream
,
4792 StorageBaseImpl_CreateStorage
,
4793 StorageBaseImpl_OpenStorage
,
4794 StorageBaseImpl_CopyTo
,
4795 StorageBaseImpl_MoveElementTo
,
4796 StorageInternalImpl_Commit
,
4797 StorageInternalImpl_Revert
,
4798 StorageBaseImpl_EnumElements
,
4799 StorageBaseImpl_DestroyElement
,
4800 StorageBaseImpl_RenameElement
,
4801 StorageBaseImpl_SetElementTimes
,
4802 StorageBaseImpl_SetClass
,
4803 StorageBaseImpl_SetStateBits
,
4804 StorageBaseImpl_Stat
4807 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4809 StorageInternalImpl_Destroy
,
4810 StorageInternalImpl_Invalidate
,
4811 StorageInternalImpl_CreateDirEntry
,
4812 StorageInternalImpl_WriteDirEntry
,
4813 StorageInternalImpl_ReadDirEntry
,
4814 StorageInternalImpl_DestroyDirEntry
,
4815 StorageInternalImpl_StreamReadAt
,
4816 StorageInternalImpl_StreamWriteAt
,
4817 StorageInternalImpl_StreamSetSize
4820 /******************************************************************************
4821 ** Storage32InternalImpl implementation
4824 static StorageInternalImpl
* StorageInternalImpl_Construct(
4825 StorageBaseImpl
* parentStorage
,
4827 DirRef storageDirEntry
)
4829 StorageInternalImpl
* newStorage
;
4831 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4835 list_init(&newStorage
->base
.strmHead
);
4837 list_init(&newStorage
->base
.storageHead
);
4840 * Initialize the virtual function table.
4842 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4843 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4844 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4846 newStorage
->base
.reverted
= 0;
4848 newStorage
->base
.ref
= 1;
4850 newStorage
->parentStorage
= parentStorage
;
4853 * Keep a reference to the directory entry of this storage
4855 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4857 newStorage
->base
.create
= 0;
4865 /******************************************************************************
4866 ** StorageUtl implementation
4869 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4873 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4874 *value
= lendian16toh(tmp
);
4877 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4879 value
= htole16(value
);
4880 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4883 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4887 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4888 *value
= lendian32toh(tmp
);
4891 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4893 value
= htole32(value
);
4894 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4897 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4898 ULARGE_INTEGER
* value
)
4900 #ifdef WORDS_BIGENDIAN
4903 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4904 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4905 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4907 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4911 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4912 const ULARGE_INTEGER
*value
)
4914 #ifdef WORDS_BIGENDIAN
4917 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4918 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4919 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4921 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4925 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4927 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4928 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4929 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4931 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4934 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4936 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4937 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4938 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4940 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4943 void StorageUtl_CopyDirEntryToSTATSTG(
4944 StorageBaseImpl
* storage
,
4945 STATSTG
* destination
,
4946 const DirEntry
* source
,
4951 if (source
->stgType
== STGTY_ROOT
)
4953 /* replace the name of root entry (often "Root Entry") by the file name */
4954 entryName
= storage
->filename
;
4958 entryName
= source
->name
;
4962 * The copy of the string occurs only when the flag is not set
4964 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4965 (entryName
== NULL
) ||
4966 (entryName
[0] == 0) )
4968 destination
->pwcsName
= 0;
4972 destination
->pwcsName
=
4973 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4975 strcpyW(destination
->pwcsName
, entryName
);
4978 switch (source
->stgType
)
4982 destination
->type
= STGTY_STORAGE
;
4985 destination
->type
= STGTY_STREAM
;
4988 destination
->type
= STGTY_STREAM
;
4992 destination
->cbSize
= source
->size
;
4994 currentReturnStruct->mtime = {0}; TODO
4995 currentReturnStruct->ctime = {0};
4996 currentReturnStruct->atime = {0};
4998 destination
->grfMode
= 0;
4999 destination
->grfLocksSupported
= 0;
5000 destination
->clsid
= source
->clsid
;
5001 destination
->grfStateBits
= 0;
5002 destination
->reserved
= 0;
5005 /******************************************************************************
5006 ** BlockChainStream implementation
5009 BlockChainStream
* BlockChainStream_Construct(
5010 StorageImpl
* parentStorage
,
5011 ULONG
* headOfStreamPlaceHolder
,
5014 BlockChainStream
* newStream
;
5017 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5019 newStream
->parentStorage
= parentStorage
;
5020 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5021 newStream
->ownerDirEntry
= dirEntry
;
5022 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
5023 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
5024 newStream
->numBlocks
= 0;
5026 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
5028 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5030 newStream
->numBlocks
++;
5031 newStream
->tailIndex
= blockIndex
;
5033 if(FAILED(StorageImpl_GetNextBlockInChain(
5038 HeapFree(GetProcessHeap(), 0, newStream
);
5046 void BlockChainStream_Destroy(BlockChainStream
* This
)
5048 HeapFree(GetProcessHeap(), 0, This
);
5051 /******************************************************************************
5052 * BlockChainStream_GetHeadOfChain
5054 * Returns the head of this stream chain.
5055 * Some special chains don't have directory entries, their heads are kept in
5056 * This->headOfStreamPlaceHolder.
5059 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5061 DirEntry chainEntry
;
5064 if (This
->headOfStreamPlaceHolder
!= 0)
5065 return *(This
->headOfStreamPlaceHolder
);
5067 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5069 hr
= StorageImpl_ReadDirEntry(
5070 This
->parentStorage
,
5071 This
->ownerDirEntry
,
5076 return chainEntry
.startingBlock
;
5080 return BLOCK_END_OF_CHAIN
;
5083 /******************************************************************************
5084 * BlockChainStream_GetCount
5086 * Returns the number of blocks that comprises this chain.
5087 * This is not the size of the stream as the last block may not be full!
5090 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5095 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5097 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5101 if(FAILED(StorageImpl_GetNextBlockInChain(
5102 This
->parentStorage
,
5111 /******************************************************************************
5112 * BlockChainStream_ReadAt
5114 * Reads a specified number of bytes from this chain at the specified offset.
5115 * bytesRead may be NULL.
5116 * Failure will be returned if the specified number of bytes has not been read.
5118 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5119 ULARGE_INTEGER offset
,
5124 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5125 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5126 ULONG bytesToReadInBuffer
;
5130 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5133 * Find the first block in the stream that contains part of the buffer.
5135 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5136 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5137 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5139 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5140 This
->lastBlockNoInSequence
= blockNoInSequence
;
5144 ULONG temp
= blockNoInSequence
;
5146 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5147 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5148 This
->lastBlockNoInSequence
= temp
;
5151 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5153 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5154 return STG_E_DOCFILECORRUPT
;
5155 blockNoInSequence
--;
5158 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5159 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5161 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5164 * Start reading the buffer.
5167 bufferWalker
= buffer
;
5169 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5171 ULARGE_INTEGER ulOffset
;
5174 * Calculate how many bytes we can copy from this big block.
5176 bytesToReadInBuffer
=
5177 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5179 TRACE("block %i\n",blockIndex
);
5180 ulOffset
.u
.HighPart
= 0;
5181 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5184 StorageImpl_ReadAt(This
->parentStorage
,
5187 bytesToReadInBuffer
,
5190 * Step to the next big block.
5192 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5193 return STG_E_DOCFILECORRUPT
;
5195 bufferWalker
+= bytesReadAt
;
5196 size
-= bytesReadAt
;
5197 *bytesRead
+= bytesReadAt
;
5198 offsetInBlock
= 0; /* There is no offset on the next block */
5200 if (bytesToReadInBuffer
!= bytesReadAt
)
5204 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5207 /******************************************************************************
5208 * BlockChainStream_WriteAt
5210 * Writes the specified number of bytes to this chain at the specified offset.
5211 * Will fail if not all specified number of bytes have been written.
5213 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5214 ULARGE_INTEGER offset
,
5217 ULONG
* bytesWritten
)
5219 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5220 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5223 const BYTE
* bufferWalker
;
5226 * Find the first block in the stream that contains part of the buffer.
5228 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5229 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5230 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5232 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5233 This
->lastBlockNoInSequence
= blockNoInSequence
;
5237 ULONG temp
= blockNoInSequence
;
5239 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5240 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5241 This
->lastBlockNoInSequence
= temp
;
5244 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5246 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5248 return STG_E_DOCFILECORRUPT
;
5249 blockNoInSequence
--;
5252 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5254 /* BlockChainStream_SetSize should have already been called to ensure we have
5255 * enough blocks in the chain to write into */
5256 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5258 ERR("not enough blocks in chain to write data\n");
5259 return STG_E_DOCFILECORRUPT
;
5263 bufferWalker
= buffer
;
5265 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5267 ULARGE_INTEGER ulOffset
;
5268 DWORD bytesWrittenAt
;
5270 * Calculate how many bytes we can copy from this big block.
5273 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5275 TRACE("block %i\n",blockIndex
);
5276 ulOffset
.u
.HighPart
= 0;
5277 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5280 StorageImpl_WriteAt(This
->parentStorage
,
5287 * Step to the next big block.
5289 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5291 return STG_E_DOCFILECORRUPT
;
5293 bufferWalker
+= bytesWrittenAt
;
5294 size
-= bytesWrittenAt
;
5295 *bytesWritten
+= bytesWrittenAt
;
5296 offsetInBlock
= 0; /* There is no offset on the next block */
5298 if (bytesWrittenAt
!= bytesToWrite
)
5302 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5305 /******************************************************************************
5306 * BlockChainStream_Shrink
5308 * Shrinks this chain in the big block depot.
5310 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5311 ULARGE_INTEGER newSize
)
5313 ULONG blockIndex
, extraBlock
;
5318 * Reset the last accessed block cache.
5320 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5321 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5324 * Figure out how many blocks are needed to contain the new size
5326 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5328 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5331 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5334 * Go to the new end of chain
5336 while (count
< numBlocks
)
5338 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5344 /* Get the next block before marking the new end */
5345 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5349 /* Mark the new end of chain */
5350 StorageImpl_SetNextBlockInChain(
5351 This
->parentStorage
,
5353 BLOCK_END_OF_CHAIN
);
5355 This
->tailIndex
= blockIndex
;
5356 This
->numBlocks
= numBlocks
;
5359 * Mark the extra blocks as free
5361 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5363 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5366 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5367 extraBlock
= blockIndex
;
5373 /******************************************************************************
5374 * BlockChainStream_Enlarge
5376 * Grows this chain in the big block depot.
5378 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5379 ULARGE_INTEGER newSize
)
5381 ULONG blockIndex
, currentBlock
;
5383 ULONG oldNumBlocks
= 0;
5385 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5388 * Empty chain. Create the head.
5390 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5392 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5393 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5395 BLOCK_END_OF_CHAIN
);
5397 if (This
->headOfStreamPlaceHolder
!= 0)
5399 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5403 DirEntry chainEntry
;
5404 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5406 StorageImpl_ReadDirEntry(
5407 This
->parentStorage
,
5408 This
->ownerDirEntry
,
5411 chainEntry
.startingBlock
= blockIndex
;
5413 StorageImpl_WriteDirEntry(
5414 This
->parentStorage
,
5415 This
->ownerDirEntry
,
5419 This
->tailIndex
= blockIndex
;
5420 This
->numBlocks
= 1;
5424 * Figure out how many blocks are needed to contain this stream
5426 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5428 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5432 * Go to the current end of chain
5434 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5436 currentBlock
= blockIndex
;
5438 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5441 currentBlock
= blockIndex
;
5443 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5448 This
->tailIndex
= currentBlock
;
5451 currentBlock
= This
->tailIndex
;
5452 oldNumBlocks
= This
->numBlocks
;
5455 * Add new blocks to the chain
5457 if (oldNumBlocks
< newNumBlocks
)
5459 while (oldNumBlocks
< newNumBlocks
)
5461 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5463 StorageImpl_SetNextBlockInChain(
5464 This
->parentStorage
,
5468 StorageImpl_SetNextBlockInChain(
5469 This
->parentStorage
,
5471 BLOCK_END_OF_CHAIN
);
5473 currentBlock
= blockIndex
;
5477 This
->tailIndex
= blockIndex
;
5478 This
->numBlocks
= newNumBlocks
;
5484 /******************************************************************************
5485 * BlockChainStream_SetSize
5487 * Sets the size of this stream. The big block depot will be updated.
5488 * The file will grow if we grow the chain.
5490 * TODO: Free the actual blocks in the file when we shrink the chain.
5491 * Currently, the blocks are still in the file. So the file size
5492 * doesn't shrink even if we shrink streams.
5494 BOOL
BlockChainStream_SetSize(
5495 BlockChainStream
* This
,
5496 ULARGE_INTEGER newSize
)
5498 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5500 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5503 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5505 BlockChainStream_Shrink(This
, newSize
);
5509 BlockChainStream_Enlarge(This
, newSize
);
5515 /******************************************************************************
5516 * BlockChainStream_GetSize
5518 * Returns the size of this chain.
5519 * Will return the block count if this chain doesn't have a directory entry.
5521 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5523 DirEntry chainEntry
;
5525 if(This
->headOfStreamPlaceHolder
== NULL
)
5528 * This chain has a directory entry so use the size value from there.
5530 StorageImpl_ReadDirEntry(
5531 This
->parentStorage
,
5532 This
->ownerDirEntry
,
5535 return chainEntry
.size
;
5540 * this chain is a chain that does not have a directory entry, figure out the
5541 * size by making the product number of used blocks times the
5544 ULARGE_INTEGER result
;
5545 result
.u
.HighPart
= 0;
5548 BlockChainStream_GetCount(This
) *
5549 This
->parentStorage
->bigBlockSize
;
5555 /******************************************************************************
5556 ** SmallBlockChainStream implementation
5559 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5560 StorageImpl
* parentStorage
,
5561 ULONG
* headOfStreamPlaceHolder
,
5564 SmallBlockChainStream
* newStream
;
5566 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5568 newStream
->parentStorage
= parentStorage
;
5569 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5570 newStream
->ownerDirEntry
= dirEntry
;
5575 void SmallBlockChainStream_Destroy(
5576 SmallBlockChainStream
* This
)
5578 HeapFree(GetProcessHeap(), 0, This
);
5581 /******************************************************************************
5582 * SmallBlockChainStream_GetHeadOfChain
5584 * Returns the head of this chain of small blocks.
5586 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5587 SmallBlockChainStream
* This
)
5589 DirEntry chainEntry
;
5592 if (This
->headOfStreamPlaceHolder
!= NULL
)
5593 return *(This
->headOfStreamPlaceHolder
);
5595 if (This
->ownerDirEntry
)
5597 hr
= StorageImpl_ReadDirEntry(
5598 This
->parentStorage
,
5599 This
->ownerDirEntry
,
5604 return chainEntry
.startingBlock
;
5609 return BLOCK_END_OF_CHAIN
;
5612 /******************************************************************************
5613 * SmallBlockChainStream_GetNextBlockInChain
5615 * Returns the index of the next small block in this chain.
5618 * - BLOCK_END_OF_CHAIN: end of this chain
5619 * - BLOCK_UNUSED: small block 'blockIndex' is free
5621 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5622 SmallBlockChainStream
* This
,
5624 ULONG
* nextBlockInChain
)
5626 ULARGE_INTEGER offsetOfBlockInDepot
;
5631 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5633 offsetOfBlockInDepot
.u
.HighPart
= 0;
5634 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5637 * Read those bytes in the buffer from the small block file.
5639 res
= BlockChainStream_ReadAt(
5640 This
->parentStorage
->smallBlockDepotChain
,
5641 offsetOfBlockInDepot
,
5648 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5655 /******************************************************************************
5656 * SmallBlockChainStream_SetNextBlockInChain
5658 * Writes the index of the next block of the specified block in the small
5660 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5661 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5663 static void SmallBlockChainStream_SetNextBlockInChain(
5664 SmallBlockChainStream
* This
,
5668 ULARGE_INTEGER offsetOfBlockInDepot
;
5672 offsetOfBlockInDepot
.u
.HighPart
= 0;
5673 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5675 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5678 * Read those bytes in the buffer from the small block file.
5680 BlockChainStream_WriteAt(
5681 This
->parentStorage
->smallBlockDepotChain
,
5682 offsetOfBlockInDepot
,
5688 /******************************************************************************
5689 * SmallBlockChainStream_FreeBlock
5691 * Flag small block 'blockIndex' as free in the small block depot.
5693 static void SmallBlockChainStream_FreeBlock(
5694 SmallBlockChainStream
* This
,
5697 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5700 /******************************************************************************
5701 * SmallBlockChainStream_GetNextFreeBlock
5703 * Returns the index of a free small block. The small block depot will be
5704 * enlarged if necessary. The small block chain will also be enlarged if
5707 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5708 SmallBlockChainStream
* This
)
5710 ULARGE_INTEGER offsetOfBlockInDepot
;
5713 ULONG blockIndex
= 0;
5714 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5716 ULONG smallBlocksPerBigBlock
;
5718 offsetOfBlockInDepot
.u
.HighPart
= 0;
5721 * Scan the small block depot for a free block
5723 while (nextBlockIndex
!= BLOCK_UNUSED
)
5725 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5727 res
= BlockChainStream_ReadAt(
5728 This
->parentStorage
->smallBlockDepotChain
,
5729 offsetOfBlockInDepot
,
5735 * If we run out of space for the small block depot, enlarge it
5739 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5741 if (nextBlockIndex
!= BLOCK_UNUSED
)
5747 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5749 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5750 ULONG nextBlock
, newsbdIndex
;
5751 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
5753 nextBlock
= sbdIndex
;
5754 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5756 sbdIndex
= nextBlock
;
5757 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5760 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5761 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5762 StorageImpl_SetNextBlockInChain(
5763 This
->parentStorage
,
5767 StorageImpl_SetNextBlockInChain(
5768 This
->parentStorage
,
5770 BLOCK_END_OF_CHAIN
);
5773 * Initialize all the small blocks to free
5775 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5776 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5781 * We have just created the small block depot.
5787 * Save it in the header
5789 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5790 StorageImpl_SaveFileHeader(This
->parentStorage
);
5793 * And allocate the first big block that will contain small blocks
5796 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5798 StorageImpl_SetNextBlockInChain(
5799 This
->parentStorage
,
5801 BLOCK_END_OF_CHAIN
);
5803 StorageImpl_ReadDirEntry(
5804 This
->parentStorage
,
5805 This
->parentStorage
->base
.storageDirEntry
,
5808 rootEntry
.startingBlock
= sbStartIndex
;
5809 rootEntry
.size
.u
.HighPart
= 0;
5810 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5812 StorageImpl_WriteDirEntry(
5813 This
->parentStorage
,
5814 This
->parentStorage
->base
.storageDirEntry
,
5818 StorageImpl_SaveFileHeader(This
->parentStorage
);
5822 smallBlocksPerBigBlock
=
5823 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5826 * Verify if we have to allocate big blocks to contain small blocks
5828 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5831 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5833 StorageImpl_ReadDirEntry(
5834 This
->parentStorage
,
5835 This
->parentStorage
->base
.storageDirEntry
,
5838 if (rootEntry
.size
.u
.LowPart
<
5839 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5841 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5843 BlockChainStream_SetSize(
5844 This
->parentStorage
->smallBlockRootChain
,
5847 StorageImpl_WriteDirEntry(
5848 This
->parentStorage
,
5849 This
->parentStorage
->base
.storageDirEntry
,
5857 /******************************************************************************
5858 * SmallBlockChainStream_ReadAt
5860 * Reads a specified number of bytes from this chain at the specified offset.
5861 * bytesRead may be NULL.
5862 * Failure will be returned if the specified number of bytes has not been read.
5864 HRESULT
SmallBlockChainStream_ReadAt(
5865 SmallBlockChainStream
* This
,
5866 ULARGE_INTEGER offset
,
5872 ULARGE_INTEGER offsetInBigBlockFile
;
5873 ULONG blockNoInSequence
=
5874 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5876 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5877 ULONG bytesToReadInBuffer
;
5879 ULONG bytesReadFromBigBlockFile
;
5883 * This should never happen on a small block file.
5885 assert(offset
.u
.HighPart
==0);
5888 * Find the first block in the stream that contains part of the buffer.
5890 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5892 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5894 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5897 blockNoInSequence
--;
5901 * Start reading the buffer.
5904 bufferWalker
= buffer
;
5906 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5909 * Calculate how many bytes we can copy from this small block.
5911 bytesToReadInBuffer
=
5912 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5915 * Calculate the offset of the small block in the small block file.
5917 offsetInBigBlockFile
.u
.HighPart
= 0;
5918 offsetInBigBlockFile
.u
.LowPart
=
5919 blockIndex
* This
->parentStorage
->smallBlockSize
;
5921 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5924 * Read those bytes in the buffer from the small block file.
5925 * The small block has already been identified so it shouldn't fail
5926 * unless the file is corrupt.
5928 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5929 offsetInBigBlockFile
,
5930 bytesToReadInBuffer
,
5932 &bytesReadFromBigBlockFile
);
5938 * Step to the next big block.
5940 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5942 return STG_E_DOCFILECORRUPT
;
5944 bufferWalker
+= bytesReadFromBigBlockFile
;
5945 size
-= bytesReadFromBigBlockFile
;
5946 *bytesRead
+= bytesReadFromBigBlockFile
;
5947 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5950 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5953 /******************************************************************************
5954 * SmallBlockChainStream_WriteAt
5956 * Writes the specified number of bytes to this chain at the specified offset.
5957 * Will fail if not all specified number of bytes have been written.
5959 HRESULT
SmallBlockChainStream_WriteAt(
5960 SmallBlockChainStream
* This
,
5961 ULARGE_INTEGER offset
,
5964 ULONG
* bytesWritten
)
5966 ULARGE_INTEGER offsetInBigBlockFile
;
5967 ULONG blockNoInSequence
=
5968 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5970 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5971 ULONG bytesToWriteInBuffer
;
5973 ULONG bytesWrittenToBigBlockFile
;
5974 const BYTE
* bufferWalker
;
5978 * This should never happen on a small block file.
5980 assert(offset
.u
.HighPart
==0);
5983 * Find the first block in the stream that contains part of the buffer.
5985 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5987 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5989 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5990 return STG_E_DOCFILECORRUPT
;
5991 blockNoInSequence
--;
5995 * Start writing the buffer.
5998 bufferWalker
= buffer
;
5999 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6002 * Calculate how many bytes we can copy to this small block.
6004 bytesToWriteInBuffer
=
6005 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6008 * Calculate the offset of the small block in the small block file.
6010 offsetInBigBlockFile
.u
.HighPart
= 0;
6011 offsetInBigBlockFile
.u
.LowPart
=
6012 blockIndex
* This
->parentStorage
->smallBlockSize
;
6014 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6017 * Write those bytes in the buffer to the small block file.
6019 res
= BlockChainStream_WriteAt(
6020 This
->parentStorage
->smallBlockRootChain
,
6021 offsetInBigBlockFile
,
6022 bytesToWriteInBuffer
,
6024 &bytesWrittenToBigBlockFile
);
6029 * Step to the next big block.
6031 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6034 bufferWalker
+= bytesWrittenToBigBlockFile
;
6035 size
-= bytesWrittenToBigBlockFile
;
6036 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6037 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6040 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6043 /******************************************************************************
6044 * SmallBlockChainStream_Shrink
6046 * Shrinks this chain in the small block depot.
6048 static BOOL
SmallBlockChainStream_Shrink(
6049 SmallBlockChainStream
* This
,
6050 ULARGE_INTEGER newSize
)
6052 ULONG blockIndex
, extraBlock
;
6056 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6058 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6061 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6064 * Go to the new end of chain
6066 while (count
< numBlocks
)
6068 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6075 * If the count is 0, we have a special case, the head of the chain was
6080 DirEntry chainEntry
;
6082 StorageImpl_ReadDirEntry(This
->parentStorage
,
6083 This
->ownerDirEntry
,
6086 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6088 StorageImpl_WriteDirEntry(This
->parentStorage
,
6089 This
->ownerDirEntry
,
6093 * We start freeing the chain at the head block.
6095 extraBlock
= blockIndex
;
6099 /* Get the next block before marking the new end */
6100 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6104 /* Mark the new end of chain */
6105 SmallBlockChainStream_SetNextBlockInChain(
6108 BLOCK_END_OF_CHAIN
);
6112 * Mark the extra blocks as free
6114 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6116 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6119 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6120 extraBlock
= blockIndex
;
6126 /******************************************************************************
6127 * SmallBlockChainStream_Enlarge
6129 * Grows this chain in the small block depot.
6131 static BOOL
SmallBlockChainStream_Enlarge(
6132 SmallBlockChainStream
* This
,
6133 ULARGE_INTEGER newSize
)
6135 ULONG blockIndex
, currentBlock
;
6137 ULONG oldNumBlocks
= 0;
6139 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6142 * Empty chain. Create the head.
6144 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6146 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6147 SmallBlockChainStream_SetNextBlockInChain(
6150 BLOCK_END_OF_CHAIN
);
6152 if (This
->headOfStreamPlaceHolder
!= NULL
)
6154 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6158 DirEntry chainEntry
;
6160 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6163 chainEntry
.startingBlock
= blockIndex
;
6165 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6170 currentBlock
= blockIndex
;
6173 * Figure out how many blocks are needed to contain this stream
6175 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6177 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6181 * Go to the current end of chain
6183 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6186 currentBlock
= blockIndex
;
6187 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6192 * Add new blocks to the chain
6194 while (oldNumBlocks
< newNumBlocks
)
6196 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6197 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6199 SmallBlockChainStream_SetNextBlockInChain(
6202 BLOCK_END_OF_CHAIN
);
6204 currentBlock
= blockIndex
;
6211 /******************************************************************************
6212 * SmallBlockChainStream_SetSize
6214 * Sets the size of this stream.
6215 * The file will grow if we grow the chain.
6217 * TODO: Free the actual blocks in the file when we shrink the chain.
6218 * Currently, the blocks are still in the file. So the file size
6219 * doesn't shrink even if we shrink streams.
6221 BOOL
SmallBlockChainStream_SetSize(
6222 SmallBlockChainStream
* This
,
6223 ULARGE_INTEGER newSize
)
6225 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6227 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6230 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6232 SmallBlockChainStream_Shrink(This
, newSize
);
6236 SmallBlockChainStream_Enlarge(This
, newSize
);
6242 /******************************************************************************
6243 * SmallBlockChainStream_GetCount
6245 * Returns the number of small blocks that comprises this chain.
6246 * This is not the size of the stream as the last block may not be full!
6249 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6254 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6256 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6260 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6261 blockIndex
, &blockIndex
)))
6268 /******************************************************************************
6269 * SmallBlockChainStream_GetSize
6271 * Returns the size of this chain.
6273 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6275 DirEntry chainEntry
;
6277 if(This
->headOfStreamPlaceHolder
!= NULL
)
6279 ULARGE_INTEGER result
;
6280 result
.u
.HighPart
= 0;
6282 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6283 This
->parentStorage
->smallBlockSize
;
6288 StorageImpl_ReadDirEntry(
6289 This
->parentStorage
,
6290 This
->ownerDirEntry
,
6293 return chainEntry
.size
;
6296 /******************************************************************************
6297 * StgCreateDocfile [OLE32.@]
6298 * Creates a new compound file storage object
6301 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6302 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6303 * reserved [ ?] unused?, usually 0
6304 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6307 * S_OK if the file was successfully created
6308 * some STG_E_ value if error
6310 * if pwcsName is NULL, create file with new unique name
6311 * the function can returns
6312 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6315 HRESULT WINAPI
StgCreateDocfile(
6319 IStorage
**ppstgOpen
)
6321 StorageBaseImpl
* newStorage
= 0;
6322 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6323 HRESULT hr
= STG_E_INVALIDFLAG
;
6327 DWORD fileAttributes
;
6328 WCHAR tempFileName
[MAX_PATH
];
6330 TRACE("(%s, %x, %d, %p)\n",
6331 debugstr_w(pwcsName
), grfMode
,
6332 reserved
, ppstgOpen
);
6335 return STG_E_INVALIDPOINTER
;
6337 return STG_E_INVALIDPARAMETER
;
6339 /* if no share mode given then DENY_NONE is the default */
6340 if (STGM_SHARE_MODE(grfMode
) == 0)
6341 grfMode
|= STGM_SHARE_DENY_NONE
;
6343 if ( FAILED( validateSTGM(grfMode
) ))
6346 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6347 switch(STGM_ACCESS_MODE(grfMode
))
6350 case STGM_READWRITE
:
6356 /* in direct mode, can only use SHARE_EXCLUSIVE */
6357 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6360 /* but in transacted mode, any share mode is valid */
6363 * Generate a unique name.
6367 WCHAR tempPath
[MAX_PATH
];
6368 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6370 memset(tempPath
, 0, sizeof(tempPath
));
6371 memset(tempFileName
, 0, sizeof(tempFileName
));
6373 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6376 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6377 pwcsName
= tempFileName
;
6380 hr
= STG_E_INSUFFICIENTMEMORY
;
6384 creationMode
= TRUNCATE_EXISTING
;
6388 creationMode
= GetCreationModeFromSTGM(grfMode
);
6392 * Interpret the STGM value grfMode
6394 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6395 accessMode
= GetAccessModeFromSTGM(grfMode
);
6397 if (grfMode
& STGM_DELETEONRELEASE
)
6398 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6400 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6402 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6406 FIXME("Storage share mode not implemented.\n");
6411 hFile
= CreateFileW(pwcsName
,
6419 if (hFile
== INVALID_HANDLE_VALUE
)
6421 if(GetLastError() == ERROR_FILE_EXISTS
)
6422 hr
= STG_E_FILEALREADYEXISTS
;
6429 * Allocate and initialize the new IStorage32object.
6431 hr
= Storage_Construct(
6446 * Get an "out" pointer for the caller.
6448 *ppstgOpen
= (IStorage
*)newStorage
;
6451 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6456 /******************************************************************************
6457 * StgCreateStorageEx [OLE32.@]
6459 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6461 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6462 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6464 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6466 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6467 return STG_E_INVALIDPARAMETER
;
6470 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6472 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6473 return STG_E_INVALIDPARAMETER
;
6476 if (stgfmt
== STGFMT_FILE
)
6478 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6479 return STG_E_INVALIDPARAMETER
;
6482 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6484 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6485 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6488 ERR("Invalid stgfmt argument\n");
6489 return STG_E_INVALIDPARAMETER
;
6492 /******************************************************************************
6493 * StgCreatePropSetStg [OLE32.@]
6495 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6496 IPropertySetStorage
**ppPropSetStg
)
6500 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6502 hr
= STG_E_INVALIDPARAMETER
;
6504 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6505 (void**)ppPropSetStg
);
6509 /******************************************************************************
6510 * StgOpenStorageEx [OLE32.@]
6512 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6514 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6515 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6517 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6519 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6520 return STG_E_INVALIDPARAMETER
;
6526 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6527 return STG_E_INVALIDPARAMETER
;
6529 case STGFMT_STORAGE
:
6532 case STGFMT_DOCFILE
:
6533 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6535 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6536 return STG_E_INVALIDPARAMETER
;
6538 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6542 WARN("STGFMT_ANY assuming storage\n");
6546 return STG_E_INVALIDPARAMETER
;
6549 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6553 /******************************************************************************
6554 * StgOpenStorage [OLE32.@]
6556 HRESULT WINAPI
StgOpenStorage(
6557 const OLECHAR
*pwcsName
,
6558 IStorage
*pstgPriority
,
6562 IStorage
**ppstgOpen
)
6564 StorageBaseImpl
* newStorage
= 0;
6570 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6571 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6572 snbExclude
, reserved
, ppstgOpen
);
6576 hr
= STG_E_INVALIDNAME
;
6582 hr
= STG_E_INVALIDPOINTER
;
6588 hr
= STG_E_INVALIDPARAMETER
;
6592 if (grfMode
& STGM_PRIORITY
)
6594 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6595 return STG_E_INVALIDFLAG
;
6596 if (grfMode
& STGM_DELETEONRELEASE
)
6597 return STG_E_INVALIDFUNCTION
;
6598 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6599 return STG_E_INVALIDFLAG
;
6600 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6601 grfMode
|= STGM_SHARE_DENY_NONE
;
6603 /* STGM_PRIORITY stops other IStorage objects on the same file from
6604 * committing until the STGM_PRIORITY IStorage is closed. it also
6605 * stops non-transacted mode StgOpenStorage calls with write access from
6606 * succeeding. obviously, both of these cannot be achieved through just
6607 * file share flags */
6608 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6612 * Validate the sharing mode
6614 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6615 switch(STGM_SHARE_MODE(grfMode
))
6617 case STGM_SHARE_EXCLUSIVE
:
6618 case STGM_SHARE_DENY_WRITE
:
6621 hr
= STG_E_INVALIDFLAG
;
6625 if ( FAILED( validateSTGM(grfMode
) ) ||
6626 (grfMode
&STGM_CREATE
))
6628 hr
= STG_E_INVALIDFLAG
;
6632 /* shared reading requires transacted mode */
6633 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6634 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6635 !(grfMode
&STGM_TRANSACTED
) )
6637 hr
= STG_E_INVALIDFLAG
;
6642 * Interpret the STGM value grfMode
6644 shareMode
= GetShareModeFromSTGM(grfMode
);
6645 accessMode
= GetAccessModeFromSTGM(grfMode
);
6649 hFile
= CreateFileW( pwcsName
,
6654 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6657 if (hFile
==INVALID_HANDLE_VALUE
)
6659 DWORD last_error
= GetLastError();
6665 case ERROR_FILE_NOT_FOUND
:
6666 hr
= STG_E_FILENOTFOUND
;
6669 case ERROR_PATH_NOT_FOUND
:
6670 hr
= STG_E_PATHNOTFOUND
;
6673 case ERROR_ACCESS_DENIED
:
6674 case ERROR_WRITE_PROTECT
:
6675 hr
= STG_E_ACCESSDENIED
;
6678 case ERROR_SHARING_VIOLATION
:
6679 hr
= STG_E_SHAREVIOLATION
;
6690 * Refuse to open the file if it's too small to be a structured storage file
6691 * FIXME: verify the file when reading instead of here
6693 if (GetFileSize(hFile
, NULL
) < 0x100)
6696 hr
= STG_E_FILEALREADYEXISTS
;
6701 * Allocate and initialize the new IStorage32object.
6703 hr
= Storage_Construct(
6715 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6717 if(hr
== STG_E_INVALIDHEADER
)
6718 hr
= STG_E_FILEALREADYEXISTS
;
6723 * Get an "out" pointer for the caller.
6725 *ppstgOpen
= (IStorage
*)newStorage
;
6728 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6732 /******************************************************************************
6733 * StgCreateDocfileOnILockBytes [OLE32.@]
6735 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6739 IStorage
** ppstgOpen
)
6741 StorageBaseImpl
* newStorage
= 0;
6744 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6745 return STG_E_INVALIDPOINTER
;
6748 * Allocate and initialize the new IStorage object.
6750 hr
= Storage_Construct(
6765 * Get an "out" pointer for the caller.
6767 *ppstgOpen
= (IStorage
*)newStorage
;
6772 /******************************************************************************
6773 * StgOpenStorageOnILockBytes [OLE32.@]
6775 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6777 IStorage
*pstgPriority
,
6781 IStorage
**ppstgOpen
)
6783 StorageBaseImpl
* newStorage
= 0;
6786 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6787 return STG_E_INVALIDPOINTER
;
6789 if ( FAILED( validateSTGM(grfMode
) ))
6790 return STG_E_INVALIDFLAG
;
6795 * Allocate and initialize the new IStorage object.
6797 hr
= Storage_Construct(
6812 * Get an "out" pointer for the caller.
6814 *ppstgOpen
= (IStorage
*)newStorage
;
6819 /******************************************************************************
6820 * StgSetTimes [ole32.@]
6821 * StgSetTimes [OLE32.@]
6825 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6826 FILETIME
const *patime
, FILETIME
const *pmtime
)
6828 IStorage
*stg
= NULL
;
6831 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6833 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6837 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6838 IStorage_Release(stg
);
6844 /******************************************************************************
6845 * StgIsStorageILockBytes [OLE32.@]
6847 * Determines if the ILockBytes contains a storage object.
6849 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6852 ULARGE_INTEGER offset
;
6854 offset
.u
.HighPart
= 0;
6855 offset
.u
.LowPart
= 0;
6857 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6859 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6865 /******************************************************************************
6866 * WriteClassStg [OLE32.@]
6868 * This method will store the specified CLSID in the specified storage object
6870 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6875 return E_INVALIDARG
;
6878 return STG_E_INVALIDPOINTER
;
6880 hRes
= IStorage_SetClass(pStg
, rclsid
);
6885 /***********************************************************************
6886 * ReadClassStg (OLE32.@)
6888 * This method reads the CLSID previously written to a storage object with
6889 * the WriteClassStg.
6892 * pstg [I] IStorage pointer
6893 * pclsid [O] Pointer to where the CLSID is written
6897 * Failure: HRESULT code.
6899 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6904 TRACE("(%p, %p)\n", pstg
, pclsid
);
6906 if(!pstg
|| !pclsid
)
6907 return E_INVALIDARG
;
6910 * read a STATSTG structure (contains the clsid) from the storage
6912 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6915 *pclsid
=pstatstg
.clsid
;
6920 /***********************************************************************
6921 * OleLoadFromStream (OLE32.@)
6923 * This function loads an object from stream
6925 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6929 LPPERSISTSTREAM xstm
;
6931 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6933 res
=ReadClassStm(pStm
,&clsid
);
6936 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6939 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6941 IUnknown_Release((IUnknown
*)*ppvObj
);
6944 res
=IPersistStream_Load(xstm
,pStm
);
6945 IPersistStream_Release(xstm
);
6946 /* FIXME: all refcounts ok at this point? I think they should be:
6949 * xstm : 0 (released)
6954 /***********************************************************************
6955 * OleSaveToStream (OLE32.@)
6957 * This function saves an object with the IPersistStream interface on it
6958 * to the specified stream.
6960 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6966 TRACE("(%p,%p)\n",pPStm
,pStm
);
6968 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6970 if (SUCCEEDED(res
)){
6972 res
=WriteClassStm(pStm
,&clsid
);
6976 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6979 TRACE("Finished Save\n");
6983 /****************************************************************************
6984 * This method validate a STGM parameter that can contain the values below
6986 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6987 * The stgm values contained in 0xffff0000 are bitmasks.
6989 * STGM_DIRECT 0x00000000
6990 * STGM_TRANSACTED 0x00010000
6991 * STGM_SIMPLE 0x08000000
6993 * STGM_READ 0x00000000
6994 * STGM_WRITE 0x00000001
6995 * STGM_READWRITE 0x00000002
6997 * STGM_SHARE_DENY_NONE 0x00000040
6998 * STGM_SHARE_DENY_READ 0x00000030
6999 * STGM_SHARE_DENY_WRITE 0x00000020
7000 * STGM_SHARE_EXCLUSIVE 0x00000010
7002 * STGM_PRIORITY 0x00040000
7003 * STGM_DELETEONRELEASE 0x04000000
7005 * STGM_CREATE 0x00001000
7006 * STGM_CONVERT 0x00020000
7007 * STGM_FAILIFTHERE 0x00000000
7009 * STGM_NOSCRATCH 0x00100000
7010 * STGM_NOSNAPSHOT 0x00200000
7012 static HRESULT
validateSTGM(DWORD stgm
)
7014 DWORD access
= STGM_ACCESS_MODE(stgm
);
7015 DWORD share
= STGM_SHARE_MODE(stgm
);
7016 DWORD create
= STGM_CREATE_MODE(stgm
);
7018 if (stgm
&~STGM_KNOWN_FLAGS
)
7020 ERR("unknown flags %08x\n", stgm
);
7028 case STGM_READWRITE
:
7036 case STGM_SHARE_DENY_NONE
:
7037 case STGM_SHARE_DENY_READ
:
7038 case STGM_SHARE_DENY_WRITE
:
7039 case STGM_SHARE_EXCLUSIVE
:
7048 case STGM_FAILIFTHERE
:
7055 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7057 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7061 * STGM_CREATE | STGM_CONVERT
7062 * if both are false, STGM_FAILIFTHERE is set to TRUE
7064 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7068 * STGM_NOSCRATCH requires STGM_TRANSACTED
7070 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7074 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7075 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7077 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7078 (!(stgm
& STGM_TRANSACTED
) ||
7079 share
== STGM_SHARE_EXCLUSIVE
||
7080 share
== STGM_SHARE_DENY_WRITE
) )
7086 /****************************************************************************
7087 * GetShareModeFromSTGM
7089 * This method will return a share mode flag from a STGM value.
7090 * The STGM value is assumed valid.
7092 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7094 switch (STGM_SHARE_MODE(stgm
))
7096 case STGM_SHARE_DENY_NONE
:
7097 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7098 case STGM_SHARE_DENY_READ
:
7099 return FILE_SHARE_WRITE
;
7100 case STGM_SHARE_DENY_WRITE
:
7101 return FILE_SHARE_READ
;
7102 case STGM_SHARE_EXCLUSIVE
:
7105 ERR("Invalid share mode!\n");
7110 /****************************************************************************
7111 * GetAccessModeFromSTGM
7113 * This method will return an access mode flag from a STGM value.
7114 * The STGM value is assumed valid.
7116 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7118 switch (STGM_ACCESS_MODE(stgm
))
7121 return GENERIC_READ
;
7123 case STGM_READWRITE
:
7124 return GENERIC_READ
| GENERIC_WRITE
;
7126 ERR("Invalid access mode!\n");
7131 /****************************************************************************
7132 * GetCreationModeFromSTGM
7134 * This method will return a creation mode flag from a STGM value.
7135 * The STGM value is assumed valid.
7137 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7139 switch(STGM_CREATE_MODE(stgm
))
7142 return CREATE_ALWAYS
;
7144 FIXME("STGM_CONVERT not implemented!\n");
7146 case STGM_FAILIFTHERE
:
7149 ERR("Invalid create mode!\n");
7155 /*************************************************************************
7156 * OLECONVERT_LoadOLE10 [Internal]
7158 * Loads the OLE10 STREAM to memory
7161 * pOleStream [I] The OLESTREAM
7162 * pData [I] Data Structure for the OLESTREAM Data
7166 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7167 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7170 * This function is used by OleConvertOLESTREAMToIStorage only.
7172 * Memory allocated for pData must be freed by the caller
7174 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7177 HRESULT hRes
= S_OK
;
7181 pData
->pData
= NULL
;
7182 pData
->pstrOleObjFileName
= NULL
;
7184 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7187 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7188 if(dwSize
!= sizeof(pData
->dwOleID
))
7190 hRes
= CONVERT10_E_OLESTREAM_GET
;
7192 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7194 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7205 /* Get the TypeID... more info needed for this field */
7206 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7207 if(dwSize
!= sizeof(pData
->dwTypeID
))
7209 hRes
= CONVERT10_E_OLESTREAM_GET
;
7214 if(pData
->dwTypeID
!= 0)
7216 /* Get the length of the OleTypeName */
7217 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7218 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7220 hRes
= CONVERT10_E_OLESTREAM_GET
;
7225 if(pData
->dwOleTypeNameLength
> 0)
7227 /* Get the OleTypeName */
7228 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7229 if(dwSize
!= pData
->dwOleTypeNameLength
)
7231 hRes
= CONVERT10_E_OLESTREAM_GET
;
7237 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7238 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7240 hRes
= CONVERT10_E_OLESTREAM_GET
;
7244 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7245 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7246 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7247 if(pData
->pstrOleObjFileName
)
7249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7250 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7252 hRes
= CONVERT10_E_OLESTREAM_GET
;
7256 hRes
= CONVERT10_E_OLESTREAM_GET
;
7261 /* Get the Width of the Metafile */
7262 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7263 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7265 hRes
= CONVERT10_E_OLESTREAM_GET
;
7269 /* Get the Height of the Metafile */
7270 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7271 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7273 hRes
= CONVERT10_E_OLESTREAM_GET
;
7279 /* Get the Length of the Data */
7280 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7281 if(dwSize
!= sizeof(pData
->dwDataLength
))
7283 hRes
= CONVERT10_E_OLESTREAM_GET
;
7287 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7289 if(!bStrem1
) /* if it is a second OLE stream data */
7291 pData
->dwDataLength
-= 8;
7292 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7293 if(dwSize
!= sizeof(pData
->strUnknown
))
7295 hRes
= CONVERT10_E_OLESTREAM_GET
;
7301 if(pData
->dwDataLength
> 0)
7303 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7305 /* Get Data (ex. IStorage, Metafile, or BMP) */
7308 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7309 if(dwSize
!= pData
->dwDataLength
)
7311 hRes
= CONVERT10_E_OLESTREAM_GET
;
7316 hRes
= CONVERT10_E_OLESTREAM_GET
;
7325 /*************************************************************************
7326 * OLECONVERT_SaveOLE10 [Internal]
7328 * Saves the OLE10 STREAM From memory
7331 * pData [I] Data Structure for the OLESTREAM Data
7332 * pOleStream [I] The OLESTREAM to save
7336 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7339 * This function is used by OleConvertIStorageToOLESTREAM only.
7342 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7345 HRESULT hRes
= S_OK
;
7349 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7350 if(dwSize
!= sizeof(pData
->dwOleID
))
7352 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7357 /* Set the TypeID */
7358 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7359 if(dwSize
!= sizeof(pData
->dwTypeID
))
7361 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7365 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7367 /* Set the Length of the OleTypeName */
7368 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7369 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7371 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7376 if(pData
->dwOleTypeNameLength
> 0)
7378 /* Set the OleTypeName */
7379 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7380 if(dwSize
!= pData
->dwOleTypeNameLength
)
7382 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7389 /* Set the width of the Metafile */
7390 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7391 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7393 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7399 /* Set the height of the Metafile */
7400 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7401 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7403 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7409 /* Set the length of the Data */
7410 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7411 if(dwSize
!= sizeof(pData
->dwDataLength
))
7413 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7419 if(pData
->dwDataLength
> 0)
7421 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7422 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7423 if(dwSize
!= pData
->dwDataLength
)
7425 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7433 /*************************************************************************
7434 * OLECONVERT_GetOLE20FromOLE10[Internal]
7436 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7437 * opens it, and copies the content to the dest IStorage for
7438 * OleConvertOLESTREAMToIStorage
7442 * pDestStorage [I] The IStorage to copy the data to
7443 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7444 * nBufferLength [I] The size of the buffer
7453 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7457 IStorage
*pTempStorage
;
7458 DWORD dwNumOfBytesWritten
;
7459 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7460 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7462 /* Create a temp File */
7463 GetTempPathW(MAX_PATH
, wstrTempDir
);
7464 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7465 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7467 if(hFile
!= INVALID_HANDLE_VALUE
)
7469 /* Write IStorage Data to File */
7470 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7473 /* Open and copy temp storage to the Dest Storage */
7474 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7477 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7478 IStorage_Release(pTempStorage
);
7480 DeleteFileW(wstrTempFile
);
7485 /*************************************************************************
7486 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7488 * Saves the OLE10 STREAM From memory
7491 * pStorage [I] The Src IStorage to copy
7492 * pData [I] The Dest Memory to write to.
7495 * The size in bytes allocated for pData
7498 * Memory allocated for pData must be freed by the caller
7500 * Used by OleConvertIStorageToOLESTREAM only.
7503 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7507 DWORD nDataLength
= 0;
7508 IStorage
*pTempStorage
;
7509 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7510 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7514 /* Create temp Storage */
7515 GetTempPathW(MAX_PATH
, wstrTempDir
);
7516 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7517 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7521 /* Copy Src Storage to the Temp Storage */
7522 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7523 IStorage_Release(pTempStorage
);
7525 /* Open Temp Storage as a file and copy to memory */
7526 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7527 if(hFile
!= INVALID_HANDLE_VALUE
)
7529 nDataLength
= GetFileSize(hFile
, NULL
);
7530 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7531 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7534 DeleteFileW(wstrTempFile
);
7539 /*************************************************************************
7540 * OLECONVERT_CreateOleStream [Internal]
7542 * Creates the "\001OLE" stream in the IStorage if necessary.
7545 * pStorage [I] Dest storage to create the stream in
7551 * This function is used by OleConvertOLESTREAMToIStorage only.
7553 * This stream is still unknown, MS Word seems to have extra data
7554 * but since the data is stored in the OLESTREAM there should be
7555 * no need to recreate the stream. If the stream is manually
7556 * deleted it will create it with this default data.
7559 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7563 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7564 BYTE pOleStreamHeader
[] =
7566 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7568 0x00, 0x00, 0x00, 0x00
7571 /* Create stream if not present */
7572 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7573 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7577 /* Write default Data */
7578 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7579 IStream_Release(pStream
);
7583 /* write a string to a stream, preceded by its length */
7584 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7591 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7592 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7597 str
= CoTaskMemAlloc( len
);
7598 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7599 r
= IStream_Write( stm
, str
, len
, NULL
);
7600 CoTaskMemFree( str
);
7604 /* read a string preceded by its length from a stream */
7605 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7608 DWORD len
, count
= 0;
7612 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7615 if( count
!= sizeof(len
) )
7616 return E_OUTOFMEMORY
;
7618 TRACE("%d bytes\n",len
);
7620 str
= CoTaskMemAlloc( len
);
7622 return E_OUTOFMEMORY
;
7624 r
= IStream_Read( stm
, str
, len
, &count
);
7629 CoTaskMemFree( str
);
7630 return E_OUTOFMEMORY
;
7633 TRACE("Read string %s\n",debugstr_an(str
,len
));
7635 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7636 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7638 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7639 CoTaskMemFree( str
);
7647 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7648 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7652 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7654 static const BYTE unknown1
[12] =
7655 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7656 0xFF, 0xFF, 0xFF, 0xFF};
7657 static const BYTE unknown2
[16] =
7658 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7659 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7661 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7662 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7663 debugstr_w(szProgIDName
));
7665 /* Create a CompObj stream */
7666 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7667 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7671 /* Write CompObj Structure to stream */
7672 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7674 if( SUCCEEDED( r
) )
7675 r
= WriteClassStm( pstm
, clsid
);
7677 if( SUCCEEDED( r
) )
7678 r
= STREAM_WriteString( pstm
, lpszUserType
);
7679 if( SUCCEEDED( r
) )
7680 r
= STREAM_WriteString( pstm
, szClipName
);
7681 if( SUCCEEDED( r
) )
7682 r
= STREAM_WriteString( pstm
, szProgIDName
);
7683 if( SUCCEEDED( r
) )
7684 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7686 IStream_Release( pstm
);
7691 /***********************************************************************
7692 * WriteFmtUserTypeStg (OLE32.@)
7694 HRESULT WINAPI
WriteFmtUserTypeStg(
7695 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7698 WCHAR szwClipName
[0x40];
7699 CLSID clsid
= CLSID_NULL
;
7700 LPWSTR wstrProgID
= NULL
;
7703 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7705 /* get the clipboard format name */
7706 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7709 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7711 /* FIXME: There's room to save a CLSID and its ProgID, but
7712 the CLSID is not looked up in the registry and in all the
7713 tests I wrote it was CLSID_NULL. Where does it come from?
7716 /* get the real program ID. This may fail, but that's fine */
7717 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7719 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7721 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7722 lpszUserType
, szwClipName
, wstrProgID
);
7724 CoTaskMemFree(wstrProgID
);
7730 /******************************************************************************
7731 * ReadFmtUserTypeStg [OLE32.@]
7733 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7737 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7738 unsigned char unknown1
[12];
7739 unsigned char unknown2
[16];
7741 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7744 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7746 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7747 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7750 WARN("Failed to open stream r = %08x\n", r
);
7754 /* read the various parts of the structure */
7755 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7756 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7758 r
= ReadClassStm( stm
, &clsid
);
7762 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7766 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7770 r
= STREAM_ReadString( stm
, &szProgIDName
);
7774 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7775 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7778 /* ok, success... now we just need to store what we found */
7780 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7781 CoTaskMemFree( szOleTypeName
);
7783 if( lplpszUserType
)
7784 *lplpszUserType
= szCLSIDName
;
7785 CoTaskMemFree( szProgIDName
);
7788 IStream_Release( stm
);
7794 /*************************************************************************
7795 * OLECONVERT_CreateCompObjStream [Internal]
7797 * Creates a "\001CompObj" is the destination IStorage if necessary.
7800 * pStorage [I] The dest IStorage to create the CompObj Stream
7802 * strOleTypeName [I] The ProgID
7806 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7809 * This function is used by OleConvertOLESTREAMToIStorage only.
7811 * The stream data is stored in the OLESTREAM and there should be
7812 * no need to recreate the stream. If the stream is manually
7813 * deleted it will attempt to create it by querying the registry.
7817 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7820 HRESULT hStorageRes
, hRes
= S_OK
;
7821 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7822 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7823 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7825 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7826 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7828 /* Initialize the CompObj structure */
7829 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7830 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7831 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7834 /* Create a CompObj stream if it doesn't exist */
7835 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7836 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7837 if(hStorageRes
== S_OK
)
7839 /* copy the OleTypeName to the compobj struct */
7840 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7841 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7843 /* copy the OleTypeName to the compobj struct */
7844 /* Note: in the test made, these were Identical */
7845 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7846 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7849 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7850 bufferW
, OLESTREAM_MAX_STR_LEN
);
7851 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7857 /* Get the CLSID Default Name from the Registry */
7858 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7859 if(hErr
== ERROR_SUCCESS
)
7861 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7862 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7863 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7864 if(hErr
== ERROR_SUCCESS
)
7866 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7872 /* Write CompObj Structure to stream */
7873 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7875 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7877 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7878 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7880 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7882 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7883 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7885 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7887 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7888 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7890 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7892 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7893 IStream_Release(pStream
);
7899 /*************************************************************************
7900 * OLECONVERT_CreateOlePresStream[Internal]
7902 * Creates the "\002OlePres000" Stream with the Metafile data
7905 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7906 * dwExtentX [I] Width of the Metafile
7907 * dwExtentY [I] Height of the Metafile
7908 * pData [I] Metafile data
7909 * dwDataLength [I] Size of the Metafile data
7913 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7916 * This function is used by OleConvertOLESTREAMToIStorage only.
7919 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7923 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7924 BYTE pOlePresStreamHeader
[] =
7926 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7927 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7928 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7929 0x00, 0x00, 0x00, 0x00
7932 BYTE pOlePresStreamHeaderEmpty
[] =
7934 0x00, 0x00, 0x00, 0x00,
7935 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7936 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7937 0x00, 0x00, 0x00, 0x00
7940 /* Create the OlePres000 Stream */
7941 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7942 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7947 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7949 memset(&OlePres
, 0, sizeof(OlePres
));
7950 /* Do we have any metafile data to save */
7951 if(dwDataLength
> 0)
7953 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7954 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7958 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7959 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7961 /* Set width and height of the metafile */
7962 OlePres
.dwExtentX
= dwExtentX
;
7963 OlePres
.dwExtentY
= -dwExtentY
;
7965 /* Set Data and Length */
7966 if(dwDataLength
> sizeof(METAFILEPICT16
))
7968 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7969 OlePres
.pData
= &(pData
[8]);
7971 /* Save OlePres000 Data to Stream */
7972 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7973 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7974 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7975 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7976 if(OlePres
.dwSize
> 0)
7978 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7980 IStream_Release(pStream
);
7984 /*************************************************************************
7985 * OLECONVERT_CreateOle10NativeStream [Internal]
7987 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7990 * pStorage [I] Dest storage to create the stream in
7991 * pData [I] Ole10 Native Data (ex. bmp)
7992 * dwDataLength [I] Size of the Ole10 Native Data
7998 * This function is used by OleConvertOLESTREAMToIStorage only.
8000 * Might need to verify the data and return appropriate error message
8003 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8007 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8009 /* Create the Ole10Native Stream */
8010 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8011 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8015 /* Write info to stream */
8016 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8017 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8018 IStream_Release(pStream
);
8023 /*************************************************************************
8024 * OLECONVERT_GetOLE10ProgID [Internal]
8026 * Finds the ProgID (or OleTypeID) from the IStorage
8029 * pStorage [I] The Src IStorage to get the ProgID
8030 * strProgID [I] the ProgID string to get
8031 * dwSize [I] the size of the string
8035 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8038 * This function is used by OleConvertIStorageToOLESTREAM only.
8042 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8046 LARGE_INTEGER iSeekPos
;
8047 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8048 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8050 /* Open the CompObj Stream */
8051 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8052 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8056 /*Get the OleType from the CompObj Stream */
8057 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8058 iSeekPos
.u
.HighPart
= 0;
8060 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8061 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8062 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8063 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8064 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8065 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8066 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8068 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8071 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8073 IStream_Release(pStream
);
8078 LPOLESTR wstrProgID
;
8080 /* Get the OleType from the registry */
8081 REFCLSID clsid
= &(stat
.clsid
);
8082 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8083 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8086 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8093 /*************************************************************************
8094 * OLECONVERT_GetOle10PresData [Internal]
8096 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8099 * pStorage [I] Src IStroage
8100 * pOleStream [I] Dest OleStream Mem Struct
8106 * This function is used by OleConvertIStorageToOLESTREAM only.
8108 * Memory allocated for pData must be freed by the caller
8112 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8117 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8119 /* Initialize Default data for OLESTREAM */
8120 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8121 pOleStreamData
[0].dwTypeID
= 2;
8122 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8123 pOleStreamData
[1].dwTypeID
= 0;
8124 pOleStreamData
[0].dwMetaFileWidth
= 0;
8125 pOleStreamData
[0].dwMetaFileHeight
= 0;
8126 pOleStreamData
[0].pData
= NULL
;
8127 pOleStreamData
[1].pData
= NULL
;
8129 /* Open Ole10Native Stream */
8130 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8131 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8135 /* Read Size and Data */
8136 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8137 if(pOleStreamData
->dwDataLength
> 0)
8139 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8140 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8142 IStream_Release(pStream
);
8148 /*************************************************************************
8149 * OLECONVERT_GetOle20PresData[Internal]
8151 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8154 * pStorage [I] Src IStroage
8155 * pOleStreamData [I] Dest OleStream Mem Struct
8161 * This function is used by OleConvertIStorageToOLESTREAM only.
8163 * Memory allocated for pData must be freed by the caller
8165 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8169 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8170 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8172 /* Initialize Default data for OLESTREAM */
8173 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8174 pOleStreamData
[0].dwTypeID
= 2;
8175 pOleStreamData
[0].dwMetaFileWidth
= 0;
8176 pOleStreamData
[0].dwMetaFileHeight
= 0;
8177 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8178 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8179 pOleStreamData
[1].dwTypeID
= 0;
8180 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8181 pOleStreamData
[1].strOleTypeName
[0] = 0;
8182 pOleStreamData
[1].dwMetaFileWidth
= 0;
8183 pOleStreamData
[1].dwMetaFileHeight
= 0;
8184 pOleStreamData
[1].pData
= NULL
;
8185 pOleStreamData
[1].dwDataLength
= 0;
8188 /* Open OlePress000 stream */
8189 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8190 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8193 LARGE_INTEGER iSeekPos
;
8194 METAFILEPICT16 MetaFilePict
;
8195 static const char strMetafilePictName
[] = "METAFILEPICT";
8197 /* Set the TypeID for a Metafile */
8198 pOleStreamData
[1].dwTypeID
= 5;
8200 /* Set the OleTypeName to Metafile */
8201 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8202 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8204 iSeekPos
.u
.HighPart
= 0;
8205 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8207 /* Get Presentation Data */
8208 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8209 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8210 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8211 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8213 /*Set width and Height */
8214 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8215 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8216 if(olePress
.dwSize
> 0)
8219 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8221 /* Set MetaFilePict struct */
8222 MetaFilePict
.mm
= 8;
8223 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8224 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8225 MetaFilePict
.hMF
= 0;
8227 /* Get Metafile Data */
8228 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8229 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8230 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8232 IStream_Release(pStream
);
8236 /*************************************************************************
8237 * OleConvertOLESTREAMToIStorage [OLE32.@]
8242 * DVTARGETDEVICE parameter is not handled
8243 * Still unsure of some mem fields for OLE 10 Stream
8244 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8245 * and "\001OLE" streams
8248 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8249 LPOLESTREAM pOleStream
,
8251 const DVTARGETDEVICE
* ptd
)
8255 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8257 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8259 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8263 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8266 if(pstg
== NULL
|| pOleStream
== NULL
)
8268 hRes
= E_INVALIDARG
;
8273 /* Load the OLESTREAM to Memory */
8274 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8279 /* Load the OLESTREAM to Memory (part 2)*/
8280 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8286 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8288 /* Do we have the IStorage Data in the OLESTREAM */
8289 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8291 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8292 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8296 /* It must be an original OLE 1.0 source */
8297 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8302 /* It must be an original OLE 1.0 source */
8303 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8306 /* Create CompObj Stream if necessary */
8307 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8310 /*Create the Ole Stream if necessary */
8311 OLECONVERT_CreateOleStream(pstg
);
8316 /* Free allocated memory */
8317 for(i
=0; i
< 2; i
++)
8319 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8320 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8321 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8326 /*************************************************************************
8327 * OleConvertIStorageToOLESTREAM [OLE32.@]
8334 * Still unsure of some mem fields for OLE 10 Stream
8335 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8336 * and "\001OLE" streams.
8339 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8341 LPOLESTREAM pOleStream
)
8344 HRESULT hRes
= S_OK
;
8346 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8347 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8349 TRACE("%p %p\n", pstg
, pOleStream
);
8351 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8353 if(pstg
== NULL
|| pOleStream
== NULL
)
8355 hRes
= E_INVALIDARG
;
8359 /* Get the ProgID */
8360 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8361 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8365 /* Was it originally Ole10 */
8366 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8369 IStream_Release(pStream
);
8370 /* Get Presentation Data for Ole10Native */
8371 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8375 /* Get Presentation Data (OLE20) */
8376 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8379 /* Save OLESTREAM */
8380 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8383 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8388 /* Free allocated memory */
8389 for(i
=0; i
< 2; i
++)
8391 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8397 /***********************************************************************
8398 * GetConvertStg (OLE32.@)
8400 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8401 FIXME("unimplemented stub!\n");
8405 /******************************************************************************
8406 * StgIsStorageFile [OLE32.@]
8407 * Verify if the file contains a storage object
8413 * S_OK if file has magic bytes as a storage object
8414 * S_FALSE if file is not storage
8417 StgIsStorageFile(LPCOLESTR fn
)
8423 TRACE("%s\n", debugstr_w(fn
));
8424 hf
= CreateFileW(fn
, GENERIC_READ
,
8425 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8426 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8428 if (hf
== INVALID_HANDLE_VALUE
)
8429 return STG_E_FILENOTFOUND
;
8431 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8433 WARN(" unable to read file\n");
8440 if (bytes_read
!= 8) {
8441 TRACE(" too short\n");
8445 if (!memcmp(magic
,STORAGE_magic
,8)) {
8450 TRACE(" -> Invalid header.\n");
8454 /***********************************************************************
8455 * WriteClassStm (OLE32.@)
8457 * Writes a CLSID to a stream.
8460 * pStm [I] Stream to write to.
8461 * rclsid [I] CLSID to write.
8465 * Failure: HRESULT code.
8467 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8469 TRACE("(%p,%p)\n",pStm
,rclsid
);
8471 if (!pStm
|| !rclsid
)
8472 return E_INVALIDARG
;
8474 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8477 /***********************************************************************
8478 * ReadClassStm (OLE32.@)
8480 * Reads a CLSID from a stream.
8483 * pStm [I] Stream to read from.
8484 * rclsid [O] CLSID to read.
8488 * Failure: HRESULT code.
8490 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8495 TRACE("(%p,%p)\n",pStm
,pclsid
);
8497 if (!pStm
|| !pclsid
)
8498 return E_INVALIDARG
;
8500 /* clear the output args */
8501 *pclsid
= CLSID_NULL
;
8503 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8508 if (nbByte
!= sizeof(CLSID
))
8509 return STG_E_READFAULT
;