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(
2587 StorageImpl
** result
)
2591 DirEntry currentEntry
;
2592 DirRef currentEntryRef
;
2593 WCHAR fullpath
[MAX_PATH
];
2595 if ( FAILED( validateSTGM(openFlags
) ))
2596 return STG_E_INVALIDFLAG
;
2598 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2600 return E_OUTOFMEMORY
;
2602 memset(This
, 0, sizeof(StorageImpl
));
2604 list_init(&This
->base
.strmHead
);
2606 list_init(&This
->base
.storageHead
);
2608 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2609 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2610 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2611 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2613 This
->base
.create
= create
;
2615 This
->base
.reverted
= 0;
2617 This
->hFile
= hFile
;
2620 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2622 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2624 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2625 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2626 if (!This
->pwcsName
)
2628 hr
= STG_E_INSUFFICIENTMEMORY
;
2631 strcpyW(This
->pwcsName
, fullpath
);
2632 This
->base
.filename
= This
->pwcsName
;
2636 * Initialize the big block cache.
2638 This
->bigBlockSize
= sector_size
;
2639 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2640 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2645 if (This
->bigBlockFile
== 0)
2653 ULARGE_INTEGER size
;
2654 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2657 * Initialize all header variables:
2658 * - The big block depot consists of one block and it is at block 0
2659 * - The directory table starts at block 1
2660 * - There is no small block depot
2662 memset( This
->bigBlockDepotStart
,
2664 sizeof(This
->bigBlockDepotStart
));
2666 This
->bigBlockDepotCount
= 1;
2667 This
->bigBlockDepotStart
[0] = 0;
2668 This
->rootStartBlock
= 1;
2669 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2670 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2671 if (sector_size
== 4096)
2672 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2674 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2675 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2676 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2677 This
->extBigBlockDepotCount
= 0;
2679 StorageImpl_SaveFileHeader(This
);
2682 * Add one block for the big block depot and one block for the directory table
2684 size
.u
.HighPart
= 0;
2685 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2686 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2689 * Initialize the big block depot
2691 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2692 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2693 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2694 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2699 * Load the header for the file.
2701 hr
= StorageImpl_LoadFileHeader(This
);
2710 * There is no block depot cached yet.
2712 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2715 * Start searching for free blocks with block 0.
2717 This
->prevFreeBlock
= 0;
2719 This
->firstFreeSmallBlock
= 0;
2722 * Create the block chain abstractions.
2724 if(!(This
->rootBlockChain
=
2725 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2727 hr
= STG_E_READFAULT
;
2731 if(!(This
->smallBlockDepotChain
=
2732 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2735 hr
= STG_E_READFAULT
;
2740 * Write the root storage entry (memory only)
2746 * Initialize the directory table
2748 memset(&rootEntry
, 0, sizeof(rootEntry
));
2749 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2750 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2751 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2752 rootEntry
.stgType
= STGTY_ROOT
;
2753 rootEntry
.leftChild
= DIRENTRY_NULL
;
2754 rootEntry
.rightChild
= DIRENTRY_NULL
;
2755 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2756 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2757 rootEntry
.size
.u
.HighPart
= 0;
2758 rootEntry
.size
.u
.LowPart
= 0;
2760 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2764 * Find the ID of the root storage.
2766 currentEntryRef
= 0;
2770 hr
= StorageImpl_ReadDirEntry(
2777 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2778 (currentEntry
.stgType
== STGTY_ROOT
) )
2780 This
->base
.storageDirEntry
= currentEntryRef
;
2786 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2790 hr
= STG_E_READFAULT
;
2795 * Create the block chain abstraction for the small block root chain.
2797 if(!(This
->smallBlockRootChain
=
2798 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2800 hr
= STG_E_READFAULT
;
2806 IStorage_Release((IStorage
*)This
);
2815 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2817 StorageImpl
*This
= (StorageImpl
*) iface
;
2819 StorageBaseImpl_DeleteAll(&This
->base
);
2821 This
->base
.reverted
= 1;
2824 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2826 StorageImpl
*This
= (StorageImpl
*) iface
;
2828 TRACE("(%p)\n", This
);
2830 StorageImpl_Invalidate(iface
);
2832 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2834 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2835 BlockChainStream_Destroy(This
->rootBlockChain
);
2836 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2838 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2839 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2841 if (This
->bigBlockFile
)
2842 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2843 HeapFree(GetProcessHeap(), 0, This
);
2846 /******************************************************************************
2847 * Storage32Impl_GetNextFreeBigBlock
2849 * Returns the index of the next free big block.
2850 * If the big block depot is filled, this method will enlarge it.
2853 static ULONG
StorageImpl_GetNextFreeBigBlock(
2856 ULONG depotBlockIndexPos
;
2857 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2859 ULONG depotBlockOffset
;
2860 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2861 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2863 ULONG freeBlock
= BLOCK_UNUSED
;
2864 ULARGE_INTEGER neededSize
;
2866 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2867 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2870 * Scan the entire big block depot until we find a block marked free
2872 while (nextBlockIndex
!= BLOCK_UNUSED
)
2874 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2876 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2879 * Grow the primary depot.
2881 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2883 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2886 * Add a block depot.
2888 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2889 This
->bigBlockDepotCount
++;
2890 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2893 * Flag it as a block depot.
2895 StorageImpl_SetNextBlockInChain(This
,
2899 /* Save new header information.
2901 StorageImpl_SaveFileHeader(This
);
2906 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2908 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2911 * Grow the extended depot.
2913 ULONG extIndex
= BLOCK_UNUSED
;
2914 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2915 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2917 if (extBlockOffset
== 0)
2919 /* We need an extended block.
2921 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2922 This
->extBigBlockDepotCount
++;
2923 depotBlockIndexPos
= extIndex
+ 1;
2926 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2929 * Add a block depot and mark it in the extended block.
2931 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2932 This
->bigBlockDepotCount
++;
2933 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2935 /* Flag the block depot.
2937 StorageImpl_SetNextBlockInChain(This
,
2941 /* If necessary, flag the extended depot block.
2943 if (extIndex
!= BLOCK_UNUSED
)
2944 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2946 /* Save header information.
2948 StorageImpl_SaveFileHeader(This
);
2952 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2956 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2957 ( nextBlockIndex
!= BLOCK_UNUSED
))
2959 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2961 if (nextBlockIndex
== BLOCK_UNUSED
)
2963 freeBlock
= (depotIndex
* blocksPerDepot
) +
2964 (depotBlockOffset
/sizeof(ULONG
));
2967 depotBlockOffset
+= sizeof(ULONG
);
2972 depotBlockOffset
= 0;
2976 * make sure that the block physically exists before using it
2978 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
2979 BIGBLOCKFILE_Expand(This
->bigBlockFile
, neededSize
);
2981 This
->prevFreeBlock
= freeBlock
;
2986 /******************************************************************************
2987 * Storage32Impl_AddBlockDepot
2989 * This will create a depot block, essentially it is a block initialized
2992 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2994 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
2997 * Initialize blocks as free
2999 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3000 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3003 /******************************************************************************
3004 * Storage32Impl_GetExtDepotBlock
3006 * Returns the index of the block that corresponds to the specified depot
3007 * index. This method is only for depot indexes equal or greater than
3008 * COUNT_BBDEPOTINHEADER.
3010 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3012 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3013 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3014 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3015 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3016 ULONG blockIndex
= BLOCK_UNUSED
;
3017 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3019 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3021 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3022 return BLOCK_UNUSED
;
3024 while (extBlockCount
> 0)
3026 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3030 if (extBlockIndex
!= BLOCK_UNUSED
)
3031 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3032 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3037 /******************************************************************************
3038 * Storage32Impl_SetExtDepotBlock
3040 * Associates the specified block index to the specified depot index.
3041 * This method is only for depot indexes equal or greater than
3042 * COUNT_BBDEPOTINHEADER.
3044 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3046 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3047 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3048 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3049 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3050 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3052 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3054 while (extBlockCount
> 0)
3056 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3060 if (extBlockIndex
!= BLOCK_UNUSED
)
3062 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3063 extBlockOffset
* sizeof(ULONG
),
3068 /******************************************************************************
3069 * Storage32Impl_AddExtBlockDepot
3071 * Creates an extended depot block.
3073 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3075 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3076 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3077 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3078 ULONG index
= BLOCK_UNUSED
;
3079 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3080 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3081 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3083 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3084 blocksPerDepotBlock
;
3086 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3089 * The first extended block.
3091 This
->extBigBlockDepotStart
= index
;
3097 * Follow the chain to the last one.
3099 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3101 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3105 * Add the new extended block to the chain.
3107 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3112 * Initialize this block.
3114 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3115 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3120 /******************************************************************************
3121 * Storage32Impl_FreeBigBlock
3123 * This method will flag the specified block as free in the big block depot.
3125 static void StorageImpl_FreeBigBlock(
3129 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3131 if (blockIndex
< This
->prevFreeBlock
)
3132 This
->prevFreeBlock
= blockIndex
;
3135 /************************************************************************
3136 * Storage32Impl_GetNextBlockInChain
3138 * This method will retrieve the block index of the next big block in
3141 * Params: This - Pointer to the Storage object.
3142 * blockIndex - Index of the block to retrieve the chain
3144 * nextBlockIndex - receives the return value.
3146 * Returns: This method returns the index of the next block in the chain.
3147 * It will return the constants:
3148 * BLOCK_SPECIAL - If the block given was not part of a
3150 * BLOCK_END_OF_CHAIN - If the block given was the last in
3152 * BLOCK_UNUSED - If the block given was not past of a chain
3154 * BLOCK_EXTBBDEPOT - This block is part of the extended
3157 * See Windows documentation for more details on IStorage methods.
3159 static HRESULT
StorageImpl_GetNextBlockInChain(
3162 ULONG
* nextBlockIndex
)
3164 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3165 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3166 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3167 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3169 ULONG depotBlockIndexPos
;
3170 int index
, num_blocks
;
3172 *nextBlockIndex
= BLOCK_SPECIAL
;
3174 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3176 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3177 This
->bigBlockDepotCount
);
3178 return STG_E_READFAULT
;
3182 * Cache the currently accessed depot block.
3184 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3186 This
->indexBlockDepotCached
= depotBlockCount
;
3188 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3190 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3195 * We have to look in the extended depot.
3197 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3200 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3203 return STG_E_READFAULT
;
3205 num_blocks
= This
->bigBlockSize
/ 4;
3207 for (index
= 0; index
< num_blocks
; index
++)
3209 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3210 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3214 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3219 /******************************************************************************
3220 * Storage32Impl_GetNextExtendedBlock
3222 * Given an extended block this method will return the next extended block.
3225 * The last ULONG of an extended block is the block index of the next
3226 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3230 * - The index of the next extended block
3231 * - BLOCK_UNUSED: there is no next extended block.
3232 * - Any other return values denotes failure.
3234 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3236 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3237 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3239 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3242 return nextBlockIndex
;
3245 /******************************************************************************
3246 * Storage32Impl_SetNextBlockInChain
3248 * This method will write the index of the specified block's next block
3249 * in the big block depot.
3251 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3254 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3255 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3256 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3259 static void StorageImpl_SetNextBlockInChain(
3264 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3265 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3266 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3267 ULONG depotBlockIndexPos
;
3269 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3270 assert(blockIndex
!= nextBlock
);
3272 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3274 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3279 * We have to look in the extended depot.
3281 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3284 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3287 * Update the cached block depot, if necessary.
3289 if (depotBlockCount
== This
->indexBlockDepotCached
)
3291 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3295 /******************************************************************************
3296 * Storage32Impl_LoadFileHeader
3298 * This method will read in the file header
3300 static HRESULT
StorageImpl_LoadFileHeader(
3304 BYTE headerBigBlock
[HEADER_SIZE
];
3306 ULARGE_INTEGER offset
;
3311 * Get a pointer to the big block of data containing the header.
3313 offset
.u
.HighPart
= 0;
3314 offset
.u
.LowPart
= 0;
3315 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3316 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3317 hr
= STG_E_FILENOTFOUND
;
3320 * Extract the information from the header.
3325 * Check for the "magic number" signature and return an error if it is not
3328 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3330 return STG_E_OLDFORMAT
;
3333 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3335 return STG_E_INVALIDHEADER
;
3338 StorageUtl_ReadWord(
3340 OFFSET_BIGBLOCKSIZEBITS
,
3341 &This
->bigBlockSizeBits
);
3343 StorageUtl_ReadWord(
3345 OFFSET_SMALLBLOCKSIZEBITS
,
3346 &This
->smallBlockSizeBits
);
3348 StorageUtl_ReadDWord(
3350 OFFSET_BBDEPOTCOUNT
,
3351 &This
->bigBlockDepotCount
);
3353 StorageUtl_ReadDWord(
3355 OFFSET_ROOTSTARTBLOCK
,
3356 &This
->rootStartBlock
);
3358 StorageUtl_ReadDWord(
3360 OFFSET_SMALLBLOCKLIMIT
,
3361 &This
->smallBlockLimit
);
3363 StorageUtl_ReadDWord(
3365 OFFSET_SBDEPOTSTART
,
3366 &This
->smallBlockDepotStart
);
3368 StorageUtl_ReadDWord(
3370 OFFSET_EXTBBDEPOTSTART
,
3371 &This
->extBigBlockDepotStart
);
3373 StorageUtl_ReadDWord(
3375 OFFSET_EXTBBDEPOTCOUNT
,
3376 &This
->extBigBlockDepotCount
);
3378 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3380 StorageUtl_ReadDWord(
3382 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3383 &(This
->bigBlockDepotStart
[index
]));
3387 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3389 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3390 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3393 * Right now, the code is making some assumptions about the size of the
3394 * blocks, just make sure they are what we're expecting.
3396 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3397 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3398 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3400 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3401 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3402 hr
= STG_E_INVALIDHEADER
;
3411 /******************************************************************************
3412 * Storage32Impl_SaveFileHeader
3414 * This method will save to the file the header
3416 static void StorageImpl_SaveFileHeader(
3419 BYTE headerBigBlock
[HEADER_SIZE
];
3422 ULARGE_INTEGER offset
;
3423 DWORD bytes_read
, bytes_written
;
3426 * Get a pointer to the big block of data containing the header.
3428 offset
.u
.HighPart
= 0;
3429 offset
.u
.LowPart
= 0;
3430 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3431 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3432 hr
= STG_E_FILENOTFOUND
;
3435 * If the block read failed, the file is probably new.
3440 * Initialize for all unknown fields.
3442 memset(headerBigBlock
, 0, HEADER_SIZE
);
3445 * Initialize the magic number.
3447 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3450 * And a bunch of things we don't know what they mean
3452 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3453 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3454 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3458 * Write the information to the header.
3460 StorageUtl_WriteWord(
3462 OFFSET_BIGBLOCKSIZEBITS
,
3463 This
->bigBlockSizeBits
);
3465 StorageUtl_WriteWord(
3467 OFFSET_SMALLBLOCKSIZEBITS
,
3468 This
->smallBlockSizeBits
);
3470 StorageUtl_WriteDWord(
3472 OFFSET_BBDEPOTCOUNT
,
3473 This
->bigBlockDepotCount
);
3475 StorageUtl_WriteDWord(
3477 OFFSET_ROOTSTARTBLOCK
,
3478 This
->rootStartBlock
);
3480 StorageUtl_WriteDWord(
3482 OFFSET_SMALLBLOCKLIMIT
,
3483 This
->smallBlockLimit
);
3485 StorageUtl_WriteDWord(
3487 OFFSET_SBDEPOTSTART
,
3488 This
->smallBlockDepotStart
);
3490 StorageUtl_WriteDWord(
3492 OFFSET_SBDEPOTCOUNT
,
3493 This
->smallBlockDepotChain
?
3494 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3496 StorageUtl_WriteDWord(
3498 OFFSET_EXTBBDEPOTSTART
,
3499 This
->extBigBlockDepotStart
);
3501 StorageUtl_WriteDWord(
3503 OFFSET_EXTBBDEPOTCOUNT
,
3504 This
->extBigBlockDepotCount
);
3506 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3508 StorageUtl_WriteDWord(
3510 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3511 (This
->bigBlockDepotStart
[index
]));
3515 * Write the big block back to the file.
3517 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3520 /******************************************************************************
3521 * StorageImpl_ReadRawDirEntry
3523 * This method will read the raw data from a directory entry in the file.
3525 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3527 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3529 ULARGE_INTEGER offset
;
3533 offset
.u
.HighPart
= 0;
3534 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3536 hr
= BlockChainStream_ReadAt(
3537 This
->rootBlockChain
,
3546 /******************************************************************************
3547 * StorageImpl_WriteRawDirEntry
3549 * This method will write the raw data from a directory entry in the file.
3551 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3553 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3555 ULARGE_INTEGER offset
;
3559 offset
.u
.HighPart
= 0;
3560 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3562 hr
= BlockChainStream_WriteAt(
3563 This
->rootBlockChain
,
3572 /******************************************************************************
3575 * Update raw directory entry data from the fields in newData.
3577 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3579 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3581 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3584 buffer
+ OFFSET_PS_NAME
,
3586 DIRENTRY_NAME_BUFFER_LEN
);
3588 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3590 StorageUtl_WriteWord(
3592 OFFSET_PS_NAMELENGTH
,
3593 newData
->sizeOfNameString
);
3595 StorageUtl_WriteDWord(
3597 OFFSET_PS_LEFTCHILD
,
3598 newData
->leftChild
);
3600 StorageUtl_WriteDWord(
3602 OFFSET_PS_RIGHTCHILD
,
3603 newData
->rightChild
);
3605 StorageUtl_WriteDWord(
3608 newData
->dirRootEntry
);
3610 StorageUtl_WriteGUID(
3615 StorageUtl_WriteDWord(
3618 newData
->ctime
.dwLowDateTime
);
3620 StorageUtl_WriteDWord(
3622 OFFSET_PS_CTIMEHIGH
,
3623 newData
->ctime
.dwHighDateTime
);
3625 StorageUtl_WriteDWord(
3628 newData
->mtime
.dwLowDateTime
);
3630 StorageUtl_WriteDWord(
3632 OFFSET_PS_MTIMEHIGH
,
3633 newData
->ctime
.dwHighDateTime
);
3635 StorageUtl_WriteDWord(
3637 OFFSET_PS_STARTBLOCK
,
3638 newData
->startingBlock
);
3640 StorageUtl_WriteDWord(
3643 newData
->size
.u
.LowPart
);
3646 /******************************************************************************
3647 * Storage32Impl_ReadDirEntry
3649 * This method will read the specified directory entry.
3651 HRESULT
StorageImpl_ReadDirEntry(
3656 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3659 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3661 if (SUCCEEDED(readRes
))
3663 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3666 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3667 DIRENTRY_NAME_BUFFER_LEN
);
3668 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3670 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3672 StorageUtl_ReadWord(
3674 OFFSET_PS_NAMELENGTH
,
3675 &buffer
->sizeOfNameString
);
3677 StorageUtl_ReadDWord(
3679 OFFSET_PS_LEFTCHILD
,
3680 &buffer
->leftChild
);
3682 StorageUtl_ReadDWord(
3684 OFFSET_PS_RIGHTCHILD
,
3685 &buffer
->rightChild
);
3687 StorageUtl_ReadDWord(
3690 &buffer
->dirRootEntry
);
3692 StorageUtl_ReadGUID(
3697 StorageUtl_ReadDWord(
3700 &buffer
->ctime
.dwLowDateTime
);
3702 StorageUtl_ReadDWord(
3704 OFFSET_PS_CTIMEHIGH
,
3705 &buffer
->ctime
.dwHighDateTime
);
3707 StorageUtl_ReadDWord(
3710 &buffer
->mtime
.dwLowDateTime
);
3712 StorageUtl_ReadDWord(
3714 OFFSET_PS_MTIMEHIGH
,
3715 &buffer
->mtime
.dwHighDateTime
);
3717 StorageUtl_ReadDWord(
3719 OFFSET_PS_STARTBLOCK
,
3720 &buffer
->startingBlock
);
3722 StorageUtl_ReadDWord(
3725 &buffer
->size
.u
.LowPart
);
3727 buffer
->size
.u
.HighPart
= 0;
3733 /*********************************************************************
3734 * Write the specified directory entry to the file
3736 HRESULT
StorageImpl_WriteDirEntry(
3739 const DirEntry
* buffer
)
3741 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3744 UpdateRawDirEntry(currentEntry
, buffer
);
3746 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3750 static BOOL
StorageImpl_ReadBigBlock(
3755 ULARGE_INTEGER ulOffset
;
3758 ulOffset
.u
.HighPart
= 0;
3759 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3761 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3762 return (read
== This
->bigBlockSize
);
3765 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3771 ULARGE_INTEGER ulOffset
;
3775 ulOffset
.u
.HighPart
= 0;
3776 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3777 ulOffset
.u
.LowPart
+= offset
;
3779 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3780 *value
= lendian32toh(tmp
);
3781 return (read
== sizeof(DWORD
));
3784 static BOOL
StorageImpl_WriteBigBlock(
3789 ULARGE_INTEGER ulOffset
;
3792 ulOffset
.u
.HighPart
= 0;
3793 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3795 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3796 return (wrote
== This
->bigBlockSize
);
3799 static BOOL
StorageImpl_WriteDWordToBigBlock(
3805 ULARGE_INTEGER ulOffset
;
3808 ulOffset
.u
.HighPart
= 0;
3809 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3810 ulOffset
.u
.LowPart
+= offset
;
3812 value
= htole32(value
);
3813 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3814 return (wrote
== sizeof(DWORD
));
3817 /******************************************************************************
3818 * Storage32Impl_SmallBlocksToBigBlocks
3820 * This method will convert a small block chain to a big block chain.
3821 * The small block chain will be destroyed.
3823 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3825 SmallBlockChainStream
** ppsbChain
)
3827 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3828 ULARGE_INTEGER size
, offset
;
3829 ULONG cbRead
, cbWritten
;
3830 ULARGE_INTEGER cbTotalRead
;
3831 DirRef streamEntryRef
;
3832 HRESULT resWrite
= S_OK
;
3834 DirEntry streamEntry
;
3836 BlockChainStream
*bbTempChain
= NULL
;
3837 BlockChainStream
*bigBlockChain
= NULL
;
3840 * Create a temporary big block chain that doesn't have
3841 * an associated directory entry. This temporary chain will be
3842 * used to copy data from small blocks to big blocks.
3844 bbTempChain
= BlockChainStream_Construct(This
,
3847 if(!bbTempChain
) return NULL
;
3849 * Grow the big block chain.
3851 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3852 BlockChainStream_SetSize(bbTempChain
, size
);
3855 * Copy the contents of the small block chain to the big block chain
3856 * by small block size increments.
3858 offset
.u
.LowPart
= 0;
3859 offset
.u
.HighPart
= 0;
3860 cbTotalRead
.QuadPart
= 0;
3862 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3865 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3867 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3870 if (FAILED(resRead
))
3875 cbTotalRead
.QuadPart
+= cbRead
;
3877 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3883 if (FAILED(resWrite
))
3886 offset
.u
.LowPart
+= cbRead
;
3888 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3889 HeapFree(GetProcessHeap(),0,buffer
);
3891 size
.u
.HighPart
= 0;
3894 if (FAILED(resRead
) || FAILED(resWrite
))
3896 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3897 BlockChainStream_SetSize(bbTempChain
, size
);
3898 BlockChainStream_Destroy(bbTempChain
);
3903 * Destroy the small block chain.
3905 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3906 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3907 SmallBlockChainStream_Destroy(*ppsbChain
);
3911 * Change the directory entry. This chain is now a big block chain
3912 * and it doesn't reside in the small blocks chain anymore.
3914 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3916 streamEntry
.startingBlock
= bbHeadOfChain
;
3918 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3921 * Destroy the temporary entryless big block chain.
3922 * Create a new big block chain associated with this entry.
3924 BlockChainStream_Destroy(bbTempChain
);
3925 bigBlockChain
= BlockChainStream_Construct(This
,
3929 return bigBlockChain
;
3932 /******************************************************************************
3933 * Storage32Impl_BigBlocksToSmallBlocks
3935 * This method will convert a big block chain to a small block chain.
3936 * The big block chain will be destroyed on success.
3938 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3940 BlockChainStream
** ppbbChain
)
3942 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3943 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3944 DirRef streamEntryRef
;
3945 HRESULT resWrite
= S_OK
, resRead
;
3946 DirEntry streamEntry
;
3948 SmallBlockChainStream
* sbTempChain
;
3950 TRACE("%p %p\n", This
, ppbbChain
);
3952 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3958 size
= BlockChainStream_GetSize(*ppbbChain
);
3959 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3961 offset
.u
.HighPart
= 0;
3962 offset
.u
.LowPart
= 0;
3963 cbTotalRead
.QuadPart
= 0;
3964 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3967 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3968 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3976 cbTotalRead
.QuadPart
+= cbRead
;
3978 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3979 cbRead
, buffer
, &cbWritten
);
3981 if(FAILED(resWrite
))
3984 offset
.u
.LowPart
+= cbRead
;
3986 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3987 HeapFree(GetProcessHeap(), 0, buffer
);
3989 size
.u
.HighPart
= 0;
3992 if(FAILED(resRead
) || FAILED(resWrite
))
3994 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3995 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3996 SmallBlockChainStream_Destroy(sbTempChain
);
4000 /* destroy the original big block chain */
4001 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4002 BlockChainStream_SetSize(*ppbbChain
, size
);
4003 BlockChainStream_Destroy(*ppbbChain
);
4006 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4007 streamEntry
.startingBlock
= sbHeadOfChain
;
4008 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4010 SmallBlockChainStream_Destroy(sbTempChain
);
4011 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4014 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
4017 DirEntry parentData
, snapshotData
;
4019 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
4020 0, (IStorage
**)snapshot
);
4024 hr
= StorageBaseImpl_ReadDirEntry(original
,
4025 original
->storageDirEntry
, &parentData
);
4028 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
4029 (*snapshot
)->storageDirEntry
, &snapshotData
);
4033 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
4034 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
4035 snapshotData
.stgType
= parentData
.stgType
;
4036 snapshotData
.clsid
= parentData
.clsid
;
4037 snapshotData
.ctime
= parentData
.ctime
;
4038 snapshotData
.mtime
= parentData
.mtime
;
4039 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
4040 (*snapshot
)->storageDirEntry
, &snapshotData
);
4044 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
4045 (IStorage
*)(*snapshot
));
4047 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
4053 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4055 DWORD grfCommitFlags
) /* [in] */
4057 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4059 DirEntry data
, tempStorageData
, snapshotRootData
;
4060 DirRef tempStorageEntry
, oldDirRoot
;
4061 StorageInternalImpl
*tempStorage
;
4063 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4065 /* Cannot commit a read-only transacted storage */
4066 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4067 return STG_E_ACCESSDENIED
;
4069 /* To prevent data loss, we create the new structure in the file before we
4070 * delete the old one, so that in case of errors the old data is intact. We
4071 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4072 * needed in the rare situation where we have just enough free disk space to
4073 * overwrite the existing data. */
4075 /* Create an orphaned storage in the parent for the new directory structure. */
4076 memset(&data
, 0, sizeof(data
));
4078 data
.sizeOfNameString
= 1;
4079 data
.stgType
= STGTY_STORAGE
;
4080 data
.leftChild
= DIRENTRY_NULL
;
4081 data
.rightChild
= DIRENTRY_NULL
;
4082 data
.dirRootEntry
= DIRENTRY_NULL
;
4083 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4085 if (FAILED(hr
)) return hr
;
4087 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4088 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4091 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4092 (IStorage
*)tempStorage
);
4094 list_init(&tempStorage
->ParentListEntry
);
4096 IStorage_Release((IStorage
*) tempStorage
);
4103 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4107 /* Update the storage to use the new data in one step. */
4108 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4109 This
->transactedParent
->storageDirEntry
, &data
);
4113 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4114 tempStorageEntry
, &tempStorageData
);
4119 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4120 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4125 oldDirRoot
= data
.dirRootEntry
;
4126 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4127 data
.clsid
= snapshotRootData
.clsid
;
4128 data
.ctime
= snapshotRootData
.ctime
;
4129 data
.mtime
= snapshotRootData
.mtime
;
4131 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4132 This
->transactedParent
->storageDirEntry
, &data
);
4137 /* Destroy the old now-orphaned data. */
4138 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4139 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4143 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4149 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4152 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4153 StorageBaseImpl
*newSnapshot
;
4156 TRACE("(%p)\n", iface
);
4158 /* Create a new copy of the parent data. */
4159 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4160 if (FAILED(hr
)) return hr
;
4162 /* Destroy the open objects. */
4163 StorageBaseImpl_DeleteAll(&This
->base
);
4165 /* Replace our current snapshot. */
4166 IStorage_Release((IStorage
*)This
->snapshot
);
4167 This
->snapshot
= newSnapshot
;
4172 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4174 if (!This
->reverted
)
4176 TRACE("Storage invalidated (stg=%p)\n", This
);
4180 StorageBaseImpl_DeleteAll(This
);
4184 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4186 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4188 TransactedSnapshotImpl_Invalidate(iface
);
4190 IStorage_Release((IStorage
*)This
->transactedParent
);
4192 IStorage_Release((IStorage
*)This
->snapshot
);
4194 HeapFree(GetProcessHeap(), 0, This
);
4197 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4198 const DirEntry
*newData
, DirRef
*index
)
4200 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4202 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4206 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4207 DirRef index
, const DirEntry
*data
)
4209 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4211 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4215 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4216 DirRef index
, DirEntry
*data
)
4218 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4220 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4224 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4227 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4229 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4233 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4234 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4236 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4238 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4239 index
, offset
, size
, buffer
, bytesRead
);
4242 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4243 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4245 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4247 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4248 index
, offset
, size
, buffer
, bytesWritten
);
4251 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4252 DirRef index
, ULARGE_INTEGER newsize
)
4254 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4256 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4260 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4262 StorageBaseImpl_QueryInterface
,
4263 StorageBaseImpl_AddRef
,
4264 StorageBaseImpl_Release
,
4265 StorageBaseImpl_CreateStream
,
4266 StorageBaseImpl_OpenStream
,
4267 StorageBaseImpl_CreateStorage
,
4268 StorageBaseImpl_OpenStorage
,
4269 StorageBaseImpl_CopyTo
,
4270 StorageBaseImpl_MoveElementTo
,
4271 TransactedSnapshotImpl_Commit
,
4272 TransactedSnapshotImpl_Revert
,
4273 StorageBaseImpl_EnumElements
,
4274 StorageBaseImpl_DestroyElement
,
4275 StorageBaseImpl_RenameElement
,
4276 StorageBaseImpl_SetElementTimes
,
4277 StorageBaseImpl_SetClass
,
4278 StorageBaseImpl_SetStateBits
,
4279 StorageBaseImpl_Stat
4282 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4284 TransactedSnapshotImpl_Destroy
,
4285 TransactedSnapshotImpl_Invalidate
,
4286 TransactedSnapshotImpl_CreateDirEntry
,
4287 TransactedSnapshotImpl_WriteDirEntry
,
4288 TransactedSnapshotImpl_ReadDirEntry
,
4289 TransactedSnapshotImpl_DestroyDirEntry
,
4290 TransactedSnapshotImpl_StreamReadAt
,
4291 TransactedSnapshotImpl_StreamWriteAt
,
4292 TransactedSnapshotImpl_StreamSetSize
4295 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4296 TransactedSnapshotImpl
** result
)
4300 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4303 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4305 /* This is OK because the property set storage functions use the IStorage functions. */
4306 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4308 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4310 list_init(&(*result
)->base
.strmHead
);
4312 list_init(&(*result
)->base
.storageHead
);
4314 (*result
)->base
.ref
= 1;
4316 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4318 (*result
)->base
.filename
= parentStorage
->filename
;
4320 /* Create a new temporary storage to act as the snapshot */
4321 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4325 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4327 /* parentStorage already has 1 reference, which we take over here. */
4328 (*result
)->transactedParent
= parentStorage
;
4330 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4333 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4338 return E_OUTOFMEMORY
;
4341 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4342 StorageBaseImpl
** result
)
4346 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4348 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4351 return TransactedSnapshotImpl_Construct(parentStorage
,
4352 (TransactedSnapshotImpl
**)result
);
4355 static HRESULT
Storage_Construct(
4363 StorageBaseImpl
** result
)
4365 StorageImpl
*newStorage
;
4366 StorageBaseImpl
*newTransactedStorage
;
4369 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
4370 if (FAILED(hr
)) goto end
;
4372 if (openFlags
& STGM_TRANSACTED
)
4374 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4376 IStorage_Release((IStorage
*)newStorage
);
4378 *result
= newTransactedStorage
;
4381 *result
= &newStorage
->base
;
4387 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4389 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4391 if (!This
->base
.reverted
)
4393 TRACE("Storage invalidated (stg=%p)\n", This
);
4395 This
->base
.reverted
= 1;
4397 This
->parentStorage
= NULL
;
4399 StorageBaseImpl_DeleteAll(&This
->base
);
4401 list_remove(&This
->ParentListEntry
);
4405 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4407 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4409 StorageInternalImpl_Invalidate(&This
->base
);
4411 HeapFree(GetProcessHeap(), 0, This
);
4414 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4415 const DirEntry
*newData
, DirRef
*index
)
4417 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4419 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4423 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4424 DirRef index
, const DirEntry
*data
)
4426 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4428 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4432 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4433 DirRef index
, DirEntry
*data
)
4435 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4437 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4441 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4444 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4446 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4450 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4451 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4453 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4455 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4456 index
, offset
, size
, buffer
, bytesRead
);
4459 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4460 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4462 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4464 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4465 index
, offset
, size
, buffer
, bytesWritten
);
4468 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4469 DirRef index
, ULARGE_INTEGER newsize
)
4471 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4473 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4477 /******************************************************************************
4479 ** Storage32InternalImpl_Commit
4482 static HRESULT WINAPI
StorageInternalImpl_Commit(
4484 DWORD grfCommitFlags
) /* [in] */
4486 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4490 /******************************************************************************
4492 ** Storage32InternalImpl_Revert
4495 static HRESULT WINAPI
StorageInternalImpl_Revert(
4498 FIXME("(%p): stub\n", iface
);
4502 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4504 IStorage_Release((IStorage
*)This
->parentStorage
);
4505 HeapFree(GetProcessHeap(), 0, This
);
4508 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4509 IEnumSTATSTG
* iface
,
4513 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4516 return E_INVALIDARG
;
4520 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4521 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4524 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4528 return E_NOINTERFACE
;
4531 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4532 IEnumSTATSTG
* iface
)
4534 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4535 return InterlockedIncrement(&This
->ref
);
4538 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4539 IEnumSTATSTG
* iface
)
4541 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4545 newRef
= InterlockedDecrement(&This
->ref
);
4549 IEnumSTATSTGImpl_Destroy(This
);
4555 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4556 IEnumSTATSTGImpl
* This
,
4559 DirRef result
= DIRENTRY_NULL
;
4563 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4565 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4566 This
->parentStorage
->storageDirEntry
, &entry
);
4567 searchNode
= entry
.dirRootEntry
;
4569 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4571 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4575 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4579 searchNode
= entry
.rightChild
;
4583 result
= searchNode
;
4584 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4585 searchNode
= entry
.leftChild
;
4593 if (result
!= DIRENTRY_NULL
)
4594 memcpy(This
->name
, result_name
, sizeof(result_name
));
4600 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4601 IEnumSTATSTG
* iface
,
4604 ULONG
* pceltFetched
)
4606 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4608 DirEntry currentEntry
;
4609 STATSTG
* currentReturnStruct
= rgelt
;
4610 ULONG objectFetched
= 0;
4611 DirRef currentSearchNode
;
4614 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4615 return E_INVALIDARG
;
4617 if (This
->parentStorage
->reverted
)
4618 return STG_E_REVERTED
;
4621 * To avoid the special case, get another pointer to a ULONG value if
4622 * the caller didn't supply one.
4624 if (pceltFetched
==0)
4625 pceltFetched
= &objectFetched
;
4628 * Start the iteration, we will iterate until we hit the end of the
4629 * linked list or until we hit the number of items to iterate through
4633 while ( *pceltFetched
< celt
)
4635 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4637 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4641 * Read the entry from the storage.
4643 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4648 * Copy the information to the return buffer.
4650 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4651 currentReturnStruct
,
4656 * Step to the next item in the iteration
4659 currentReturnStruct
++;
4662 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4669 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4670 IEnumSTATSTG
* iface
,
4673 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4675 ULONG objectFetched
= 0;
4676 DirRef currentSearchNode
;
4679 if (This
->parentStorage
->reverted
)
4680 return STG_E_REVERTED
;
4682 while ( (objectFetched
< celt
) )
4684 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4686 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4692 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4698 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4699 IEnumSTATSTG
* iface
)
4701 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4703 if (This
->parentStorage
->reverted
)
4704 return STG_E_REVERTED
;
4711 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4712 IEnumSTATSTG
* iface
,
4713 IEnumSTATSTG
** ppenum
)
4715 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4717 IEnumSTATSTGImpl
* newClone
;
4719 if (This
->parentStorage
->reverted
)
4720 return STG_E_REVERTED
;
4723 * Perform a sanity check on the parameters.
4726 return E_INVALIDARG
;
4728 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4729 This
->storageDirEntry
);
4733 * The new clone enumeration must point to the same current node as
4736 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4738 *ppenum
= (IEnumSTATSTG
*)newClone
;
4741 * Don't forget to nail down a reference to the clone before
4744 IEnumSTATSTGImpl_AddRef(*ppenum
);
4750 * Virtual function table for the IEnumSTATSTGImpl class.
4752 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4754 IEnumSTATSTGImpl_QueryInterface
,
4755 IEnumSTATSTGImpl_AddRef
,
4756 IEnumSTATSTGImpl_Release
,
4757 IEnumSTATSTGImpl_Next
,
4758 IEnumSTATSTGImpl_Skip
,
4759 IEnumSTATSTGImpl_Reset
,
4760 IEnumSTATSTGImpl_Clone
4763 /******************************************************************************
4764 ** IEnumSTATSTGImpl implementation
4767 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4768 StorageBaseImpl
* parentStorage
,
4769 DirRef storageDirEntry
)
4771 IEnumSTATSTGImpl
* newEnumeration
;
4773 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4775 if (newEnumeration
!=0)
4778 * Set-up the virtual function table and reference count.
4780 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4781 newEnumeration
->ref
= 0;
4784 * We want to nail-down the reference to the storage in case the
4785 * enumeration out-lives the storage in the client application.
4787 newEnumeration
->parentStorage
= parentStorage
;
4788 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4790 newEnumeration
->storageDirEntry
= storageDirEntry
;
4793 * Make sure the current node of the iterator is the first one.
4795 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4798 return newEnumeration
;
4802 * Virtual function table for the Storage32InternalImpl class.
4804 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4806 StorageBaseImpl_QueryInterface
,
4807 StorageBaseImpl_AddRef
,
4808 StorageBaseImpl_Release
,
4809 StorageBaseImpl_CreateStream
,
4810 StorageBaseImpl_OpenStream
,
4811 StorageBaseImpl_CreateStorage
,
4812 StorageBaseImpl_OpenStorage
,
4813 StorageBaseImpl_CopyTo
,
4814 StorageBaseImpl_MoveElementTo
,
4815 StorageInternalImpl_Commit
,
4816 StorageInternalImpl_Revert
,
4817 StorageBaseImpl_EnumElements
,
4818 StorageBaseImpl_DestroyElement
,
4819 StorageBaseImpl_RenameElement
,
4820 StorageBaseImpl_SetElementTimes
,
4821 StorageBaseImpl_SetClass
,
4822 StorageBaseImpl_SetStateBits
,
4823 StorageBaseImpl_Stat
4826 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4828 StorageInternalImpl_Destroy
,
4829 StorageInternalImpl_Invalidate
,
4830 StorageInternalImpl_CreateDirEntry
,
4831 StorageInternalImpl_WriteDirEntry
,
4832 StorageInternalImpl_ReadDirEntry
,
4833 StorageInternalImpl_DestroyDirEntry
,
4834 StorageInternalImpl_StreamReadAt
,
4835 StorageInternalImpl_StreamWriteAt
,
4836 StorageInternalImpl_StreamSetSize
4839 /******************************************************************************
4840 ** Storage32InternalImpl implementation
4843 static StorageInternalImpl
* StorageInternalImpl_Construct(
4844 StorageBaseImpl
* parentStorage
,
4846 DirRef storageDirEntry
)
4848 StorageInternalImpl
* newStorage
;
4850 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4854 list_init(&newStorage
->base
.strmHead
);
4856 list_init(&newStorage
->base
.storageHead
);
4859 * Initialize the virtual function table.
4861 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4862 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
4863 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4864 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4866 newStorage
->base
.reverted
= 0;
4868 newStorage
->base
.ref
= 1;
4870 newStorage
->parentStorage
= parentStorage
;
4873 * Keep a reference to the directory entry of this storage
4875 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4877 newStorage
->base
.create
= 0;
4885 /******************************************************************************
4886 ** StorageUtl implementation
4889 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4893 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4894 *value
= lendian16toh(tmp
);
4897 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4899 value
= htole16(value
);
4900 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4903 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4907 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4908 *value
= lendian32toh(tmp
);
4911 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4913 value
= htole32(value
);
4914 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4917 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4918 ULARGE_INTEGER
* value
)
4920 #ifdef WORDS_BIGENDIAN
4923 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4924 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4925 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4927 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4931 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4932 const ULARGE_INTEGER
*value
)
4934 #ifdef WORDS_BIGENDIAN
4937 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4938 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4939 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4941 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4945 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4947 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4948 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4949 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4951 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4954 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4956 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4957 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4958 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4960 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4963 void StorageUtl_CopyDirEntryToSTATSTG(
4964 StorageBaseImpl
* storage
,
4965 STATSTG
* destination
,
4966 const DirEntry
* source
,
4971 if (source
->stgType
== STGTY_ROOT
)
4973 /* replace the name of root entry (often "Root Entry") by the file name */
4974 entryName
= storage
->filename
;
4978 entryName
= source
->name
;
4982 * The copy of the string occurs only when the flag is not set
4984 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4985 (entryName
== NULL
) ||
4986 (entryName
[0] == 0) )
4988 destination
->pwcsName
= 0;
4992 destination
->pwcsName
=
4993 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4995 strcpyW(destination
->pwcsName
, entryName
);
4998 switch (source
->stgType
)
5002 destination
->type
= STGTY_STORAGE
;
5005 destination
->type
= STGTY_STREAM
;
5008 destination
->type
= STGTY_STREAM
;
5012 destination
->cbSize
= source
->size
;
5014 currentReturnStruct->mtime = {0}; TODO
5015 currentReturnStruct->ctime = {0};
5016 currentReturnStruct->atime = {0};
5018 destination
->grfMode
= 0;
5019 destination
->grfLocksSupported
= 0;
5020 destination
->clsid
= source
->clsid
;
5021 destination
->grfStateBits
= 0;
5022 destination
->reserved
= 0;
5025 /******************************************************************************
5026 ** BlockChainStream implementation
5029 BlockChainStream
* BlockChainStream_Construct(
5030 StorageImpl
* parentStorage
,
5031 ULONG
* headOfStreamPlaceHolder
,
5034 BlockChainStream
* newStream
;
5037 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5039 newStream
->parentStorage
= parentStorage
;
5040 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5041 newStream
->ownerDirEntry
= dirEntry
;
5042 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
5043 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
5044 newStream
->numBlocks
= 0;
5046 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
5048 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5050 newStream
->numBlocks
++;
5051 newStream
->tailIndex
= blockIndex
;
5053 if(FAILED(StorageImpl_GetNextBlockInChain(
5058 HeapFree(GetProcessHeap(), 0, newStream
);
5066 void BlockChainStream_Destroy(BlockChainStream
* This
)
5068 HeapFree(GetProcessHeap(), 0, This
);
5071 /******************************************************************************
5072 * BlockChainStream_GetHeadOfChain
5074 * Returns the head of this stream chain.
5075 * Some special chains don't have directory entries, their heads are kept in
5076 * This->headOfStreamPlaceHolder.
5079 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5081 DirEntry chainEntry
;
5084 if (This
->headOfStreamPlaceHolder
!= 0)
5085 return *(This
->headOfStreamPlaceHolder
);
5087 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5089 hr
= StorageImpl_ReadDirEntry(
5090 This
->parentStorage
,
5091 This
->ownerDirEntry
,
5096 return chainEntry
.startingBlock
;
5100 return BLOCK_END_OF_CHAIN
;
5103 /******************************************************************************
5104 * BlockChainStream_GetCount
5106 * Returns the number of blocks that comprises this chain.
5107 * This is not the size of the stream as the last block may not be full!
5110 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5115 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5117 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5121 if(FAILED(StorageImpl_GetNextBlockInChain(
5122 This
->parentStorage
,
5131 /******************************************************************************
5132 * BlockChainStream_ReadAt
5134 * Reads a specified number of bytes from this chain at the specified offset.
5135 * bytesRead may be NULL.
5136 * Failure will be returned if the specified number of bytes has not been read.
5138 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5139 ULARGE_INTEGER offset
,
5144 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5145 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5146 ULONG bytesToReadInBuffer
;
5150 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5153 * Find the first block in the stream that contains part of the buffer.
5155 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5156 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5157 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5159 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5160 This
->lastBlockNoInSequence
= blockNoInSequence
;
5164 ULONG temp
= blockNoInSequence
;
5166 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5167 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5168 This
->lastBlockNoInSequence
= temp
;
5171 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5173 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5174 return STG_E_DOCFILECORRUPT
;
5175 blockNoInSequence
--;
5178 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5179 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5181 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5184 * Start reading the buffer.
5187 bufferWalker
= buffer
;
5189 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5191 ULARGE_INTEGER ulOffset
;
5194 * Calculate how many bytes we can copy from this big block.
5196 bytesToReadInBuffer
=
5197 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5199 TRACE("block %i\n",blockIndex
);
5200 ulOffset
.u
.HighPart
= 0;
5201 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5204 StorageImpl_ReadAt(This
->parentStorage
,
5207 bytesToReadInBuffer
,
5210 * Step to the next big block.
5212 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5213 return STG_E_DOCFILECORRUPT
;
5215 bufferWalker
+= bytesReadAt
;
5216 size
-= bytesReadAt
;
5217 *bytesRead
+= bytesReadAt
;
5218 offsetInBlock
= 0; /* There is no offset on the next block */
5220 if (bytesToReadInBuffer
!= bytesReadAt
)
5224 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5227 /******************************************************************************
5228 * BlockChainStream_WriteAt
5230 * Writes the specified number of bytes to this chain at the specified offset.
5231 * Will fail if not all specified number of bytes have been written.
5233 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5234 ULARGE_INTEGER offset
,
5237 ULONG
* bytesWritten
)
5239 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5240 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5243 const BYTE
* bufferWalker
;
5246 * Find the first block in the stream that contains part of the buffer.
5248 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5249 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5250 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5252 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5253 This
->lastBlockNoInSequence
= blockNoInSequence
;
5257 ULONG temp
= blockNoInSequence
;
5259 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5260 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5261 This
->lastBlockNoInSequence
= temp
;
5264 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5266 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5268 return STG_E_DOCFILECORRUPT
;
5269 blockNoInSequence
--;
5272 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5274 /* BlockChainStream_SetSize should have already been called to ensure we have
5275 * enough blocks in the chain to write into */
5276 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5278 ERR("not enough blocks in chain to write data\n");
5279 return STG_E_DOCFILECORRUPT
;
5283 bufferWalker
= buffer
;
5285 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5287 ULARGE_INTEGER ulOffset
;
5288 DWORD bytesWrittenAt
;
5290 * Calculate how many bytes we can copy from this big block.
5293 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5295 TRACE("block %i\n",blockIndex
);
5296 ulOffset
.u
.HighPart
= 0;
5297 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5300 StorageImpl_WriteAt(This
->parentStorage
,
5307 * Step to the next big block.
5309 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5311 return STG_E_DOCFILECORRUPT
;
5313 bufferWalker
+= bytesWrittenAt
;
5314 size
-= bytesWrittenAt
;
5315 *bytesWritten
+= bytesWrittenAt
;
5316 offsetInBlock
= 0; /* There is no offset on the next block */
5318 if (bytesWrittenAt
!= bytesToWrite
)
5322 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5325 /******************************************************************************
5326 * BlockChainStream_Shrink
5328 * Shrinks this chain in the big block depot.
5330 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5331 ULARGE_INTEGER newSize
)
5333 ULONG blockIndex
, extraBlock
;
5338 * Reset the last accessed block cache.
5340 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5341 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5344 * Figure out how many blocks are needed to contain the new size
5346 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5348 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5351 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5354 * Go to the new end of chain
5356 while (count
< numBlocks
)
5358 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5364 /* Get the next block before marking the new end */
5365 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5369 /* Mark the new end of chain */
5370 StorageImpl_SetNextBlockInChain(
5371 This
->parentStorage
,
5373 BLOCK_END_OF_CHAIN
);
5375 This
->tailIndex
= blockIndex
;
5376 This
->numBlocks
= numBlocks
;
5379 * Mark the extra blocks as free
5381 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5383 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5386 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5387 extraBlock
= blockIndex
;
5393 /******************************************************************************
5394 * BlockChainStream_Enlarge
5396 * Grows this chain in the big block depot.
5398 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5399 ULARGE_INTEGER newSize
)
5401 ULONG blockIndex
, currentBlock
;
5403 ULONG oldNumBlocks
= 0;
5405 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5408 * Empty chain. Create the head.
5410 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5412 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5413 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5415 BLOCK_END_OF_CHAIN
);
5417 if (This
->headOfStreamPlaceHolder
!= 0)
5419 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5423 DirEntry chainEntry
;
5424 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5426 StorageImpl_ReadDirEntry(
5427 This
->parentStorage
,
5428 This
->ownerDirEntry
,
5431 chainEntry
.startingBlock
= blockIndex
;
5433 StorageImpl_WriteDirEntry(
5434 This
->parentStorage
,
5435 This
->ownerDirEntry
,
5439 This
->tailIndex
= blockIndex
;
5440 This
->numBlocks
= 1;
5444 * Figure out how many blocks are needed to contain this stream
5446 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5448 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5452 * Go to the current end of chain
5454 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5456 currentBlock
= blockIndex
;
5458 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5461 currentBlock
= blockIndex
;
5463 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5468 This
->tailIndex
= currentBlock
;
5471 currentBlock
= This
->tailIndex
;
5472 oldNumBlocks
= This
->numBlocks
;
5475 * Add new blocks to the chain
5477 if (oldNumBlocks
< newNumBlocks
)
5479 while (oldNumBlocks
< newNumBlocks
)
5481 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5483 StorageImpl_SetNextBlockInChain(
5484 This
->parentStorage
,
5488 StorageImpl_SetNextBlockInChain(
5489 This
->parentStorage
,
5491 BLOCK_END_OF_CHAIN
);
5493 currentBlock
= blockIndex
;
5497 This
->tailIndex
= blockIndex
;
5498 This
->numBlocks
= newNumBlocks
;
5504 /******************************************************************************
5505 * BlockChainStream_SetSize
5507 * Sets the size of this stream. The big block depot will be updated.
5508 * The file will grow if we grow the chain.
5510 * TODO: Free the actual blocks in the file when we shrink the chain.
5511 * Currently, the blocks are still in the file. So the file size
5512 * doesn't shrink even if we shrink streams.
5514 BOOL
BlockChainStream_SetSize(
5515 BlockChainStream
* This
,
5516 ULARGE_INTEGER newSize
)
5518 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5520 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5523 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5525 BlockChainStream_Shrink(This
, newSize
);
5529 BlockChainStream_Enlarge(This
, newSize
);
5535 /******************************************************************************
5536 * BlockChainStream_GetSize
5538 * Returns the size of this chain.
5539 * Will return the block count if this chain doesn't have a directory entry.
5541 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5543 DirEntry chainEntry
;
5545 if(This
->headOfStreamPlaceHolder
== NULL
)
5548 * This chain has a directory entry so use the size value from there.
5550 StorageImpl_ReadDirEntry(
5551 This
->parentStorage
,
5552 This
->ownerDirEntry
,
5555 return chainEntry
.size
;
5560 * this chain is a chain that does not have a directory entry, figure out the
5561 * size by making the product number of used blocks times the
5564 ULARGE_INTEGER result
;
5565 result
.u
.HighPart
= 0;
5568 BlockChainStream_GetCount(This
) *
5569 This
->parentStorage
->bigBlockSize
;
5575 /******************************************************************************
5576 ** SmallBlockChainStream implementation
5579 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5580 StorageImpl
* parentStorage
,
5581 ULONG
* headOfStreamPlaceHolder
,
5584 SmallBlockChainStream
* newStream
;
5586 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5588 newStream
->parentStorage
= parentStorage
;
5589 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5590 newStream
->ownerDirEntry
= dirEntry
;
5595 void SmallBlockChainStream_Destroy(
5596 SmallBlockChainStream
* This
)
5598 HeapFree(GetProcessHeap(), 0, This
);
5601 /******************************************************************************
5602 * SmallBlockChainStream_GetHeadOfChain
5604 * Returns the head of this chain of small blocks.
5606 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5607 SmallBlockChainStream
* This
)
5609 DirEntry chainEntry
;
5612 if (This
->headOfStreamPlaceHolder
!= NULL
)
5613 return *(This
->headOfStreamPlaceHolder
);
5615 if (This
->ownerDirEntry
)
5617 hr
= StorageImpl_ReadDirEntry(
5618 This
->parentStorage
,
5619 This
->ownerDirEntry
,
5624 return chainEntry
.startingBlock
;
5629 return BLOCK_END_OF_CHAIN
;
5632 /******************************************************************************
5633 * SmallBlockChainStream_GetNextBlockInChain
5635 * Returns the index of the next small block in this chain.
5638 * - BLOCK_END_OF_CHAIN: end of this chain
5639 * - BLOCK_UNUSED: small block 'blockIndex' is free
5641 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5642 SmallBlockChainStream
* This
,
5644 ULONG
* nextBlockInChain
)
5646 ULARGE_INTEGER offsetOfBlockInDepot
;
5651 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5653 offsetOfBlockInDepot
.u
.HighPart
= 0;
5654 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5657 * Read those bytes in the buffer from the small block file.
5659 res
= BlockChainStream_ReadAt(
5660 This
->parentStorage
->smallBlockDepotChain
,
5661 offsetOfBlockInDepot
,
5668 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5675 /******************************************************************************
5676 * SmallBlockChainStream_SetNextBlockInChain
5678 * Writes the index of the next block of the specified block in the small
5680 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5681 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5683 static void SmallBlockChainStream_SetNextBlockInChain(
5684 SmallBlockChainStream
* This
,
5688 ULARGE_INTEGER offsetOfBlockInDepot
;
5692 offsetOfBlockInDepot
.u
.HighPart
= 0;
5693 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5695 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5698 * Read those bytes in the buffer from the small block file.
5700 BlockChainStream_WriteAt(
5701 This
->parentStorage
->smallBlockDepotChain
,
5702 offsetOfBlockInDepot
,
5708 /******************************************************************************
5709 * SmallBlockChainStream_FreeBlock
5711 * Flag small block 'blockIndex' as free in the small block depot.
5713 static void SmallBlockChainStream_FreeBlock(
5714 SmallBlockChainStream
* This
,
5717 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5720 /******************************************************************************
5721 * SmallBlockChainStream_GetNextFreeBlock
5723 * Returns the index of a free small block. The small block depot will be
5724 * enlarged if necessary. The small block chain will also be enlarged if
5727 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5728 SmallBlockChainStream
* This
)
5730 ULARGE_INTEGER offsetOfBlockInDepot
;
5733 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
5734 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5736 ULONG smallBlocksPerBigBlock
;
5738 offsetOfBlockInDepot
.u
.HighPart
= 0;
5741 * Scan the small block depot for a free block
5743 while (nextBlockIndex
!= BLOCK_UNUSED
)
5745 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5747 res
= BlockChainStream_ReadAt(
5748 This
->parentStorage
->smallBlockDepotChain
,
5749 offsetOfBlockInDepot
,
5755 * If we run out of space for the small block depot, enlarge it
5759 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5761 if (nextBlockIndex
!= BLOCK_UNUSED
)
5767 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5769 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5770 ULONG nextBlock
, newsbdIndex
;
5771 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
5773 nextBlock
= sbdIndex
;
5774 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5776 sbdIndex
= nextBlock
;
5777 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5780 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5781 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5782 StorageImpl_SetNextBlockInChain(
5783 This
->parentStorage
,
5787 StorageImpl_SetNextBlockInChain(
5788 This
->parentStorage
,
5790 BLOCK_END_OF_CHAIN
);
5793 * Initialize all the small blocks to free
5795 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5796 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5801 * We have just created the small block depot.
5807 * Save it in the header
5809 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5810 StorageImpl_SaveFileHeader(This
->parentStorage
);
5813 * And allocate the first big block that will contain small blocks
5816 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5818 StorageImpl_SetNextBlockInChain(
5819 This
->parentStorage
,
5821 BLOCK_END_OF_CHAIN
);
5823 StorageImpl_ReadDirEntry(
5824 This
->parentStorage
,
5825 This
->parentStorage
->base
.storageDirEntry
,
5828 rootEntry
.startingBlock
= sbStartIndex
;
5829 rootEntry
.size
.u
.HighPart
= 0;
5830 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5832 StorageImpl_WriteDirEntry(
5833 This
->parentStorage
,
5834 This
->parentStorage
->base
.storageDirEntry
,
5838 StorageImpl_SaveFileHeader(This
->parentStorage
);
5842 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
5844 smallBlocksPerBigBlock
=
5845 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5848 * Verify if we have to allocate big blocks to contain small blocks
5850 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5853 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5855 StorageImpl_ReadDirEntry(
5856 This
->parentStorage
,
5857 This
->parentStorage
->base
.storageDirEntry
,
5860 if (rootEntry
.size
.u
.LowPart
<
5861 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5863 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5865 BlockChainStream_SetSize(
5866 This
->parentStorage
->smallBlockRootChain
,
5869 StorageImpl_WriteDirEntry(
5870 This
->parentStorage
,
5871 This
->parentStorage
->base
.storageDirEntry
,
5879 /******************************************************************************
5880 * SmallBlockChainStream_ReadAt
5882 * Reads a specified number of bytes from this chain at the specified offset.
5883 * bytesRead may be NULL.
5884 * Failure will be returned if the specified number of bytes has not been read.
5886 HRESULT
SmallBlockChainStream_ReadAt(
5887 SmallBlockChainStream
* This
,
5888 ULARGE_INTEGER offset
,
5894 ULARGE_INTEGER offsetInBigBlockFile
;
5895 ULONG blockNoInSequence
=
5896 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5898 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5899 ULONG bytesToReadInBuffer
;
5901 ULONG bytesReadFromBigBlockFile
;
5905 * This should never happen on a small block file.
5907 assert(offset
.u
.HighPart
==0);
5910 * Find the first block in the stream that contains part of the buffer.
5912 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5914 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5916 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5919 blockNoInSequence
--;
5923 * Start reading the buffer.
5926 bufferWalker
= buffer
;
5928 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5931 * Calculate how many bytes we can copy from this small block.
5933 bytesToReadInBuffer
=
5934 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5937 * Calculate the offset of the small block in the small block file.
5939 offsetInBigBlockFile
.u
.HighPart
= 0;
5940 offsetInBigBlockFile
.u
.LowPart
=
5941 blockIndex
* This
->parentStorage
->smallBlockSize
;
5943 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5946 * Read those bytes in the buffer from the small block file.
5947 * The small block has already been identified so it shouldn't fail
5948 * unless the file is corrupt.
5950 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5951 offsetInBigBlockFile
,
5952 bytesToReadInBuffer
,
5954 &bytesReadFromBigBlockFile
);
5960 * Step to the next big block.
5962 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5964 return STG_E_DOCFILECORRUPT
;
5966 bufferWalker
+= bytesReadFromBigBlockFile
;
5967 size
-= bytesReadFromBigBlockFile
;
5968 *bytesRead
+= bytesReadFromBigBlockFile
;
5969 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5972 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5975 /******************************************************************************
5976 * SmallBlockChainStream_WriteAt
5978 * Writes the specified number of bytes to this chain at the specified offset.
5979 * Will fail if not all specified number of bytes have been written.
5981 HRESULT
SmallBlockChainStream_WriteAt(
5982 SmallBlockChainStream
* This
,
5983 ULARGE_INTEGER offset
,
5986 ULONG
* bytesWritten
)
5988 ULARGE_INTEGER offsetInBigBlockFile
;
5989 ULONG blockNoInSequence
=
5990 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5992 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5993 ULONG bytesToWriteInBuffer
;
5995 ULONG bytesWrittenToBigBlockFile
;
5996 const BYTE
* bufferWalker
;
6000 * This should never happen on a small block file.
6002 assert(offset
.u
.HighPart
==0);
6005 * Find the first block in the stream that contains part of the buffer.
6007 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6009 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6011 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6012 return STG_E_DOCFILECORRUPT
;
6013 blockNoInSequence
--;
6017 * Start writing the buffer.
6020 bufferWalker
= buffer
;
6021 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6024 * Calculate how many bytes we can copy to this small block.
6026 bytesToWriteInBuffer
=
6027 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6030 * Calculate the offset of the small block in the small block file.
6032 offsetInBigBlockFile
.u
.HighPart
= 0;
6033 offsetInBigBlockFile
.u
.LowPart
=
6034 blockIndex
* This
->parentStorage
->smallBlockSize
;
6036 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6039 * Write those bytes in the buffer to the small block file.
6041 res
= BlockChainStream_WriteAt(
6042 This
->parentStorage
->smallBlockRootChain
,
6043 offsetInBigBlockFile
,
6044 bytesToWriteInBuffer
,
6046 &bytesWrittenToBigBlockFile
);
6051 * Step to the next big block.
6053 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6056 bufferWalker
+= bytesWrittenToBigBlockFile
;
6057 size
-= bytesWrittenToBigBlockFile
;
6058 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6059 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6062 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6065 /******************************************************************************
6066 * SmallBlockChainStream_Shrink
6068 * Shrinks this chain in the small block depot.
6070 static BOOL
SmallBlockChainStream_Shrink(
6071 SmallBlockChainStream
* This
,
6072 ULARGE_INTEGER newSize
)
6074 ULONG blockIndex
, extraBlock
;
6078 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6080 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6083 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6086 * Go to the new end of chain
6088 while (count
< numBlocks
)
6090 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6097 * If the count is 0, we have a special case, the head of the chain was
6102 DirEntry chainEntry
;
6104 StorageImpl_ReadDirEntry(This
->parentStorage
,
6105 This
->ownerDirEntry
,
6108 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6110 StorageImpl_WriteDirEntry(This
->parentStorage
,
6111 This
->ownerDirEntry
,
6115 * We start freeing the chain at the head block.
6117 extraBlock
= blockIndex
;
6121 /* Get the next block before marking the new end */
6122 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6126 /* Mark the new end of chain */
6127 SmallBlockChainStream_SetNextBlockInChain(
6130 BLOCK_END_OF_CHAIN
);
6134 * Mark the extra blocks as free
6136 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6138 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6141 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6142 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
6143 extraBlock
= blockIndex
;
6149 /******************************************************************************
6150 * SmallBlockChainStream_Enlarge
6152 * Grows this chain in the small block depot.
6154 static BOOL
SmallBlockChainStream_Enlarge(
6155 SmallBlockChainStream
* This
,
6156 ULARGE_INTEGER newSize
)
6158 ULONG blockIndex
, currentBlock
;
6160 ULONG oldNumBlocks
= 0;
6162 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6165 * Empty chain. Create the head.
6167 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6169 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6170 SmallBlockChainStream_SetNextBlockInChain(
6173 BLOCK_END_OF_CHAIN
);
6175 if (This
->headOfStreamPlaceHolder
!= NULL
)
6177 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6181 DirEntry chainEntry
;
6183 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6186 chainEntry
.startingBlock
= blockIndex
;
6188 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6193 currentBlock
= blockIndex
;
6196 * Figure out how many blocks are needed to contain this stream
6198 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6200 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6204 * Go to the current end of chain
6206 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6209 currentBlock
= blockIndex
;
6210 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6215 * Add new blocks to the chain
6217 while (oldNumBlocks
< newNumBlocks
)
6219 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6220 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6222 SmallBlockChainStream_SetNextBlockInChain(
6225 BLOCK_END_OF_CHAIN
);
6227 currentBlock
= blockIndex
;
6234 /******************************************************************************
6235 * SmallBlockChainStream_SetSize
6237 * Sets the size of this stream.
6238 * The file will grow if we grow the chain.
6240 * TODO: Free the actual blocks in the file when we shrink the chain.
6241 * Currently, the blocks are still in the file. So the file size
6242 * doesn't shrink even if we shrink streams.
6244 BOOL
SmallBlockChainStream_SetSize(
6245 SmallBlockChainStream
* This
,
6246 ULARGE_INTEGER newSize
)
6248 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6250 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6253 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6255 SmallBlockChainStream_Shrink(This
, newSize
);
6259 SmallBlockChainStream_Enlarge(This
, newSize
);
6265 /******************************************************************************
6266 * SmallBlockChainStream_GetCount
6268 * Returns the number of small blocks that comprises this chain.
6269 * This is not the size of the stream as the last block may not be full!
6272 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6277 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6279 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6283 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6284 blockIndex
, &blockIndex
)))
6291 /******************************************************************************
6292 * SmallBlockChainStream_GetSize
6294 * Returns the size of this chain.
6296 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6298 DirEntry chainEntry
;
6300 if(This
->headOfStreamPlaceHolder
!= NULL
)
6302 ULARGE_INTEGER result
;
6303 result
.u
.HighPart
= 0;
6305 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6306 This
->parentStorage
->smallBlockSize
;
6311 StorageImpl_ReadDirEntry(
6312 This
->parentStorage
,
6313 This
->ownerDirEntry
,
6316 return chainEntry
.size
;
6319 static HRESULT
create_storagefile(
6323 STGOPTIONS
* pStgOptions
,
6327 StorageBaseImpl
* newStorage
= 0;
6328 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6329 HRESULT hr
= STG_E_INVALIDFLAG
;
6333 DWORD fileAttributes
;
6334 WCHAR tempFileName
[MAX_PATH
];
6337 return STG_E_INVALIDPOINTER
;
6339 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
6340 return STG_E_INVALIDPARAMETER
;
6342 /* if no share mode given then DENY_NONE is the default */
6343 if (STGM_SHARE_MODE(grfMode
) == 0)
6344 grfMode
|= STGM_SHARE_DENY_NONE
;
6346 if ( FAILED( validateSTGM(grfMode
) ))
6349 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6350 switch(STGM_ACCESS_MODE(grfMode
))
6353 case STGM_READWRITE
:
6359 /* in direct mode, can only use SHARE_EXCLUSIVE */
6360 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6363 /* but in transacted mode, any share mode is valid */
6366 * Generate a unique name.
6370 WCHAR tempPath
[MAX_PATH
];
6371 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6373 memset(tempPath
, 0, sizeof(tempPath
));
6374 memset(tempFileName
, 0, sizeof(tempFileName
));
6376 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6379 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6380 pwcsName
= tempFileName
;
6383 hr
= STG_E_INSUFFICIENTMEMORY
;
6387 creationMode
= TRUNCATE_EXISTING
;
6391 creationMode
= GetCreationModeFromSTGM(grfMode
);
6395 * Interpret the STGM value grfMode
6397 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6398 accessMode
= GetAccessModeFromSTGM(grfMode
);
6400 if (grfMode
& STGM_DELETEONRELEASE
)
6401 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6403 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6405 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6409 FIXME("Storage share mode not implemented.\n");
6414 hFile
= CreateFileW(pwcsName
,
6422 if (hFile
== INVALID_HANDLE_VALUE
)
6424 if(GetLastError() == ERROR_FILE_EXISTS
)
6425 hr
= STG_E_FILEALREADYEXISTS
;
6432 * Allocate and initialize the new IStorage32object.
6434 hr
= Storage_Construct(
6441 pStgOptions
->ulSectorSize
,
6449 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
6451 IStorage_Release((IStorage
*)newStorage
);
6454 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6459 /******************************************************************************
6460 * StgCreateDocfile [OLE32.@]
6461 * Creates a new compound file storage object
6464 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6465 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6466 * reserved [ ?] unused?, usually 0
6467 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6470 * S_OK if the file was successfully created
6471 * some STG_E_ value if error
6473 * if pwcsName is NULL, create file with new unique name
6474 * the function can returns
6475 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6478 HRESULT WINAPI
StgCreateDocfile(
6482 IStorage
**ppstgOpen
)
6484 STGOPTIONS stgoptions
= {1, 0, 512};
6486 TRACE("(%s, %x, %d, %p)\n",
6487 debugstr_w(pwcsName
), grfMode
,
6488 reserved
, ppstgOpen
);
6491 return STG_E_INVALIDPOINTER
;
6493 return STG_E_INVALIDPARAMETER
;
6495 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
6498 /******************************************************************************
6499 * StgCreateStorageEx [OLE32.@]
6501 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6503 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6504 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6506 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6508 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6509 return STG_E_INVALIDPARAMETER
;
6512 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6514 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6515 return STG_E_INVALIDPARAMETER
;
6518 if (stgfmt
== STGFMT_FILE
)
6520 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6521 return STG_E_INVALIDPARAMETER
;
6524 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6526 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
6530 ERR("Invalid stgfmt argument\n");
6531 return STG_E_INVALIDPARAMETER
;
6534 /******************************************************************************
6535 * StgCreatePropSetStg [OLE32.@]
6537 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6538 IPropertySetStorage
**ppPropSetStg
)
6542 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6544 hr
= STG_E_INVALIDPARAMETER
;
6546 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6547 (void**)ppPropSetStg
);
6551 /******************************************************************************
6552 * StgOpenStorageEx [OLE32.@]
6554 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6556 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6557 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6559 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6561 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6562 return STG_E_INVALIDPARAMETER
;
6568 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6569 return STG_E_INVALIDPARAMETER
;
6571 case STGFMT_STORAGE
:
6574 case STGFMT_DOCFILE
:
6575 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6577 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6578 return STG_E_INVALIDPARAMETER
;
6580 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6584 WARN("STGFMT_ANY assuming storage\n");
6588 return STG_E_INVALIDPARAMETER
;
6591 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6595 /******************************************************************************
6596 * StgOpenStorage [OLE32.@]
6598 HRESULT WINAPI
StgOpenStorage(
6599 const OLECHAR
*pwcsName
,
6600 IStorage
*pstgPriority
,
6604 IStorage
**ppstgOpen
)
6606 StorageBaseImpl
* newStorage
= 0;
6612 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6613 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6614 snbExclude
, reserved
, ppstgOpen
);
6618 hr
= STG_E_INVALIDNAME
;
6624 hr
= STG_E_INVALIDPOINTER
;
6630 hr
= STG_E_INVALIDPARAMETER
;
6634 if (grfMode
& STGM_PRIORITY
)
6636 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6637 return STG_E_INVALIDFLAG
;
6638 if (grfMode
& STGM_DELETEONRELEASE
)
6639 return STG_E_INVALIDFUNCTION
;
6640 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6641 return STG_E_INVALIDFLAG
;
6642 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6643 grfMode
|= STGM_SHARE_DENY_NONE
;
6645 /* STGM_PRIORITY stops other IStorage objects on the same file from
6646 * committing until the STGM_PRIORITY IStorage is closed. it also
6647 * stops non-transacted mode StgOpenStorage calls with write access from
6648 * succeeding. obviously, both of these cannot be achieved through just
6649 * file share flags */
6650 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6654 * Validate the sharing mode
6656 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6657 switch(STGM_SHARE_MODE(grfMode
))
6659 case STGM_SHARE_EXCLUSIVE
:
6660 case STGM_SHARE_DENY_WRITE
:
6663 hr
= STG_E_INVALIDFLAG
;
6667 if ( FAILED( validateSTGM(grfMode
) ) ||
6668 (grfMode
&STGM_CREATE
))
6670 hr
= STG_E_INVALIDFLAG
;
6674 /* shared reading requires transacted mode */
6675 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6676 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6677 !(grfMode
&STGM_TRANSACTED
) )
6679 hr
= STG_E_INVALIDFLAG
;
6684 * Interpret the STGM value grfMode
6686 shareMode
= GetShareModeFromSTGM(grfMode
);
6687 accessMode
= GetAccessModeFromSTGM(grfMode
);
6691 hFile
= CreateFileW( pwcsName
,
6696 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6699 if (hFile
==INVALID_HANDLE_VALUE
)
6701 DWORD last_error
= GetLastError();
6707 case ERROR_FILE_NOT_FOUND
:
6708 hr
= STG_E_FILENOTFOUND
;
6711 case ERROR_PATH_NOT_FOUND
:
6712 hr
= STG_E_PATHNOTFOUND
;
6715 case ERROR_ACCESS_DENIED
:
6716 case ERROR_WRITE_PROTECT
:
6717 hr
= STG_E_ACCESSDENIED
;
6720 case ERROR_SHARING_VIOLATION
:
6721 hr
= STG_E_SHAREVIOLATION
;
6732 * Refuse to open the file if it's too small to be a structured storage file
6733 * FIXME: verify the file when reading instead of here
6735 if (GetFileSize(hFile
, NULL
) < 0x100)
6738 hr
= STG_E_FILEALREADYEXISTS
;
6743 * Allocate and initialize the new IStorage32object.
6745 hr
= Storage_Construct(
6758 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6760 if(hr
== STG_E_INVALIDHEADER
)
6761 hr
= STG_E_FILEALREADYEXISTS
;
6766 * Get an "out" pointer for the caller.
6768 *ppstgOpen
= (IStorage
*)newStorage
;
6771 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6775 /******************************************************************************
6776 * StgCreateDocfileOnILockBytes [OLE32.@]
6778 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6782 IStorage
** ppstgOpen
)
6784 StorageBaseImpl
* newStorage
= 0;
6787 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6788 return STG_E_INVALIDPOINTER
;
6791 * Allocate and initialize the new IStorage object.
6793 hr
= Storage_Construct(
6809 * Get an "out" pointer for the caller.
6811 *ppstgOpen
= (IStorage
*)newStorage
;
6816 /******************************************************************************
6817 * StgOpenStorageOnILockBytes [OLE32.@]
6819 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6821 IStorage
*pstgPriority
,
6825 IStorage
**ppstgOpen
)
6827 StorageBaseImpl
* newStorage
= 0;
6830 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6831 return STG_E_INVALIDPOINTER
;
6833 if ( FAILED( validateSTGM(grfMode
) ))
6834 return STG_E_INVALIDFLAG
;
6839 * Allocate and initialize the new IStorage object.
6841 hr
= Storage_Construct(
6857 * Get an "out" pointer for the caller.
6859 *ppstgOpen
= (IStorage
*)newStorage
;
6864 /******************************************************************************
6865 * StgSetTimes [ole32.@]
6866 * StgSetTimes [OLE32.@]
6870 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6871 FILETIME
const *patime
, FILETIME
const *pmtime
)
6873 IStorage
*stg
= NULL
;
6876 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6878 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6882 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6883 IStorage_Release(stg
);
6889 /******************************************************************************
6890 * StgIsStorageILockBytes [OLE32.@]
6892 * Determines if the ILockBytes contains a storage object.
6894 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6897 ULARGE_INTEGER offset
;
6899 offset
.u
.HighPart
= 0;
6900 offset
.u
.LowPart
= 0;
6902 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6904 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6910 /******************************************************************************
6911 * WriteClassStg [OLE32.@]
6913 * This method will store the specified CLSID in the specified storage object
6915 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6920 return E_INVALIDARG
;
6923 return STG_E_INVALIDPOINTER
;
6925 hRes
= IStorage_SetClass(pStg
, rclsid
);
6930 /***********************************************************************
6931 * ReadClassStg (OLE32.@)
6933 * This method reads the CLSID previously written to a storage object with
6934 * the WriteClassStg.
6937 * pstg [I] IStorage pointer
6938 * pclsid [O] Pointer to where the CLSID is written
6942 * Failure: HRESULT code.
6944 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6949 TRACE("(%p, %p)\n", pstg
, pclsid
);
6951 if(!pstg
|| !pclsid
)
6952 return E_INVALIDARG
;
6955 * read a STATSTG structure (contains the clsid) from the storage
6957 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6960 *pclsid
=pstatstg
.clsid
;
6965 /***********************************************************************
6966 * OleLoadFromStream (OLE32.@)
6968 * This function loads an object from stream
6970 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6974 LPPERSISTSTREAM xstm
;
6976 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6978 res
=ReadClassStm(pStm
,&clsid
);
6981 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6984 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6986 IUnknown_Release((IUnknown
*)*ppvObj
);
6989 res
=IPersistStream_Load(xstm
,pStm
);
6990 IPersistStream_Release(xstm
);
6991 /* FIXME: all refcounts ok at this point? I think they should be:
6994 * xstm : 0 (released)
6999 /***********************************************************************
7000 * OleSaveToStream (OLE32.@)
7002 * This function saves an object with the IPersistStream interface on it
7003 * to the specified stream.
7005 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7011 TRACE("(%p,%p)\n",pPStm
,pStm
);
7013 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7015 if (SUCCEEDED(res
)){
7017 res
=WriteClassStm(pStm
,&clsid
);
7021 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7024 TRACE("Finished Save\n");
7028 /****************************************************************************
7029 * This method validate a STGM parameter that can contain the values below
7031 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7032 * The stgm values contained in 0xffff0000 are bitmasks.
7034 * STGM_DIRECT 0x00000000
7035 * STGM_TRANSACTED 0x00010000
7036 * STGM_SIMPLE 0x08000000
7038 * STGM_READ 0x00000000
7039 * STGM_WRITE 0x00000001
7040 * STGM_READWRITE 0x00000002
7042 * STGM_SHARE_DENY_NONE 0x00000040
7043 * STGM_SHARE_DENY_READ 0x00000030
7044 * STGM_SHARE_DENY_WRITE 0x00000020
7045 * STGM_SHARE_EXCLUSIVE 0x00000010
7047 * STGM_PRIORITY 0x00040000
7048 * STGM_DELETEONRELEASE 0x04000000
7050 * STGM_CREATE 0x00001000
7051 * STGM_CONVERT 0x00020000
7052 * STGM_FAILIFTHERE 0x00000000
7054 * STGM_NOSCRATCH 0x00100000
7055 * STGM_NOSNAPSHOT 0x00200000
7057 static HRESULT
validateSTGM(DWORD stgm
)
7059 DWORD access
= STGM_ACCESS_MODE(stgm
);
7060 DWORD share
= STGM_SHARE_MODE(stgm
);
7061 DWORD create
= STGM_CREATE_MODE(stgm
);
7063 if (stgm
&~STGM_KNOWN_FLAGS
)
7065 ERR("unknown flags %08x\n", stgm
);
7073 case STGM_READWRITE
:
7081 case STGM_SHARE_DENY_NONE
:
7082 case STGM_SHARE_DENY_READ
:
7083 case STGM_SHARE_DENY_WRITE
:
7084 case STGM_SHARE_EXCLUSIVE
:
7093 case STGM_FAILIFTHERE
:
7100 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7102 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7106 * STGM_CREATE | STGM_CONVERT
7107 * if both are false, STGM_FAILIFTHERE is set to TRUE
7109 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7113 * STGM_NOSCRATCH requires STGM_TRANSACTED
7115 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7119 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7120 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7122 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7123 (!(stgm
& STGM_TRANSACTED
) ||
7124 share
== STGM_SHARE_EXCLUSIVE
||
7125 share
== STGM_SHARE_DENY_WRITE
) )
7131 /****************************************************************************
7132 * GetShareModeFromSTGM
7134 * This method will return a share mode flag from a STGM value.
7135 * The STGM value is assumed valid.
7137 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7139 switch (STGM_SHARE_MODE(stgm
))
7141 case STGM_SHARE_DENY_NONE
:
7142 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7143 case STGM_SHARE_DENY_READ
:
7144 return FILE_SHARE_WRITE
;
7145 case STGM_SHARE_DENY_WRITE
:
7146 return FILE_SHARE_READ
;
7147 case STGM_SHARE_EXCLUSIVE
:
7150 ERR("Invalid share mode!\n");
7155 /****************************************************************************
7156 * GetAccessModeFromSTGM
7158 * This method will return an access mode flag from a STGM value.
7159 * The STGM value is assumed valid.
7161 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7163 switch (STGM_ACCESS_MODE(stgm
))
7166 return GENERIC_READ
;
7168 case STGM_READWRITE
:
7169 return GENERIC_READ
| GENERIC_WRITE
;
7171 ERR("Invalid access mode!\n");
7176 /****************************************************************************
7177 * GetCreationModeFromSTGM
7179 * This method will return a creation mode flag from a STGM value.
7180 * The STGM value is assumed valid.
7182 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7184 switch(STGM_CREATE_MODE(stgm
))
7187 return CREATE_ALWAYS
;
7189 FIXME("STGM_CONVERT not implemented!\n");
7191 case STGM_FAILIFTHERE
:
7194 ERR("Invalid create mode!\n");
7200 /*************************************************************************
7201 * OLECONVERT_LoadOLE10 [Internal]
7203 * Loads the OLE10 STREAM to memory
7206 * pOleStream [I] The OLESTREAM
7207 * pData [I] Data Structure for the OLESTREAM Data
7211 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7212 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7215 * This function is used by OleConvertOLESTREAMToIStorage only.
7217 * Memory allocated for pData must be freed by the caller
7219 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7222 HRESULT hRes
= S_OK
;
7226 pData
->pData
= NULL
;
7227 pData
->pstrOleObjFileName
= NULL
;
7229 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7232 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7233 if(dwSize
!= sizeof(pData
->dwOleID
))
7235 hRes
= CONVERT10_E_OLESTREAM_GET
;
7237 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7239 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7250 /* Get the TypeID... more info needed for this field */
7251 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7252 if(dwSize
!= sizeof(pData
->dwTypeID
))
7254 hRes
= CONVERT10_E_OLESTREAM_GET
;
7259 if(pData
->dwTypeID
!= 0)
7261 /* Get the length of the OleTypeName */
7262 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7263 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7265 hRes
= CONVERT10_E_OLESTREAM_GET
;
7270 if(pData
->dwOleTypeNameLength
> 0)
7272 /* Get the OleTypeName */
7273 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7274 if(dwSize
!= pData
->dwOleTypeNameLength
)
7276 hRes
= CONVERT10_E_OLESTREAM_GET
;
7282 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7283 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7285 hRes
= CONVERT10_E_OLESTREAM_GET
;
7289 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7290 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7291 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7292 if(pData
->pstrOleObjFileName
)
7294 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7295 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7297 hRes
= CONVERT10_E_OLESTREAM_GET
;
7301 hRes
= CONVERT10_E_OLESTREAM_GET
;
7306 /* Get the Width of the Metafile */
7307 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7308 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7310 hRes
= CONVERT10_E_OLESTREAM_GET
;
7314 /* Get the Height of the Metafile */
7315 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7316 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7318 hRes
= CONVERT10_E_OLESTREAM_GET
;
7324 /* Get the Length of the Data */
7325 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7326 if(dwSize
!= sizeof(pData
->dwDataLength
))
7328 hRes
= CONVERT10_E_OLESTREAM_GET
;
7332 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7334 if(!bStrem1
) /* if it is a second OLE stream data */
7336 pData
->dwDataLength
-= 8;
7337 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7338 if(dwSize
!= sizeof(pData
->strUnknown
))
7340 hRes
= CONVERT10_E_OLESTREAM_GET
;
7346 if(pData
->dwDataLength
> 0)
7348 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7350 /* Get Data (ex. IStorage, Metafile, or BMP) */
7353 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7354 if(dwSize
!= pData
->dwDataLength
)
7356 hRes
= CONVERT10_E_OLESTREAM_GET
;
7361 hRes
= CONVERT10_E_OLESTREAM_GET
;
7370 /*************************************************************************
7371 * OLECONVERT_SaveOLE10 [Internal]
7373 * Saves the OLE10 STREAM From memory
7376 * pData [I] Data Structure for the OLESTREAM Data
7377 * pOleStream [I] The OLESTREAM to save
7381 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7384 * This function is used by OleConvertIStorageToOLESTREAM only.
7387 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7390 HRESULT hRes
= S_OK
;
7394 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7395 if(dwSize
!= sizeof(pData
->dwOleID
))
7397 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7402 /* Set the TypeID */
7403 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7404 if(dwSize
!= sizeof(pData
->dwTypeID
))
7406 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7410 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7412 /* Set the Length of the OleTypeName */
7413 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7414 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7416 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7421 if(pData
->dwOleTypeNameLength
> 0)
7423 /* Set the OleTypeName */
7424 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7425 if(dwSize
!= pData
->dwOleTypeNameLength
)
7427 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7434 /* Set the width of the Metafile */
7435 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7436 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7438 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7444 /* Set the height of the Metafile */
7445 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7446 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7448 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7454 /* Set the length of the Data */
7455 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7456 if(dwSize
!= sizeof(pData
->dwDataLength
))
7458 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7464 if(pData
->dwDataLength
> 0)
7466 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7467 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7468 if(dwSize
!= pData
->dwDataLength
)
7470 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7478 /*************************************************************************
7479 * OLECONVERT_GetOLE20FromOLE10[Internal]
7481 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7482 * opens it, and copies the content to the dest IStorage for
7483 * OleConvertOLESTREAMToIStorage
7487 * pDestStorage [I] The IStorage to copy the data to
7488 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7489 * nBufferLength [I] The size of the buffer
7498 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7502 IStorage
*pTempStorage
;
7503 DWORD dwNumOfBytesWritten
;
7504 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7505 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7507 /* Create a temp File */
7508 GetTempPathW(MAX_PATH
, wstrTempDir
);
7509 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7510 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7512 if(hFile
!= INVALID_HANDLE_VALUE
)
7514 /* Write IStorage Data to File */
7515 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7518 /* Open and copy temp storage to the Dest Storage */
7519 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7522 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7523 IStorage_Release(pTempStorage
);
7525 DeleteFileW(wstrTempFile
);
7530 /*************************************************************************
7531 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7533 * Saves the OLE10 STREAM From memory
7536 * pStorage [I] The Src IStorage to copy
7537 * pData [I] The Dest Memory to write to.
7540 * The size in bytes allocated for pData
7543 * Memory allocated for pData must be freed by the caller
7545 * Used by OleConvertIStorageToOLESTREAM only.
7548 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7552 DWORD nDataLength
= 0;
7553 IStorage
*pTempStorage
;
7554 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7555 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7559 /* Create temp Storage */
7560 GetTempPathW(MAX_PATH
, wstrTempDir
);
7561 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7562 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7566 /* Copy Src Storage to the Temp Storage */
7567 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7568 IStorage_Release(pTempStorage
);
7570 /* Open Temp Storage as a file and copy to memory */
7571 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7572 if(hFile
!= INVALID_HANDLE_VALUE
)
7574 nDataLength
= GetFileSize(hFile
, NULL
);
7575 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7576 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7579 DeleteFileW(wstrTempFile
);
7584 /*************************************************************************
7585 * OLECONVERT_CreateOleStream [Internal]
7587 * Creates the "\001OLE" stream in the IStorage if necessary.
7590 * pStorage [I] Dest storage to create the stream in
7596 * This function is used by OleConvertOLESTREAMToIStorage only.
7598 * This stream is still unknown, MS Word seems to have extra data
7599 * but since the data is stored in the OLESTREAM there should be
7600 * no need to recreate the stream. If the stream is manually
7601 * deleted it will create it with this default data.
7604 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7608 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7609 BYTE pOleStreamHeader
[] =
7611 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7613 0x00, 0x00, 0x00, 0x00
7616 /* Create stream if not present */
7617 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7618 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7622 /* Write default Data */
7623 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7624 IStream_Release(pStream
);
7628 /* write a string to a stream, preceded by its length */
7629 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7636 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7637 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7642 str
= CoTaskMemAlloc( len
);
7643 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7644 r
= IStream_Write( stm
, str
, len
, NULL
);
7645 CoTaskMemFree( str
);
7649 /* read a string preceded by its length from a stream */
7650 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7653 DWORD len
, count
= 0;
7657 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7660 if( count
!= sizeof(len
) )
7661 return E_OUTOFMEMORY
;
7663 TRACE("%d bytes\n",len
);
7665 str
= CoTaskMemAlloc( len
);
7667 return E_OUTOFMEMORY
;
7669 r
= IStream_Read( stm
, str
, len
, &count
);
7674 CoTaskMemFree( str
);
7675 return E_OUTOFMEMORY
;
7678 TRACE("Read string %s\n",debugstr_an(str
,len
));
7680 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7681 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7683 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7684 CoTaskMemFree( str
);
7692 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7693 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7697 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7699 static const BYTE unknown1
[12] =
7700 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7701 0xFF, 0xFF, 0xFF, 0xFF};
7702 static const BYTE unknown2
[16] =
7703 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7704 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7706 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7707 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7708 debugstr_w(szProgIDName
));
7710 /* Create a CompObj stream */
7711 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7712 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7716 /* Write CompObj Structure to stream */
7717 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7719 if( SUCCEEDED( r
) )
7720 r
= WriteClassStm( pstm
, clsid
);
7722 if( SUCCEEDED( r
) )
7723 r
= STREAM_WriteString( pstm
, lpszUserType
);
7724 if( SUCCEEDED( r
) )
7725 r
= STREAM_WriteString( pstm
, szClipName
);
7726 if( SUCCEEDED( r
) )
7727 r
= STREAM_WriteString( pstm
, szProgIDName
);
7728 if( SUCCEEDED( r
) )
7729 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7731 IStream_Release( pstm
);
7736 /***********************************************************************
7737 * WriteFmtUserTypeStg (OLE32.@)
7739 HRESULT WINAPI
WriteFmtUserTypeStg(
7740 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7743 WCHAR szwClipName
[0x40];
7744 CLSID clsid
= CLSID_NULL
;
7745 LPWSTR wstrProgID
= NULL
;
7748 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7750 /* get the clipboard format name */
7751 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7754 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7756 /* FIXME: There's room to save a CLSID and its ProgID, but
7757 the CLSID is not looked up in the registry and in all the
7758 tests I wrote it was CLSID_NULL. Where does it come from?
7761 /* get the real program ID. This may fail, but that's fine */
7762 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7764 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7766 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7767 lpszUserType
, szwClipName
, wstrProgID
);
7769 CoTaskMemFree(wstrProgID
);
7775 /******************************************************************************
7776 * ReadFmtUserTypeStg [OLE32.@]
7778 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7782 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7783 unsigned char unknown1
[12];
7784 unsigned char unknown2
[16];
7786 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7789 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7791 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7792 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7795 WARN("Failed to open stream r = %08x\n", r
);
7799 /* read the various parts of the structure */
7800 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7801 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7803 r
= ReadClassStm( stm
, &clsid
);
7807 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7811 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7815 r
= STREAM_ReadString( stm
, &szProgIDName
);
7819 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7820 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7823 /* ok, success... now we just need to store what we found */
7825 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7826 CoTaskMemFree( szOleTypeName
);
7828 if( lplpszUserType
)
7829 *lplpszUserType
= szCLSIDName
;
7830 CoTaskMemFree( szProgIDName
);
7833 IStream_Release( stm
);
7839 /*************************************************************************
7840 * OLECONVERT_CreateCompObjStream [Internal]
7842 * Creates a "\001CompObj" is the destination IStorage if necessary.
7845 * pStorage [I] The dest IStorage to create the CompObj Stream
7847 * strOleTypeName [I] The ProgID
7851 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7854 * This function is used by OleConvertOLESTREAMToIStorage only.
7856 * The stream data is stored in the OLESTREAM and there should be
7857 * no need to recreate the stream. If the stream is manually
7858 * deleted it will attempt to create it by querying the registry.
7862 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7865 HRESULT hStorageRes
, hRes
= S_OK
;
7866 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7867 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7868 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7870 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7871 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7873 /* Initialize the CompObj structure */
7874 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7875 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7876 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7879 /* Create a CompObj stream if it doesn't exist */
7880 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7881 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7882 if(hStorageRes
== S_OK
)
7884 /* copy the OleTypeName to the compobj struct */
7885 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7886 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7888 /* copy the OleTypeName to the compobj struct */
7889 /* Note: in the test made, these were Identical */
7890 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7891 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7894 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7895 bufferW
, OLESTREAM_MAX_STR_LEN
);
7896 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7902 /* Get the CLSID Default Name from the Registry */
7903 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7904 if(hErr
== ERROR_SUCCESS
)
7906 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7907 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7908 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7909 if(hErr
== ERROR_SUCCESS
)
7911 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7917 /* Write CompObj Structure to stream */
7918 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7920 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7922 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7923 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7925 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7927 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7928 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7930 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7932 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7933 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7935 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7937 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7938 IStream_Release(pStream
);
7944 /*************************************************************************
7945 * OLECONVERT_CreateOlePresStream[Internal]
7947 * Creates the "\002OlePres000" Stream with the Metafile data
7950 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7951 * dwExtentX [I] Width of the Metafile
7952 * dwExtentY [I] Height of the Metafile
7953 * pData [I] Metafile data
7954 * dwDataLength [I] Size of the Metafile data
7958 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7961 * This function is used by OleConvertOLESTREAMToIStorage only.
7964 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7968 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7969 BYTE pOlePresStreamHeader
[] =
7971 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7972 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7973 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7974 0x00, 0x00, 0x00, 0x00
7977 BYTE pOlePresStreamHeaderEmpty
[] =
7979 0x00, 0x00, 0x00, 0x00,
7980 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7981 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7982 0x00, 0x00, 0x00, 0x00
7985 /* Create the OlePres000 Stream */
7986 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7987 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7992 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7994 memset(&OlePres
, 0, sizeof(OlePres
));
7995 /* Do we have any metafile data to save */
7996 if(dwDataLength
> 0)
7998 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7999 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8003 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8004 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8006 /* Set width and height of the metafile */
8007 OlePres
.dwExtentX
= dwExtentX
;
8008 OlePres
.dwExtentY
= -dwExtentY
;
8010 /* Set Data and Length */
8011 if(dwDataLength
> sizeof(METAFILEPICT16
))
8013 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8014 OlePres
.pData
= &(pData
[8]);
8016 /* Save OlePres000 Data to Stream */
8017 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8018 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8019 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8020 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8021 if(OlePres
.dwSize
> 0)
8023 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8025 IStream_Release(pStream
);
8029 /*************************************************************************
8030 * OLECONVERT_CreateOle10NativeStream [Internal]
8032 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8035 * pStorage [I] Dest storage to create the stream in
8036 * pData [I] Ole10 Native Data (ex. bmp)
8037 * dwDataLength [I] Size of the Ole10 Native Data
8043 * This function is used by OleConvertOLESTREAMToIStorage only.
8045 * Might need to verify the data and return appropriate error message
8048 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8052 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8054 /* Create the Ole10Native Stream */
8055 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8056 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8060 /* Write info to stream */
8061 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8062 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8063 IStream_Release(pStream
);
8068 /*************************************************************************
8069 * OLECONVERT_GetOLE10ProgID [Internal]
8071 * Finds the ProgID (or OleTypeID) from the IStorage
8074 * pStorage [I] The Src IStorage to get the ProgID
8075 * strProgID [I] the ProgID string to get
8076 * dwSize [I] the size of the string
8080 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8083 * This function is used by OleConvertIStorageToOLESTREAM only.
8087 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8091 LARGE_INTEGER iSeekPos
;
8092 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8093 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8095 /* Open the CompObj Stream */
8096 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8097 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8101 /*Get the OleType from the CompObj Stream */
8102 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8103 iSeekPos
.u
.HighPart
= 0;
8105 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8106 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8107 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8108 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8109 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8110 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8111 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8113 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8116 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8118 IStream_Release(pStream
);
8123 LPOLESTR wstrProgID
;
8125 /* Get the OleType from the registry */
8126 REFCLSID clsid
= &(stat
.clsid
);
8127 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8128 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8131 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8138 /*************************************************************************
8139 * OLECONVERT_GetOle10PresData [Internal]
8141 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8144 * pStorage [I] Src IStroage
8145 * pOleStream [I] Dest OleStream Mem Struct
8151 * This function is used by OleConvertIStorageToOLESTREAM only.
8153 * Memory allocated for pData must be freed by the caller
8157 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8162 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8164 /* Initialize Default data for OLESTREAM */
8165 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8166 pOleStreamData
[0].dwTypeID
= 2;
8167 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8168 pOleStreamData
[1].dwTypeID
= 0;
8169 pOleStreamData
[0].dwMetaFileWidth
= 0;
8170 pOleStreamData
[0].dwMetaFileHeight
= 0;
8171 pOleStreamData
[0].pData
= NULL
;
8172 pOleStreamData
[1].pData
= NULL
;
8174 /* Open Ole10Native Stream */
8175 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8176 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8180 /* Read Size and Data */
8181 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8182 if(pOleStreamData
->dwDataLength
> 0)
8184 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8185 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8187 IStream_Release(pStream
);
8193 /*************************************************************************
8194 * OLECONVERT_GetOle20PresData[Internal]
8196 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8199 * pStorage [I] Src IStroage
8200 * pOleStreamData [I] Dest OleStream Mem Struct
8206 * This function is used by OleConvertIStorageToOLESTREAM only.
8208 * Memory allocated for pData must be freed by the caller
8210 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8214 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8215 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8217 /* Initialize Default data for OLESTREAM */
8218 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8219 pOleStreamData
[0].dwTypeID
= 2;
8220 pOleStreamData
[0].dwMetaFileWidth
= 0;
8221 pOleStreamData
[0].dwMetaFileHeight
= 0;
8222 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8223 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8224 pOleStreamData
[1].dwTypeID
= 0;
8225 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8226 pOleStreamData
[1].strOleTypeName
[0] = 0;
8227 pOleStreamData
[1].dwMetaFileWidth
= 0;
8228 pOleStreamData
[1].dwMetaFileHeight
= 0;
8229 pOleStreamData
[1].pData
= NULL
;
8230 pOleStreamData
[1].dwDataLength
= 0;
8233 /* Open OlePress000 stream */
8234 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8235 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8238 LARGE_INTEGER iSeekPos
;
8239 METAFILEPICT16 MetaFilePict
;
8240 static const char strMetafilePictName
[] = "METAFILEPICT";
8242 /* Set the TypeID for a Metafile */
8243 pOleStreamData
[1].dwTypeID
= 5;
8245 /* Set the OleTypeName to Metafile */
8246 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8247 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8249 iSeekPos
.u
.HighPart
= 0;
8250 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8252 /* Get Presentation Data */
8253 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8254 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8255 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8256 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8258 /*Set width and Height */
8259 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8260 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8261 if(olePress
.dwSize
> 0)
8264 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8266 /* Set MetaFilePict struct */
8267 MetaFilePict
.mm
= 8;
8268 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8269 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8270 MetaFilePict
.hMF
= 0;
8272 /* Get Metafile Data */
8273 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8274 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8275 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8277 IStream_Release(pStream
);
8281 /*************************************************************************
8282 * OleConvertOLESTREAMToIStorage [OLE32.@]
8287 * DVTARGETDEVICE parameter is not handled
8288 * Still unsure of some mem fields for OLE 10 Stream
8289 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8290 * and "\001OLE" streams
8293 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8294 LPOLESTREAM pOleStream
,
8296 const DVTARGETDEVICE
* ptd
)
8300 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8302 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8304 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8308 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8311 if(pstg
== NULL
|| pOleStream
== NULL
)
8313 hRes
= E_INVALIDARG
;
8318 /* Load the OLESTREAM to Memory */
8319 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8324 /* Load the OLESTREAM to Memory (part 2)*/
8325 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8331 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8333 /* Do we have the IStorage Data in the OLESTREAM */
8334 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8336 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8337 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8341 /* It must be an original OLE 1.0 source */
8342 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8347 /* It must be an original OLE 1.0 source */
8348 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8351 /* Create CompObj Stream if necessary */
8352 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8355 /*Create the Ole Stream if necessary */
8356 OLECONVERT_CreateOleStream(pstg
);
8361 /* Free allocated memory */
8362 for(i
=0; i
< 2; i
++)
8364 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8365 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8366 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8371 /*************************************************************************
8372 * OleConvertIStorageToOLESTREAM [OLE32.@]
8379 * Still unsure of some mem fields for OLE 10 Stream
8380 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8381 * and "\001OLE" streams.
8384 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8386 LPOLESTREAM pOleStream
)
8389 HRESULT hRes
= S_OK
;
8391 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8392 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8394 TRACE("%p %p\n", pstg
, pOleStream
);
8396 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8398 if(pstg
== NULL
|| pOleStream
== NULL
)
8400 hRes
= E_INVALIDARG
;
8404 /* Get the ProgID */
8405 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8406 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8410 /* Was it originally Ole10 */
8411 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8414 IStream_Release(pStream
);
8415 /* Get Presentation Data for Ole10Native */
8416 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8420 /* Get Presentation Data (OLE20) */
8421 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8424 /* Save OLESTREAM */
8425 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8428 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8433 /* Free allocated memory */
8434 for(i
=0; i
< 2; i
++)
8436 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8442 /***********************************************************************
8443 * GetConvertStg (OLE32.@)
8445 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8446 FIXME("unimplemented stub!\n");
8450 /******************************************************************************
8451 * StgIsStorageFile [OLE32.@]
8452 * Verify if the file contains a storage object
8458 * S_OK if file has magic bytes as a storage object
8459 * S_FALSE if file is not storage
8462 StgIsStorageFile(LPCOLESTR fn
)
8468 TRACE("%s\n", debugstr_w(fn
));
8469 hf
= CreateFileW(fn
, GENERIC_READ
,
8470 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8471 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8473 if (hf
== INVALID_HANDLE_VALUE
)
8474 return STG_E_FILENOTFOUND
;
8476 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8478 WARN(" unable to read file\n");
8485 if (bytes_read
!= 8) {
8486 TRACE(" too short\n");
8490 if (!memcmp(magic
,STORAGE_magic
,8)) {
8495 TRACE(" -> Invalid header.\n");
8499 /***********************************************************************
8500 * WriteClassStm (OLE32.@)
8502 * Writes a CLSID to a stream.
8505 * pStm [I] Stream to write to.
8506 * rclsid [I] CLSID to write.
8510 * Failure: HRESULT code.
8512 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8514 TRACE("(%p,%p)\n",pStm
,rclsid
);
8516 if (!pStm
|| !rclsid
)
8517 return E_INVALIDARG
;
8519 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8522 /***********************************************************************
8523 * ReadClassStm (OLE32.@)
8525 * Reads a CLSID from a stream.
8528 * pStm [I] Stream to read from.
8529 * rclsid [O] CLSID to read.
8533 * Failure: HRESULT code.
8535 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8540 TRACE("(%p,%p)\n",pStm
,pclsid
);
8542 if (!pStm
|| !pclsid
)
8543 return E_INVALIDARG
;
8545 /* clear the output args */
8546 *pclsid
= CLSID_NULL
;
8548 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8553 if (nbByte
!= sizeof(CLSID
))
8554 return STG_E_READFAULT
;