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 */
262 * The current implementation of the IEnumSTATSTGImpl class uses a stack
263 * to walk the directory entries to get the content of a storage. This stack
264 * is implemented by the following 3 data members
268 DirRef
* stackToVisit
;
270 #define ENUMSTATSGT_SIZE_INCREMENT 10
274 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
275 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
276 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, DirRef nodeToPush
);
277 static DirRef
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
279 /************************************************************************
283 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
285 if (index
== 0xffffffff)
290 return index
* BIG_BLOCK_SIZE
;
293 /************************************************************************
294 ** Storage32BaseImpl implementation
296 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
297 ULARGE_INTEGER offset
,
302 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
305 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
306 ULARGE_INTEGER offset
,
311 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
314 /************************************************************************
315 * Storage32BaseImpl_QueryInterface (IUnknown)
317 * This method implements the common QueryInterface for all IStorage32
318 * implementations contained in this file.
320 * See Windows documentation for more details on IUnknown methods.
322 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
327 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
329 if ( (This
==0) || (ppvObject
==0) )
334 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
335 IsEqualGUID(&IID_IStorage
, riid
))
339 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
341 *ppvObject
= &This
->pssVtbl
;
345 return E_NOINTERFACE
;
347 IStorage_AddRef(iface
);
352 /************************************************************************
353 * Storage32BaseImpl_AddRef (IUnknown)
355 * This method implements the common AddRef for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI
StorageBaseImpl_AddRef(
363 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
364 ULONG ref
= InterlockedIncrement(&This
->ref
);
366 TRACE("(%p) AddRef to %d\n", This
, ref
);
371 /************************************************************************
372 * Storage32BaseImpl_Release (IUnknown)
374 * This method implements the common Release for all IStorage32
375 * implementations contained in this file.
377 * See Windows documentation for more details on IUnknown methods.
379 static ULONG WINAPI
StorageBaseImpl_Release(
382 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
384 ULONG ref
= InterlockedDecrement(&This
->ref
);
386 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
391 * Since we are using a system of base-classes, we want to call the
392 * destructor of the appropriate derived class. To do this, we are
393 * using virtual functions to implement the destructor.
395 StorageBaseImpl_Destroy(This
);
401 /************************************************************************
402 * Storage32BaseImpl_OpenStream (IStorage)
404 * This method will open the specified stream object from the current storage.
406 * See Windows documentation for more details on IStorage methods.
408 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
410 const OLECHAR
* pwcsName
, /* [string][in] */
411 void* reserved1
, /* [unique][in] */
412 DWORD grfMode
, /* [in] */
413 DWORD reserved2
, /* [in] */
414 IStream
** ppstm
) /* [out] */
416 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
417 StgStreamImpl
* newStream
;
418 DirEntry currentEntry
;
419 DirRef streamEntryRef
;
420 HRESULT res
= STG_E_UNKNOWN
;
422 TRACE("(%p, %s, %p, %x, %d, %p)\n",
423 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
425 if ( (pwcsName
==NULL
) || (ppstm
==0) )
433 if ( FAILED( validateSTGM(grfMode
) ) ||
434 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
436 res
= STG_E_INVALIDFLAG
;
443 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
445 res
= STG_E_INVALIDFUNCTION
;
451 res
= STG_E_REVERTED
;
456 * Check that we're compatible with the parent's storage mode, but
457 * only if we are not in transacted mode
459 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
460 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
462 res
= STG_E_ACCESSDENIED
;
468 * Search for the element with the given name
470 streamEntryRef
= findElement(
472 This
->storageDirEntry
,
477 * If it was found, construct the stream object and return a pointer to it.
479 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
480 (currentEntry
.stgType
==STGTY_STREAM
) )
482 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
484 /* A single stream cannot be opened a second time. */
485 res
= STG_E_ACCESSDENIED
;
489 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
493 newStream
->grfMode
= grfMode
;
494 *ppstm
= (IStream
*)newStream
;
496 IStream_AddRef(*ppstm
);
506 res
= STG_E_FILENOTFOUND
;
510 TRACE("<-- IStream %p\n", *ppstm
);
511 TRACE("<-- %08x\n", res
);
515 /************************************************************************
516 * Storage32BaseImpl_OpenStorage (IStorage)
518 * This method will open a new storage object from the current storage.
520 * See Windows documentation for more details on IStorage methods.
522 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
524 const OLECHAR
* pwcsName
, /* [string][unique][in] */
525 IStorage
* pstgPriority
, /* [unique][in] */
526 DWORD grfMode
, /* [in] */
527 SNB snbExclude
, /* [unique][in] */
528 DWORD reserved
, /* [in] */
529 IStorage
** ppstg
) /* [out] */
531 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
532 StorageInternalImpl
* newStorage
;
533 StorageBaseImpl
* newTransactedStorage
;
534 DirEntry currentEntry
;
535 DirRef storageEntryRef
;
536 HRESULT res
= STG_E_UNKNOWN
;
538 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
539 iface
, debugstr_w(pwcsName
), pstgPriority
,
540 grfMode
, snbExclude
, reserved
, ppstg
);
542 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
548 if (This
->openFlags
& STGM_SIMPLE
)
550 res
= STG_E_INVALIDFUNCTION
;
555 if (snbExclude
!= NULL
)
557 res
= STG_E_INVALIDPARAMETER
;
561 if ( FAILED( validateSTGM(grfMode
) ))
563 res
= STG_E_INVALIDFLAG
;
570 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
571 (grfMode
& STGM_DELETEONRELEASE
) ||
572 (grfMode
& STGM_PRIORITY
) )
574 res
= STG_E_INVALIDFUNCTION
;
579 return STG_E_REVERTED
;
582 * Check that we're compatible with the parent's storage mode,
583 * but only if we are not transacted
585 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
586 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
588 res
= STG_E_ACCESSDENIED
;
595 storageEntryRef
= findElement(
597 This
->storageDirEntry
,
601 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
602 (currentEntry
.stgType
==STGTY_STORAGE
) )
604 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
606 /* A single storage cannot be opened a second time. */
607 res
= STG_E_ACCESSDENIED
;
611 newStorage
= StorageInternalImpl_Construct(
618 if (grfMode
& STGM_TRANSACTED
)
620 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
624 HeapFree(GetProcessHeap(), 0, newStorage
);
628 *ppstg
= (IStorage
*)newTransactedStorage
;
632 *ppstg
= (IStorage
*)newStorage
;
635 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
641 res
= STG_E_INSUFFICIENTMEMORY
;
645 res
= STG_E_FILENOTFOUND
;
648 TRACE("<-- %08x\n", res
);
652 /************************************************************************
653 * Storage32BaseImpl_EnumElements (IStorage)
655 * This method will create an enumerator object that can be used to
656 * retrieve information about all the elements in the storage object.
658 * See Windows documentation for more details on IStorage methods.
660 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
662 DWORD reserved1
, /* [in] */
663 void* reserved2
, /* [size_is][unique][in] */
664 DWORD reserved3
, /* [in] */
665 IEnumSTATSTG
** ppenum
) /* [out] */
667 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
668 IEnumSTATSTGImpl
* newEnum
;
670 TRACE("(%p, %d, %p, %d, %p)\n",
671 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
673 if ( (This
==0) || (ppenum
==0))
677 return STG_E_REVERTED
;
679 newEnum
= IEnumSTATSTGImpl_Construct(
681 This
->storageDirEntry
);
685 *ppenum
= (IEnumSTATSTG
*)newEnum
;
687 IEnumSTATSTG_AddRef(*ppenum
);
692 return E_OUTOFMEMORY
;
695 /************************************************************************
696 * Storage32BaseImpl_Stat (IStorage)
698 * This method will retrieve information about this storage object.
700 * See Windows documentation for more details on IStorage methods.
702 static HRESULT WINAPI
StorageBaseImpl_Stat(
704 STATSTG
* pstatstg
, /* [out] */
705 DWORD grfStatFlag
) /* [in] */
707 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
708 DirEntry currentEntry
;
709 HRESULT res
= STG_E_UNKNOWN
;
711 TRACE("(%p, %p, %x)\n",
712 iface
, pstatstg
, grfStatFlag
);
714 if ( (This
==0) || (pstatstg
==0))
722 res
= STG_E_REVERTED
;
726 res
= StorageBaseImpl_ReadDirEntry(
728 This
->storageDirEntry
,
733 StorageUtl_CopyDirEntryToSTATSTG(
739 pstatstg
->grfMode
= This
->openFlags
;
740 pstatstg
->grfStateBits
= This
->stateBits
;
746 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
);
748 TRACE("<-- %08x\n", res
);
752 /************************************************************************
753 * Storage32BaseImpl_RenameElement (IStorage)
755 * This method will rename the specified element.
757 * See Windows documentation for more details on IStorage methods.
759 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
761 const OLECHAR
* pwcsOldName
, /* [in] */
762 const OLECHAR
* pwcsNewName
) /* [in] */
764 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
765 DirEntry currentEntry
;
766 DirRef currentEntryRef
;
768 TRACE("(%p, %s, %s)\n",
769 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
772 return STG_E_REVERTED
;
774 currentEntryRef
= findElement(This
,
775 This
->storageDirEntry
,
779 if (currentEntryRef
!= DIRENTRY_NULL
)
782 * There is already an element with the new name
784 return STG_E_FILEALREADYEXISTS
;
788 * Search for the old element name
790 currentEntryRef
= findElement(This
,
791 This
->storageDirEntry
,
795 if (currentEntryRef
!= DIRENTRY_NULL
)
797 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
798 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
800 WARN("Element is already open; cannot rename.\n");
801 return STG_E_ACCESSDENIED
;
804 /* Remove the element from its current position in the tree */
805 removeFromTree(This
, This
->storageDirEntry
,
808 /* Change the name of the element */
809 strcpyW(currentEntry
.name
, pwcsNewName
);
811 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
814 /* Insert the element in a new position in the tree */
815 insertIntoTree(This
, This
->storageDirEntry
,
821 * There is no element with the old name
823 return STG_E_FILENOTFOUND
;
829 /************************************************************************
830 * Storage32BaseImpl_CreateStream (IStorage)
832 * This method will create a stream object within this storage
834 * See Windows documentation for more details on IStorage methods.
836 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
838 const OLECHAR
* pwcsName
, /* [string][in] */
839 DWORD grfMode
, /* [in] */
840 DWORD reserved1
, /* [in] */
841 DWORD reserved2
, /* [in] */
842 IStream
** ppstm
) /* [out] */
844 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
845 StgStreamImpl
* newStream
;
846 DirEntry currentEntry
, newStreamEntry
;
847 DirRef currentEntryRef
, newStreamEntryRef
;
849 TRACE("(%p, %s, %x, %d, %d, %p)\n",
850 iface
, debugstr_w(pwcsName
), grfMode
,
851 reserved1
, reserved2
, ppstm
);
854 return STG_E_INVALIDPOINTER
;
857 return STG_E_INVALIDNAME
;
859 if (reserved1
|| reserved2
)
860 return STG_E_INVALIDPARAMETER
;
862 if ( FAILED( validateSTGM(grfMode
) ))
863 return STG_E_INVALIDFLAG
;
865 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
866 return STG_E_INVALIDFLAG
;
869 return STG_E_REVERTED
;
874 if ((grfMode
& STGM_DELETEONRELEASE
) ||
875 (grfMode
& STGM_TRANSACTED
))
876 return STG_E_INVALIDFUNCTION
;
878 /* Can't create a stream on read-only storage */
879 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
880 return STG_E_ACCESSDENIED
;
883 * Check that we're compatible with the parent's storage mode
884 * if not in transacted mode
886 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
887 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
888 return STG_E_ACCESSDENIED
;
891 if(This
->openFlags
& STGM_SIMPLE
)
892 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
896 currentEntryRef
= findElement(This
,
897 This
->storageDirEntry
,
901 if (currentEntryRef
!= DIRENTRY_NULL
)
904 * An element with this name already exists
906 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
908 IStorage_DestroyElement(iface
, pwcsName
);
911 return STG_E_FILEALREADYEXISTS
;
913 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
915 WARN("read-only storage\n");
916 return STG_E_ACCESSDENIED
;
920 * memset the empty entry
922 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
924 newStreamEntry
.sizeOfNameString
=
925 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
927 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
928 return STG_E_INVALIDNAME
;
930 strcpyW(newStreamEntry
.name
, pwcsName
);
932 newStreamEntry
.stgType
= STGTY_STREAM
;
933 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
934 newStreamEntry
.size
.u
.LowPart
= 0;
935 newStreamEntry
.size
.u
.HighPart
= 0;
937 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
938 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
939 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
941 /* call CoFileTime to get the current time
946 /* newStreamEntry.clsid */
949 * Create an entry with the new data
951 StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
953 * Insert the new entry in the parent storage's tree.
957 This
->storageDirEntry
,
961 * Open the stream to return it.
963 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
967 *ppstm
= (IStream
*)newStream
;
969 IStream_AddRef(*ppstm
);
973 return STG_E_INSUFFICIENTMEMORY
;
979 /************************************************************************
980 * Storage32BaseImpl_SetClass (IStorage)
982 * This method will write the specified CLSID in the directory entry of this
985 * See Windows documentation for more details on IStorage methods.
987 static HRESULT WINAPI
StorageBaseImpl_SetClass(
989 REFCLSID clsid
) /* [in] */
991 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
993 DirEntry currentEntry
;
995 TRACE("(%p, %p)\n", iface
, clsid
);
998 return STG_E_REVERTED
;
1000 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1001 This
->storageDirEntry
,
1003 if (SUCCEEDED(hRes
))
1005 currentEntry
.clsid
= *clsid
;
1007 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1008 This
->storageDirEntry
,
1015 /************************************************************************
1016 ** Storage32Impl implementation
1019 /************************************************************************
1020 * Storage32BaseImpl_CreateStorage (IStorage)
1022 * This method will create the storage object within the provided storage.
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1028 const OLECHAR
*pwcsName
, /* [string][in] */
1029 DWORD grfMode
, /* [in] */
1030 DWORD reserved1
, /* [in] */
1031 DWORD reserved2
, /* [in] */
1032 IStorage
**ppstg
) /* [out] */
1034 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1036 DirEntry currentEntry
;
1038 DirRef currentEntryRef
;
1042 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1043 iface
, debugstr_w(pwcsName
), grfMode
,
1044 reserved1
, reserved2
, ppstg
);
1047 return STG_E_INVALIDPOINTER
;
1049 if (This
->openFlags
& STGM_SIMPLE
)
1051 return STG_E_INVALIDFUNCTION
;
1055 return STG_E_INVALIDNAME
;
1059 if ( FAILED( validateSTGM(grfMode
) ) ||
1060 (grfMode
& STGM_DELETEONRELEASE
) )
1062 WARN("bad grfMode: 0x%x\n", grfMode
);
1063 return STG_E_INVALIDFLAG
;
1067 return STG_E_REVERTED
;
1070 * Check that we're compatible with the parent's storage mode
1072 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1074 WARN("access denied\n");
1075 return STG_E_ACCESSDENIED
;
1078 currentEntryRef
= findElement(This
,
1079 This
->storageDirEntry
,
1083 if (currentEntryRef
!= DIRENTRY_NULL
)
1086 * An element with this name already exists
1088 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1089 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
)
1091 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS
;
1101 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1103 WARN("read-only storage\n");
1104 return STG_E_ACCESSDENIED
;
1107 memset(&newEntry
, 0, sizeof(DirEntry
));
1109 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1111 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1113 FIXME("name too long\n");
1114 return STG_E_INVALIDNAME
;
1117 strcpyW(newEntry
.name
, pwcsName
);
1119 newEntry
.stgType
= STGTY_STORAGE
;
1120 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1121 newEntry
.size
.u
.LowPart
= 0;
1122 newEntry
.size
.u
.HighPart
= 0;
1124 newEntry
.leftChild
= DIRENTRY_NULL
;
1125 newEntry
.rightChild
= DIRENTRY_NULL
;
1126 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1128 /* call CoFileTime to get the current time
1133 /* newEntry.clsid */
1136 * Create a new directory entry for the storage
1138 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1141 * Insert the new directory entry into the parent storage's tree
1145 This
->storageDirEntry
,
1149 * Open it to get a pointer to return.
1151 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1153 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1163 /***************************************************************************
1167 * Reserve a directory entry in the file and initialize it.
1169 static HRESULT
StorageImpl_CreateDirEntry(
1170 StorageBaseImpl
*base
,
1171 const DirEntry
*newData
,
1174 StorageImpl
*storage
= (StorageImpl
*)base
;
1175 ULONG currentEntryIndex
= 0;
1176 ULONG newEntryIndex
= DIRENTRY_NULL
;
1178 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1179 WORD sizeOfNameString
;
1183 hr
= StorageImpl_ReadRawDirEntry(storage
,
1189 StorageUtl_ReadWord(
1191 OFFSET_PS_NAMELENGTH
,
1194 if (sizeOfNameString
== 0)
1197 * The entry exists and is available, we found it.
1199 newEntryIndex
= currentEntryIndex
;
1205 * We exhausted the directory entries, we will create more space below
1207 newEntryIndex
= currentEntryIndex
;
1209 currentEntryIndex
++;
1211 } while (newEntryIndex
== DIRENTRY_NULL
);
1214 * grow the directory stream
1218 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1219 ULARGE_INTEGER newSize
;
1221 ULONG lastEntry
= 0;
1222 ULONG blockCount
= 0;
1225 * obtain the new count of blocks in the directory stream
1227 blockCount
= BlockChainStream_GetCount(
1228 storage
->rootBlockChain
)+1;
1231 * initialize the size used by the directory stream
1233 newSize
.u
.HighPart
= 0;
1234 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1237 * add a block to the directory stream
1239 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1242 * memset the empty entry in order to initialize the unused newly
1245 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1250 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1253 entryIndex
= newEntryIndex
+ 1;
1254 entryIndex
< lastEntry
;
1257 StorageImpl_WriteRawDirEntry(
1264 UpdateRawDirEntry(currentData
, newData
);
1266 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1269 *index
= newEntryIndex
;
1274 /***************************************************************************
1278 * Mark a directory entry in the file as free.
1280 static HRESULT
StorageImpl_DestroyDirEntry(
1281 StorageBaseImpl
*base
,
1285 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1286 StorageImpl
*storage
= (StorageImpl
*)base
;
1288 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1290 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1296 /***************************************************************************
1300 * Destroy an entry, its attached data, and all entries reachable from it.
1302 static HRESULT
DestroyReachableEntries(
1303 StorageBaseImpl
*base
,
1308 ULARGE_INTEGER zero
;
1312 if (index
!= DIRENTRY_NULL
)
1314 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1317 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1320 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1323 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1326 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1329 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1336 /****************************************************************************
1340 * Case insensitive comparison of DirEntry.name by first considering
1343 * Returns <0 when name1 < name2
1344 * >0 when name1 > name2
1345 * 0 when name1 == name2
1347 static LONG
entryNameCmp(
1348 const OLECHAR
*name1
,
1349 const OLECHAR
*name2
)
1351 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1356 * We compare the string themselves only when they are of the same length
1358 diff
= lstrcmpiW( name1
, name2
);
1364 /****************************************************************************
1368 * Add a directory entry to a storage
1370 static HRESULT
insertIntoTree(
1371 StorageBaseImpl
*This
,
1372 DirRef parentStorageIndex
,
1373 DirRef newEntryIndex
)
1375 DirEntry currentEntry
;
1379 * Read the inserted entry
1381 StorageBaseImpl_ReadDirEntry(This
,
1386 * Read the storage entry
1388 StorageBaseImpl_ReadDirEntry(This
,
1392 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1395 * The root storage contains some element, therefore, start the research
1396 * for the appropriate location.
1399 DirRef current
, next
, previous
, currentEntryId
;
1402 * Keep a reference to the root of the storage's element tree
1404 currentEntryId
= currentEntry
.dirRootEntry
;
1409 StorageBaseImpl_ReadDirEntry(This
,
1410 currentEntry
.dirRootEntry
,
1413 previous
= currentEntry
.leftChild
;
1414 next
= currentEntry
.rightChild
;
1415 current
= currentEntryId
;
1419 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1423 if (previous
!= DIRENTRY_NULL
)
1425 StorageBaseImpl_ReadDirEntry(This
,
1432 currentEntry
.leftChild
= newEntryIndex
;
1433 StorageBaseImpl_WriteDirEntry(This
,
1441 if (next
!= DIRENTRY_NULL
)
1443 StorageBaseImpl_ReadDirEntry(This
,
1450 currentEntry
.rightChild
= newEntryIndex
;
1451 StorageBaseImpl_WriteDirEntry(This
,
1460 * Trying to insert an item with the same name in the
1461 * subtree structure.
1463 return STG_E_FILEALREADYEXISTS
;
1466 previous
= currentEntry
.leftChild
;
1467 next
= currentEntry
.rightChild
;
1473 * The storage is empty, make the new entry the root of its element tree
1475 currentEntry
.dirRootEntry
= newEntryIndex
;
1476 StorageBaseImpl_WriteDirEntry(This
,
1484 /****************************************************************************
1488 * Find and read the element of a storage with the given name.
1490 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1491 const OLECHAR
*name
, DirEntry
*data
)
1493 DirRef currentEntry
;
1495 /* Read the storage entry to find the root of the tree. */
1496 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1498 currentEntry
= data
->dirRootEntry
;
1500 while (currentEntry
!= DIRENTRY_NULL
)
1504 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1506 cmp
= entryNameCmp(name
, data
->name
);
1513 currentEntry
= data
->leftChild
;
1516 currentEntry
= data
->rightChild
;
1519 return currentEntry
;
1522 /****************************************************************************
1526 * Find and read the binary tree parent of the element with the given name.
1528 * If there is no such element, find a place where it could be inserted and
1529 * return STG_E_FILENOTFOUND.
1531 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1532 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1538 /* Read the storage entry to find the root of the tree. */
1539 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1541 *parentEntry
= storageEntry
;
1542 *relation
= DIRENTRY_RELATION_DIR
;
1544 childEntry
= parentData
->dirRootEntry
;
1546 while (childEntry
!= DIRENTRY_NULL
)
1550 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1552 cmp
= entryNameCmp(childName
, childData
.name
);
1560 *parentData
= childData
;
1561 *parentEntry
= childEntry
;
1562 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1564 childEntry
= parentData
->leftChild
;
1569 *parentData
= childData
;
1570 *parentEntry
= childEntry
;
1571 *relation
= DIRENTRY_RELATION_NEXT
;
1573 childEntry
= parentData
->rightChild
;
1577 if (childEntry
== DIRENTRY_NULL
)
1578 return STG_E_FILENOTFOUND
;
1584 /*************************************************************************
1587 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1589 DWORD ciidExclude
, /* [in] */
1590 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1591 SNB snbExclude
, /* [unique][in] */
1592 IStorage
* pstgDest
) /* [unique][in] */
1594 IEnumSTATSTG
*elements
= 0;
1595 STATSTG curElement
, strStat
;
1597 IStorage
*pstgTmp
, *pstgChild
;
1598 IStream
*pstrTmp
, *pstrChild
;
1599 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1602 TRACE("(%p, %d, %p, %p, %p)\n",
1603 iface
, ciidExclude
, rgiidExclude
,
1604 snbExclude
, pstgDest
);
1606 if ( pstgDest
== 0 )
1607 return STG_E_INVALIDPOINTER
;
1610 * Enumerate the elements
1612 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1620 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1621 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1623 for(i
= 0; i
< ciidExclude
; ++i
)
1625 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1626 skip_storage
= TRUE
;
1627 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1630 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1636 * Obtain the next element
1638 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1640 if ( hr
== S_FALSE
)
1642 hr
= S_OK
; /* done, every element has been copied */
1648 WCHAR
**snb
= snbExclude
;
1650 while ( *snb
!= NULL
&& !skip
)
1652 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1661 if (curElement
.type
== STGTY_STORAGE
)
1667 * open child source storage
1669 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1670 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1671 NULL
, 0, &pstgChild
);
1677 * create a new storage in destination storage
1679 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1680 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1684 * if it already exist, don't create a new one use this one
1686 if (hr
== STG_E_FILEALREADYEXISTS
)
1688 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1689 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1690 NULL
, 0, &pstgTmp
);
1698 * do the copy recursively
1700 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1703 IStorage_Release( pstgTmp
);
1704 IStorage_Release( pstgChild
);
1706 else if (curElement
.type
== STGTY_STREAM
)
1712 * create a new stream in destination storage. If the stream already
1713 * exist, it will be deleted and a new one will be created.
1715 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1716 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1723 * open child stream storage
1725 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1726 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1733 * Get the size of the source stream
1735 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1738 * Set the size of the destination stream.
1740 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1745 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1748 IStream_Release( pstrTmp
);
1749 IStream_Release( pstrChild
);
1753 WARN("unknown element type: %d\n", curElement
.type
);
1757 CoTaskMemFree(curElement
.pwcsName
);
1758 } while (hr
== S_OK
);
1763 IEnumSTATSTG_Release(elements
);
1768 /*************************************************************************
1769 * MoveElementTo (IStorage)
1771 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1773 const OLECHAR
*pwcsName
, /* [string][in] */
1774 IStorage
*pstgDest
, /* [unique][in] */
1775 const OLECHAR
*pwcsNewName
,/* [string][in] */
1776 DWORD grfFlags
) /* [in] */
1778 FIXME("(%p %s %p %s %u): stub\n", iface
,
1779 debugstr_w(pwcsName
), pstgDest
,
1780 debugstr_w(pwcsNewName
), grfFlags
);
1784 /*************************************************************************
1787 * Ensures that any changes made to a storage object open in transacted mode
1788 * are reflected in the parent storage
1791 * Wine doesn't implement transacted mode, which seems to be a basic
1792 * optimization, so we can ignore this stub for now.
1794 static HRESULT WINAPI
StorageImpl_Commit(
1796 DWORD grfCommitFlags
)/* [in] */
1798 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1802 /*************************************************************************
1805 * Discard all changes that have been made since the last commit operation
1807 static HRESULT WINAPI
StorageImpl_Revert(
1810 FIXME("(%p): stub\n", iface
);
1814 /*************************************************************************
1815 * DestroyElement (IStorage)
1817 * Strategy: This implementation is built this way for simplicity not for speed.
1818 * I always delete the topmost element of the enumeration and adjust
1819 * the deleted element pointer all the time. This takes longer to
1820 * do but allow to reinvoke DestroyElement whenever we encounter a
1821 * storage object. The optimisation resides in the usage of another
1822 * enumeration strategy that would give all the leaves of a storage
1823 * first. (postfix order)
1825 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1827 const OLECHAR
*pwcsName
)/* [string][in] */
1829 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1832 DirEntry entryToDelete
;
1833 DirRef entryToDeleteRef
;
1836 iface
, debugstr_w(pwcsName
));
1839 return STG_E_INVALIDPOINTER
;
1842 return STG_E_REVERTED
;
1844 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1845 return STG_E_ACCESSDENIED
;
1847 entryToDeleteRef
= findElement(
1849 This
->storageDirEntry
,
1853 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1855 return STG_E_FILENOTFOUND
;
1858 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1860 hr
= deleteStorageContents(
1865 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1867 hr
= deleteStreamContents(
1877 * Remove the entry from its parent storage
1879 hr
= removeFromTree(
1881 This
->storageDirEntry
,
1885 * Invalidate the entry
1888 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1901 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1907 list_remove(&(strm
->StrmListEntry
));
1910 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1912 StgStreamImpl
*strm
;
1914 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1916 if (strm
->dirEntry
== streamEntry
)
1925 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1927 StorageInternalImpl
*childstg
;
1929 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1931 if (childstg
->base
.storageDirEntry
== storageEntry
)
1940 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1942 struct list
*cur
, *cur2
;
1943 StgStreamImpl
*strm
=NULL
;
1944 StorageInternalImpl
*childstg
=NULL
;
1946 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1947 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1948 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1949 strm
->parentStorage
= NULL
;
1953 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1954 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1955 StorageBaseImpl_Invalidate( &childstg
->base
);
1958 if (stg
->transactedChild
)
1960 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1962 stg
->transactedChild
= NULL
;
1967 /*********************************************************************
1971 * Delete the contents of a storage entry.
1974 static HRESULT
deleteStorageContents(
1975 StorageBaseImpl
*parentStorage
,
1976 DirRef indexToDelete
,
1977 DirEntry entryDataToDelete
)
1979 IEnumSTATSTG
*elements
= 0;
1980 IStorage
*childStorage
= 0;
1981 STATSTG currentElement
;
1983 HRESULT destroyHr
= S_OK
;
1984 StorageInternalImpl
*stg
, *stg2
;
1986 /* Invalidate any open storage objects. */
1987 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1989 if (stg
->base
.storageDirEntry
== indexToDelete
)
1991 StorageBaseImpl_Invalidate(&stg
->base
);
1996 * Open the storage and enumerate it
1998 hr
= StorageBaseImpl_OpenStorage(
1999 (IStorage
*)parentStorage
,
2000 entryDataToDelete
.name
,
2002 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2013 * Enumerate the elements
2015 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2020 * Obtain the next element
2022 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2025 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2027 CoTaskMemFree(currentElement
.pwcsName
);
2031 * We need to Reset the enumeration every time because we delete elements
2032 * and the enumeration could be invalid
2034 IEnumSTATSTG_Reset(elements
);
2036 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2038 IStorage_Release(childStorage
);
2039 IEnumSTATSTG_Release(elements
);
2044 /*********************************************************************
2048 * Perform the deletion of a stream's data
2051 static HRESULT
deleteStreamContents(
2052 StorageBaseImpl
*parentStorage
,
2053 DirRef indexToDelete
,
2054 DirEntry entryDataToDelete
)
2058 ULARGE_INTEGER size
;
2059 StgStreamImpl
*strm
, *strm2
;
2061 /* Invalidate any open stream objects. */
2062 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2064 if (strm
->dirEntry
== indexToDelete
)
2066 TRACE("Stream deleted %p\n", strm
);
2067 strm
->parentStorage
= NULL
;
2068 list_remove(&strm
->StrmListEntry
);
2072 size
.u
.HighPart
= 0;
2075 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2076 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2086 hr
= IStream_SetSize(pis
, size
);
2094 * Release the stream object.
2096 IStream_Release(pis
);
2101 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2105 case DIRENTRY_RELATION_PREVIOUS
:
2106 entry
->leftChild
= new_target
;
2108 case DIRENTRY_RELATION_NEXT
:
2109 entry
->rightChild
= new_target
;
2111 case DIRENTRY_RELATION_DIR
:
2112 entry
->dirRootEntry
= new_target
;
2119 /*************************************************************************
2123 * This method removes a directory entry from its parent storage tree without
2124 * freeing any resources attached to it.
2126 static HRESULT
removeFromTree(
2127 StorageBaseImpl
*This
,
2128 DirRef parentStorageIndex
,
2129 DirRef deletedIndex
)
2132 DirEntry entryToDelete
;
2133 DirEntry parentEntry
;
2134 DirRef parentEntryRef
;
2135 ULONG typeOfRelation
;
2137 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2143 * Find the element that links to the one we want to delete.
2145 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2146 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2151 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2154 * Replace the deleted entry with its left child
2156 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2158 hr
= StorageBaseImpl_WriteDirEntry(
2167 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2170 * We need to reinsert the right child somewhere. We already know it and
2171 * its children are greater than everything in the left tree, so we
2172 * insert it at the rightmost point in the left tree.
2174 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2175 DirEntry newRightChildParentEntry
;
2179 hr
= StorageBaseImpl_ReadDirEntry(
2181 newRightChildParent
,
2182 &newRightChildParentEntry
);
2188 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2189 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2190 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2192 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2194 hr
= StorageBaseImpl_WriteDirEntry(
2196 newRightChildParent
,
2197 &newRightChildParentEntry
);
2207 * Replace the deleted entry with its right child
2209 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2211 hr
= StorageBaseImpl_WriteDirEntry(
2225 /******************************************************************************
2226 * SetElementTimes (IStorage)
2228 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2230 const OLECHAR
*pwcsName
,/* [string][in] */
2231 const FILETIME
*pctime
, /* [in] */
2232 const FILETIME
*patime
, /* [in] */
2233 const FILETIME
*pmtime
) /* [in] */
2235 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2239 /******************************************************************************
2240 * SetStateBits (IStorage)
2242 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2244 DWORD grfStateBits
,/* [in] */
2245 DWORD grfMask
) /* [in] */
2247 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2250 return STG_E_REVERTED
;
2252 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2256 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2257 DirRef index
, const DirEntry
*data
)
2259 StorageImpl
*This
= (StorageImpl
*)base
;
2260 return StorageImpl_WriteDirEntry(This
, index
, data
);
2263 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2264 DirRef index
, DirEntry
*data
)
2266 StorageImpl
*This
= (StorageImpl
*)base
;
2267 return StorageImpl_ReadDirEntry(This
, index
, data
);
2270 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2271 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2273 StorageImpl
*This
= (StorageImpl
*)base
;
2278 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2279 if (FAILED(hr
)) return hr
;
2281 if (data
.size
.QuadPart
== 0)
2287 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2289 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2296 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2298 SmallBlockChainStream
*stream
;
2300 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2301 if (!stream
) return E_OUTOFMEMORY
;
2303 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2305 SmallBlockChainStream_Destroy(stream
);
2311 BlockChainStream
*stream
;
2313 stream
= BlockChainStream_Construct(This
, NULL
, index
);
2314 if (!stream
) return E_OUTOFMEMORY
;
2316 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2318 BlockChainStream_Destroy(stream
);
2324 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2325 ULARGE_INTEGER newsize
)
2327 StorageImpl
*This
= (StorageImpl
*)base
;
2330 SmallBlockChainStream
*smallblock
=NULL
;
2331 BlockChainStream
*bigblock
=NULL
;
2333 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2334 if (FAILED(hr
)) return hr
;
2336 /* In simple mode keep the stream size above the small block limit */
2337 if (This
->base
.openFlags
& STGM_SIMPLE
)
2338 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2340 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2343 /* Create a block chain object of the appropriate type */
2344 if (data
.size
.QuadPart
== 0)
2346 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2348 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2349 if (!smallblock
) return E_OUTOFMEMORY
;
2353 bigblock
= BlockChainStream_Construct(This
, NULL
, index
);
2354 if (!bigblock
) return E_OUTOFMEMORY
;
2357 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2359 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2360 if (!smallblock
) return E_OUTOFMEMORY
;
2364 bigblock
= BlockChainStream_Construct(This
, NULL
, index
);
2365 if (!bigblock
) return E_OUTOFMEMORY
;
2368 /* Change the block chain type if necessary. */
2369 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2371 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2374 SmallBlockChainStream_Destroy(smallblock
);
2378 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2380 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, &bigblock
);
2383 BlockChainStream_Destroy(bigblock
);
2388 /* Set the size of the block chain. */
2391 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2392 SmallBlockChainStream_Destroy(smallblock
);
2396 BlockChainStream_SetSize(bigblock
, newsize
);
2397 BlockChainStream_Destroy(bigblock
);
2400 /* Set the size in the directory entry. */
2401 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2404 data
.size
= newsize
;
2406 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2411 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2412 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2414 StorageImpl
*This
= (StorageImpl
*)base
;
2417 ULARGE_INTEGER newSize
;
2419 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2420 if (FAILED(hr
)) return hr
;
2422 /* Grow the stream if necessary */
2423 newSize
.QuadPart
= 0;
2424 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2426 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2428 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2432 data
.size
= newSize
;
2435 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2437 SmallBlockChainStream
*stream
;
2439 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2440 if (!stream
) return E_OUTOFMEMORY
;
2442 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2444 SmallBlockChainStream_Destroy(stream
);
2450 BlockChainStream
*stream
;
2452 stream
= BlockChainStream_Construct(This
, NULL
, index
);
2453 if (!stream
) return E_OUTOFMEMORY
;
2455 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2457 BlockChainStream_Destroy(stream
);
2464 * Virtual function table for the IStorage32Impl class.
2466 static const IStorageVtbl Storage32Impl_Vtbl
=
2468 StorageBaseImpl_QueryInterface
,
2469 StorageBaseImpl_AddRef
,
2470 StorageBaseImpl_Release
,
2471 StorageBaseImpl_CreateStream
,
2472 StorageBaseImpl_OpenStream
,
2473 StorageBaseImpl_CreateStorage
,
2474 StorageBaseImpl_OpenStorage
,
2475 StorageBaseImpl_CopyTo
,
2476 StorageBaseImpl_MoveElementTo
,
2479 StorageBaseImpl_EnumElements
,
2480 StorageBaseImpl_DestroyElement
,
2481 StorageBaseImpl_RenameElement
,
2482 StorageBaseImpl_SetElementTimes
,
2483 StorageBaseImpl_SetClass
,
2484 StorageBaseImpl_SetStateBits
,
2485 StorageBaseImpl_Stat
2488 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2490 StorageImpl_Destroy
,
2491 StorageImpl_Invalidate
,
2492 StorageImpl_CreateDirEntry
,
2493 StorageImpl_BaseWriteDirEntry
,
2494 StorageImpl_BaseReadDirEntry
,
2495 StorageImpl_DestroyDirEntry
,
2496 StorageImpl_StreamReadAt
,
2497 StorageImpl_StreamWriteAt
,
2498 StorageImpl_StreamSetSize
2501 static HRESULT
StorageImpl_Construct(
2508 StorageImpl
** result
)
2512 DirEntry currentEntry
;
2513 DirRef currentEntryRef
;
2514 WCHAR fullpath
[MAX_PATH
];
2516 if ( FAILED( validateSTGM(openFlags
) ))
2517 return STG_E_INVALIDFLAG
;
2519 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2521 return E_OUTOFMEMORY
;
2523 memset(This
, 0, sizeof(StorageImpl
));
2525 list_init(&This
->base
.strmHead
);
2527 list_init(&This
->base
.storageHead
);
2529 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2530 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2531 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2532 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2534 This
->base
.create
= create
;
2536 This
->base
.reverted
= 0;
2538 This
->hFile
= hFile
;
2541 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2543 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2545 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2546 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2547 if (!This
->pwcsName
)
2549 hr
= STG_E_INSUFFICIENTMEMORY
;
2552 strcpyW(This
->pwcsName
, fullpath
);
2553 This
->base
.filename
= This
->pwcsName
;
2557 * Initialize the big block cache.
2559 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2560 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2561 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2567 if (This
->bigBlockFile
== 0)
2575 ULARGE_INTEGER size
;
2576 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2579 * Initialize all header variables:
2580 * - The big block depot consists of one block and it is at block 0
2581 * - The directory table starts at block 1
2582 * - There is no small block depot
2584 memset( This
->bigBlockDepotStart
,
2586 sizeof(This
->bigBlockDepotStart
));
2588 This
->bigBlockDepotCount
= 1;
2589 This
->bigBlockDepotStart
[0] = 0;
2590 This
->rootStartBlock
= 1;
2591 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2592 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2593 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2594 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2595 This
->extBigBlockDepotCount
= 0;
2597 StorageImpl_SaveFileHeader(This
);
2600 * Add one block for the big block depot and one block for the directory table
2602 size
.u
.HighPart
= 0;
2603 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2604 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2607 * Initialize the big block depot
2609 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2610 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2611 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2612 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2617 * Load the header for the file.
2619 hr
= StorageImpl_LoadFileHeader(This
);
2628 * There is no block depot cached yet.
2630 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2633 * Start searching for free blocks with block 0.
2635 This
->prevFreeBlock
= 0;
2638 * Create the block chain abstractions.
2640 if(!(This
->rootBlockChain
=
2641 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2643 hr
= STG_E_READFAULT
;
2647 if(!(This
->smallBlockDepotChain
=
2648 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2651 hr
= STG_E_READFAULT
;
2656 * Write the root storage entry (memory only)
2662 * Initialize the directory table
2664 memset(&rootEntry
, 0, sizeof(rootEntry
));
2665 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2666 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2667 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2668 rootEntry
.stgType
= STGTY_ROOT
;
2669 rootEntry
.leftChild
= DIRENTRY_NULL
;
2670 rootEntry
.rightChild
= DIRENTRY_NULL
;
2671 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2672 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2673 rootEntry
.size
.u
.HighPart
= 0;
2674 rootEntry
.size
.u
.LowPart
= 0;
2676 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2680 * Find the ID of the root storage.
2682 currentEntryRef
= 0;
2686 hr
= StorageImpl_ReadDirEntry(
2693 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2694 (currentEntry
.stgType
== STGTY_ROOT
) )
2696 This
->base
.storageDirEntry
= currentEntryRef
;
2702 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2706 hr
= STG_E_READFAULT
;
2711 * Create the block chain abstraction for the small block root chain.
2713 if(!(This
->smallBlockRootChain
=
2714 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2716 hr
= STG_E_READFAULT
;
2722 IStorage_Release((IStorage
*)This
);
2731 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2733 StorageImpl
*This
= (StorageImpl
*) iface
;
2735 StorageBaseImpl_DeleteAll(&This
->base
);
2737 This
->base
.reverted
= 1;
2740 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2742 StorageImpl
*This
= (StorageImpl
*) iface
;
2743 TRACE("(%p)\n", This
);
2745 StorageImpl_Invalidate(iface
);
2747 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2749 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2750 BlockChainStream_Destroy(This
->rootBlockChain
);
2751 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2753 if (This
->bigBlockFile
)
2754 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2755 HeapFree(GetProcessHeap(), 0, This
);
2758 /******************************************************************************
2759 * Storage32Impl_GetNextFreeBigBlock
2761 * Returns the index of the next free big block.
2762 * If the big block depot is filled, this method will enlarge it.
2765 static ULONG
StorageImpl_GetNextFreeBigBlock(
2768 ULONG depotBlockIndexPos
;
2769 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2771 ULONG depotBlockOffset
;
2772 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2773 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2775 ULONG freeBlock
= BLOCK_UNUSED
;
2777 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2778 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2781 * Scan the entire big block depot until we find a block marked free
2783 while (nextBlockIndex
!= BLOCK_UNUSED
)
2785 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2787 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2790 * Grow the primary depot.
2792 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2794 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2797 * Add a block depot.
2799 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2800 This
->bigBlockDepotCount
++;
2801 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2804 * Flag it as a block depot.
2806 StorageImpl_SetNextBlockInChain(This
,
2810 /* Save new header information.
2812 StorageImpl_SaveFileHeader(This
);
2817 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2819 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2822 * Grow the extended depot.
2824 ULONG extIndex
= BLOCK_UNUSED
;
2825 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2826 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2828 if (extBlockOffset
== 0)
2830 /* We need an extended block.
2832 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2833 This
->extBigBlockDepotCount
++;
2834 depotBlockIndexPos
= extIndex
+ 1;
2837 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2840 * Add a block depot and mark it in the extended block.
2842 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2843 This
->bigBlockDepotCount
++;
2844 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2846 /* Flag the block depot.
2848 StorageImpl_SetNextBlockInChain(This
,
2852 /* If necessary, flag the extended depot block.
2854 if (extIndex
!= BLOCK_UNUSED
)
2855 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2857 /* Save header information.
2859 StorageImpl_SaveFileHeader(This
);
2863 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2867 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2868 ( nextBlockIndex
!= BLOCK_UNUSED
))
2870 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2872 if (nextBlockIndex
== BLOCK_UNUSED
)
2874 freeBlock
= (depotIndex
* blocksPerDepot
) +
2875 (depotBlockOffset
/sizeof(ULONG
));
2878 depotBlockOffset
+= sizeof(ULONG
);
2883 depotBlockOffset
= 0;
2887 * make sure that the block physically exists before using it
2889 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2891 This
->prevFreeBlock
= freeBlock
;
2896 /******************************************************************************
2897 * Storage32Impl_AddBlockDepot
2899 * This will create a depot block, essentially it is a block initialized
2902 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2904 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2907 * Initialize blocks as free
2909 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2910 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2913 /******************************************************************************
2914 * Storage32Impl_GetExtDepotBlock
2916 * Returns the index of the block that corresponds to the specified depot
2917 * index. This method is only for depot indexes equal or greater than
2918 * COUNT_BBDEPOTINHEADER.
2920 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2922 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2923 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2924 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2925 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2926 ULONG blockIndex
= BLOCK_UNUSED
;
2927 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2929 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2931 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2932 return BLOCK_UNUSED
;
2934 while (extBlockCount
> 0)
2936 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2940 if (extBlockIndex
!= BLOCK_UNUSED
)
2941 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2942 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2947 /******************************************************************************
2948 * Storage32Impl_SetExtDepotBlock
2950 * Associates the specified block index to the specified depot index.
2951 * This method is only for depot indexes equal or greater than
2952 * COUNT_BBDEPOTINHEADER.
2954 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2956 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2957 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2958 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2959 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2960 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2962 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2964 while (extBlockCount
> 0)
2966 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2970 if (extBlockIndex
!= BLOCK_UNUSED
)
2972 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2973 extBlockOffset
* sizeof(ULONG
),
2978 /******************************************************************************
2979 * Storage32Impl_AddExtBlockDepot
2981 * Creates an extended depot block.
2983 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2985 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2986 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2987 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2988 ULONG index
= BLOCK_UNUSED
;
2989 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2990 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2991 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2993 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2994 blocksPerDepotBlock
;
2996 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2999 * The first extended block.
3001 This
->extBigBlockDepotStart
= index
;
3007 * Follow the chain to the last one.
3009 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3011 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3015 * Add the new extended block to the chain.
3017 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3022 * Initialize this block.
3024 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3025 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3030 /******************************************************************************
3031 * Storage32Impl_FreeBigBlock
3033 * This method will flag the specified block as free in the big block depot.
3035 static void StorageImpl_FreeBigBlock(
3039 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3041 if (blockIndex
< This
->prevFreeBlock
)
3042 This
->prevFreeBlock
= blockIndex
;
3045 /************************************************************************
3046 * Storage32Impl_GetNextBlockInChain
3048 * This method will retrieve the block index of the next big block in
3051 * Params: This - Pointer to the Storage object.
3052 * blockIndex - Index of the block to retrieve the chain
3054 * nextBlockIndex - receives the return value.
3056 * Returns: This method returns the index of the next block in the chain.
3057 * It will return the constants:
3058 * BLOCK_SPECIAL - If the block given was not part of a
3060 * BLOCK_END_OF_CHAIN - If the block given was the last in
3062 * BLOCK_UNUSED - If the block given was not past of a chain
3064 * BLOCK_EXTBBDEPOT - This block is part of the extended
3067 * See Windows documentation for more details on IStorage methods.
3069 static HRESULT
StorageImpl_GetNextBlockInChain(
3072 ULONG
* nextBlockIndex
)
3074 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3075 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3076 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3077 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
3079 ULONG depotBlockIndexPos
;
3082 *nextBlockIndex
= BLOCK_SPECIAL
;
3084 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3086 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3087 This
->bigBlockDepotCount
);
3088 return STG_E_READFAULT
;
3092 * Cache the currently accessed depot block.
3094 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3096 This
->indexBlockDepotCached
= depotBlockCount
;
3098 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3100 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3105 * We have to look in the extended depot.
3107 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3110 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3113 return STG_E_READFAULT
;
3115 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
3117 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3118 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3122 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3127 /******************************************************************************
3128 * Storage32Impl_GetNextExtendedBlock
3130 * Given an extended block this method will return the next extended block.
3133 * The last ULONG of an extended block is the block index of the next
3134 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3138 * - The index of the next extended block
3139 * - BLOCK_UNUSED: there is no next extended block.
3140 * - Any other return values denotes failure.
3142 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3144 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3145 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3147 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3150 return nextBlockIndex
;
3153 /******************************************************************************
3154 * Storage32Impl_SetNextBlockInChain
3156 * This method will write the index of the specified block's next block
3157 * in the big block depot.
3159 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3162 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3163 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3164 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3167 static void StorageImpl_SetNextBlockInChain(
3172 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3173 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3174 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3175 ULONG depotBlockIndexPos
;
3177 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3178 assert(blockIndex
!= nextBlock
);
3180 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3182 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3187 * We have to look in the extended depot.
3189 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3192 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3195 * Update the cached block depot, if necessary.
3197 if (depotBlockCount
== This
->indexBlockDepotCached
)
3199 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3203 /******************************************************************************
3204 * Storage32Impl_LoadFileHeader
3206 * This method will read in the file header, i.e. big block index -1.
3208 static HRESULT
StorageImpl_LoadFileHeader(
3211 HRESULT hr
= STG_E_FILENOTFOUND
;
3212 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3218 * Get a pointer to the big block of data containing the header.
3220 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3223 * Extract the information from the header.
3228 * Check for the "magic number" signature and return an error if it is not
3231 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3233 return STG_E_OLDFORMAT
;
3236 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3238 return STG_E_INVALIDHEADER
;
3241 StorageUtl_ReadWord(
3243 OFFSET_BIGBLOCKSIZEBITS
,
3244 &This
->bigBlockSizeBits
);
3246 StorageUtl_ReadWord(
3248 OFFSET_SMALLBLOCKSIZEBITS
,
3249 &This
->smallBlockSizeBits
);
3251 StorageUtl_ReadDWord(
3253 OFFSET_BBDEPOTCOUNT
,
3254 &This
->bigBlockDepotCount
);
3256 StorageUtl_ReadDWord(
3258 OFFSET_ROOTSTARTBLOCK
,
3259 &This
->rootStartBlock
);
3261 StorageUtl_ReadDWord(
3263 OFFSET_SBDEPOTSTART
,
3264 &This
->smallBlockDepotStart
);
3266 StorageUtl_ReadDWord(
3268 OFFSET_EXTBBDEPOTSTART
,
3269 &This
->extBigBlockDepotStart
);
3271 StorageUtl_ReadDWord(
3273 OFFSET_EXTBBDEPOTCOUNT
,
3274 &This
->extBigBlockDepotCount
);
3276 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3278 StorageUtl_ReadDWord(
3280 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3281 &(This
->bigBlockDepotStart
[index
]));
3285 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3287 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3288 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3291 * Right now, the code is making some assumptions about the size of the
3292 * blocks, just make sure they are what we're expecting.
3294 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3295 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3297 WARN("Broken OLE storage file\n");
3298 hr
= STG_E_INVALIDHEADER
;
3307 /******************************************************************************
3308 * Storage32Impl_SaveFileHeader
3310 * This method will save to the file the header, i.e. big block -1.
3312 static void StorageImpl_SaveFileHeader(
3315 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3320 * Get a pointer to the big block of data containing the header.
3322 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3325 * If the block read failed, the file is probably new.
3330 * Initialize for all unknown fields.
3332 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3335 * Initialize the magic number.
3337 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3340 * And a bunch of things we don't know what they mean
3342 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3343 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3344 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3345 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3349 * Write the information to the header.
3351 StorageUtl_WriteWord(
3353 OFFSET_BIGBLOCKSIZEBITS
,
3354 This
->bigBlockSizeBits
);
3356 StorageUtl_WriteWord(
3358 OFFSET_SMALLBLOCKSIZEBITS
,
3359 This
->smallBlockSizeBits
);
3361 StorageUtl_WriteDWord(
3363 OFFSET_BBDEPOTCOUNT
,
3364 This
->bigBlockDepotCount
);
3366 StorageUtl_WriteDWord(
3368 OFFSET_ROOTSTARTBLOCK
,
3369 This
->rootStartBlock
);
3371 StorageUtl_WriteDWord(
3373 OFFSET_SBDEPOTSTART
,
3374 This
->smallBlockDepotStart
);
3376 StorageUtl_WriteDWord(
3378 OFFSET_SBDEPOTCOUNT
,
3379 This
->smallBlockDepotChain
?
3380 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3382 StorageUtl_WriteDWord(
3384 OFFSET_EXTBBDEPOTSTART
,
3385 This
->extBigBlockDepotStart
);
3387 StorageUtl_WriteDWord(
3389 OFFSET_EXTBBDEPOTCOUNT
,
3390 This
->extBigBlockDepotCount
);
3392 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3394 StorageUtl_WriteDWord(
3396 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3397 (This
->bigBlockDepotStart
[index
]));
3401 * Write the big block back to the file.
3403 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3406 /******************************************************************************
3407 * StorageImpl_ReadRawDirEntry
3409 * This method will read the raw data from a directory entry in the file.
3411 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3413 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3415 ULARGE_INTEGER offset
;
3419 offset
.u
.HighPart
= 0;
3420 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3422 hr
= BlockChainStream_ReadAt(
3423 This
->rootBlockChain
,
3432 /******************************************************************************
3433 * StorageImpl_WriteRawDirEntry
3435 * This method will write the raw data from a directory entry in the file.
3437 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3439 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3441 ULARGE_INTEGER offset
;
3445 offset
.u
.HighPart
= 0;
3446 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3448 hr
= BlockChainStream_WriteAt(
3449 This
->rootBlockChain
,
3458 /******************************************************************************
3461 * Update raw directory entry data from the fields in newData.
3463 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3465 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3467 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3470 buffer
+ OFFSET_PS_NAME
,
3472 DIRENTRY_NAME_BUFFER_LEN
);
3474 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3476 StorageUtl_WriteWord(
3478 OFFSET_PS_NAMELENGTH
,
3479 newData
->sizeOfNameString
);
3481 StorageUtl_WriteDWord(
3483 OFFSET_PS_LEFTCHILD
,
3484 newData
->leftChild
);
3486 StorageUtl_WriteDWord(
3488 OFFSET_PS_RIGHTCHILD
,
3489 newData
->rightChild
);
3491 StorageUtl_WriteDWord(
3494 newData
->dirRootEntry
);
3496 StorageUtl_WriteGUID(
3501 StorageUtl_WriteDWord(
3504 newData
->ctime
.dwLowDateTime
);
3506 StorageUtl_WriteDWord(
3508 OFFSET_PS_CTIMEHIGH
,
3509 newData
->ctime
.dwHighDateTime
);
3511 StorageUtl_WriteDWord(
3514 newData
->mtime
.dwLowDateTime
);
3516 StorageUtl_WriteDWord(
3518 OFFSET_PS_MTIMEHIGH
,
3519 newData
->ctime
.dwHighDateTime
);
3521 StorageUtl_WriteDWord(
3523 OFFSET_PS_STARTBLOCK
,
3524 newData
->startingBlock
);
3526 StorageUtl_WriteDWord(
3529 newData
->size
.u
.LowPart
);
3532 /******************************************************************************
3533 * Storage32Impl_ReadDirEntry
3535 * This method will read the specified directory entry.
3537 HRESULT
StorageImpl_ReadDirEntry(
3542 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3545 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3547 if (SUCCEEDED(readRes
))
3549 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3552 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3553 DIRENTRY_NAME_BUFFER_LEN
);
3554 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3556 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3558 StorageUtl_ReadWord(
3560 OFFSET_PS_NAMELENGTH
,
3561 &buffer
->sizeOfNameString
);
3563 StorageUtl_ReadDWord(
3565 OFFSET_PS_LEFTCHILD
,
3566 &buffer
->leftChild
);
3568 StorageUtl_ReadDWord(
3570 OFFSET_PS_RIGHTCHILD
,
3571 &buffer
->rightChild
);
3573 StorageUtl_ReadDWord(
3576 &buffer
->dirRootEntry
);
3578 StorageUtl_ReadGUID(
3583 StorageUtl_ReadDWord(
3586 &buffer
->ctime
.dwLowDateTime
);
3588 StorageUtl_ReadDWord(
3590 OFFSET_PS_CTIMEHIGH
,
3591 &buffer
->ctime
.dwHighDateTime
);
3593 StorageUtl_ReadDWord(
3596 &buffer
->mtime
.dwLowDateTime
);
3598 StorageUtl_ReadDWord(
3600 OFFSET_PS_MTIMEHIGH
,
3601 &buffer
->mtime
.dwHighDateTime
);
3603 StorageUtl_ReadDWord(
3605 OFFSET_PS_STARTBLOCK
,
3606 &buffer
->startingBlock
);
3608 StorageUtl_ReadDWord(
3611 &buffer
->size
.u
.LowPart
);
3613 buffer
->size
.u
.HighPart
= 0;
3619 /*********************************************************************
3620 * Write the specified directory entry to the file
3622 HRESULT
StorageImpl_WriteDirEntry(
3625 const DirEntry
* buffer
)
3627 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3630 UpdateRawDirEntry(currentEntry
, buffer
);
3632 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3636 static BOOL
StorageImpl_ReadBigBlock(
3641 ULARGE_INTEGER ulOffset
;
3644 ulOffset
.u
.HighPart
= 0;
3645 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3647 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3648 return (read
== This
->bigBlockSize
);
3651 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3657 ULARGE_INTEGER ulOffset
;
3661 ulOffset
.u
.HighPart
= 0;
3662 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3663 ulOffset
.u
.LowPart
+= offset
;
3665 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3666 *value
= lendian32toh(tmp
);
3667 return (read
== sizeof(DWORD
));
3670 static BOOL
StorageImpl_WriteBigBlock(
3675 ULARGE_INTEGER ulOffset
;
3678 ulOffset
.u
.HighPart
= 0;
3679 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3681 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3682 return (wrote
== This
->bigBlockSize
);
3685 static BOOL
StorageImpl_WriteDWordToBigBlock(
3691 ULARGE_INTEGER ulOffset
;
3694 ulOffset
.u
.HighPart
= 0;
3695 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3696 ulOffset
.u
.LowPart
+= offset
;
3698 value
= htole32(value
);
3699 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3700 return (wrote
== sizeof(DWORD
));
3703 /******************************************************************************
3704 * Storage32Impl_SmallBlocksToBigBlocks
3706 * This method will convert a small block chain to a big block chain.
3707 * The small block chain will be destroyed.
3709 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3711 SmallBlockChainStream
** ppsbChain
)
3713 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3714 ULARGE_INTEGER size
, offset
;
3715 ULONG cbRead
, cbWritten
;
3716 ULARGE_INTEGER cbTotalRead
;
3717 DirRef streamEntryRef
;
3718 HRESULT resWrite
= S_OK
;
3720 DirEntry streamEntry
;
3722 BlockChainStream
*bbTempChain
= NULL
;
3723 BlockChainStream
*bigBlockChain
= NULL
;
3726 * Create a temporary big block chain that doesn't have
3727 * an associated directory entry. This temporary chain will be
3728 * used to copy data from small blocks to big blocks.
3730 bbTempChain
= BlockChainStream_Construct(This
,
3733 if(!bbTempChain
) return NULL
;
3735 * Grow the big block chain.
3737 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3738 BlockChainStream_SetSize(bbTempChain
, size
);
3741 * Copy the contents of the small block chain to the big block chain
3742 * by small block size increments.
3744 offset
.u
.LowPart
= 0;
3745 offset
.u
.HighPart
= 0;
3746 cbTotalRead
.QuadPart
= 0;
3748 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3751 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3753 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3756 if (FAILED(resRead
))
3761 cbTotalRead
.QuadPart
+= cbRead
;
3763 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3769 if (FAILED(resWrite
))
3772 offset
.u
.LowPart
+= cbRead
;
3774 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3775 HeapFree(GetProcessHeap(),0,buffer
);
3777 size
.u
.HighPart
= 0;
3780 if (FAILED(resRead
) || FAILED(resWrite
))
3782 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3783 BlockChainStream_SetSize(bbTempChain
, size
);
3784 BlockChainStream_Destroy(bbTempChain
);
3789 * Destroy the small block chain.
3791 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3792 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3793 SmallBlockChainStream_Destroy(*ppsbChain
);
3797 * Change the directory entry. This chain is now a big block chain
3798 * and it doesn't reside in the small blocks chain anymore.
3800 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3802 streamEntry
.startingBlock
= bbHeadOfChain
;
3804 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3807 * Destroy the temporary entryless big block chain.
3808 * Create a new big block chain associated with this entry.
3810 BlockChainStream_Destroy(bbTempChain
);
3811 bigBlockChain
= BlockChainStream_Construct(This
,
3815 return bigBlockChain
;
3818 /******************************************************************************
3819 * Storage32Impl_BigBlocksToSmallBlocks
3821 * This method will convert a big block chain to a small block chain.
3822 * The big block chain will be destroyed on success.
3824 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3826 BlockChainStream
** ppbbChain
)
3828 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3829 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3830 DirRef streamEntryRef
;
3831 HRESULT resWrite
= S_OK
, resRead
;
3832 DirEntry streamEntry
;
3834 SmallBlockChainStream
* sbTempChain
;
3836 TRACE("%p %p\n", This
, ppbbChain
);
3838 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3844 size
= BlockChainStream_GetSize(*ppbbChain
);
3845 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3847 offset
.u
.HighPart
= 0;
3848 offset
.u
.LowPart
= 0;
3849 cbTotalRead
.QuadPart
= 0;
3850 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3853 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3854 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3862 cbTotalRead
.QuadPart
+= cbRead
;
3864 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3865 cbRead
, buffer
, &cbWritten
);
3867 if(FAILED(resWrite
))
3870 offset
.u
.LowPart
+= cbRead
;
3872 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3873 HeapFree(GetProcessHeap(), 0, buffer
);
3875 size
.u
.HighPart
= 0;
3878 if(FAILED(resRead
) || FAILED(resWrite
))
3880 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3881 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3882 SmallBlockChainStream_Destroy(sbTempChain
);
3886 /* destroy the original big block chain */
3887 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3888 BlockChainStream_SetSize(*ppbbChain
, size
);
3889 BlockChainStream_Destroy(*ppbbChain
);
3892 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3893 streamEntry
.startingBlock
= sbHeadOfChain
;
3894 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3896 SmallBlockChainStream_Destroy(sbTempChain
);
3897 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3900 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
3903 DirEntry parentData
, snapshotData
;
3905 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
3906 0, (IStorage
**)snapshot
);
3910 hr
= StorageBaseImpl_ReadDirEntry(original
,
3911 original
->storageDirEntry
, &parentData
);
3914 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
3915 (*snapshot
)->storageDirEntry
, &snapshotData
);
3919 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
3920 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
3921 snapshotData
.stgType
= parentData
.stgType
;
3922 snapshotData
.clsid
= parentData
.clsid
;
3923 snapshotData
.ctime
= parentData
.ctime
;
3924 snapshotData
.mtime
= parentData
.mtime
;
3925 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
3926 (*snapshot
)->storageDirEntry
, &snapshotData
);
3930 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
3931 (IStorage
*)(*snapshot
));
3933 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
3939 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
3941 DWORD grfCommitFlags
) /* [in] */
3943 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
3945 DirEntry data
, tempStorageData
, snapshotRootData
;
3946 DirRef tempStorageEntry
, oldDirRoot
;
3947 StorageInternalImpl
*tempStorage
;
3949 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
3951 /* Cannot commit a read-only transacted storage */
3952 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
3953 return STG_E_ACCESSDENIED
;
3955 /* To prevent data loss, we create the new structure in the file before we
3956 * delete the old one, so that in case of errors the old data is intact. We
3957 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
3958 * needed in the rare situation where we have just enough free disk space to
3959 * overwrite the existing data. */
3961 /* Create an orphaned storage in the parent for the new directory structure. */
3962 memset(&data
, 0, sizeof(data
));
3964 data
.sizeOfNameString
= 1;
3965 data
.stgType
= STGTY_STORAGE
;
3966 data
.leftChild
= DIRENTRY_NULL
;
3967 data
.rightChild
= DIRENTRY_NULL
;
3968 data
.dirRootEntry
= DIRENTRY_NULL
;
3969 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
3971 if (FAILED(hr
)) return hr
;
3973 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
3974 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
3977 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
3978 (IStorage
*)tempStorage
);
3980 list_init(&tempStorage
->ParentListEntry
);
3982 IStorage_Release((IStorage
*) tempStorage
);
3989 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
3993 /* Update the storage to use the new data in one step. */
3994 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
3995 This
->transactedParent
->storageDirEntry
, &data
);
3999 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4000 tempStorageEntry
, &tempStorageData
);
4005 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4006 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4011 oldDirRoot
= data
.dirRootEntry
;
4012 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4013 data
.clsid
= snapshotRootData
.clsid
;
4014 data
.ctime
= snapshotRootData
.ctime
;
4015 data
.mtime
= snapshotRootData
.mtime
;
4017 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4018 This
->transactedParent
->storageDirEntry
, &data
);
4023 /* Destroy the old now-orphaned data. */
4024 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4025 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4029 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4035 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4038 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4039 StorageBaseImpl
*newSnapshot
;
4042 TRACE("(%p)\n", iface
);
4044 /* Create a new copy of the parent data. */
4045 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4046 if (FAILED(hr
)) return hr
;
4048 /* Destroy the open objects. */
4049 StorageBaseImpl_DeleteAll(&This
->base
);
4051 /* Replace our current snapshot. */
4052 IStorage_Release((IStorage
*)This
->snapshot
);
4053 This
->snapshot
= newSnapshot
;
4058 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4060 if (!This
->reverted
)
4062 TRACE("Storage invalidated (stg=%p)\n", This
);
4066 StorageBaseImpl_DeleteAll(This
);
4070 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4072 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4074 TransactedSnapshotImpl_Invalidate(iface
);
4076 IStorage_Release((IStorage
*)This
->transactedParent
);
4078 IStorage_Release((IStorage
*)This
->snapshot
);
4080 HeapFree(GetProcessHeap(), 0, This
);
4083 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4084 const DirEntry
*newData
, DirRef
*index
)
4086 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4088 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4092 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4093 DirRef index
, const DirEntry
*data
)
4095 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4097 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4101 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4102 DirRef index
, DirEntry
*data
)
4104 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4106 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4110 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4113 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4115 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4119 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4120 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4122 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4124 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4125 index
, offset
, size
, buffer
, bytesRead
);
4128 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4129 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4131 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4133 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4134 index
, offset
, size
, buffer
, bytesWritten
);
4137 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4138 DirRef index
, ULARGE_INTEGER newsize
)
4140 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4142 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4146 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4148 StorageBaseImpl_QueryInterface
,
4149 StorageBaseImpl_AddRef
,
4150 StorageBaseImpl_Release
,
4151 StorageBaseImpl_CreateStream
,
4152 StorageBaseImpl_OpenStream
,
4153 StorageBaseImpl_CreateStorage
,
4154 StorageBaseImpl_OpenStorage
,
4155 StorageBaseImpl_CopyTo
,
4156 StorageBaseImpl_MoveElementTo
,
4157 TransactedSnapshotImpl_Commit
,
4158 TransactedSnapshotImpl_Revert
,
4159 StorageBaseImpl_EnumElements
,
4160 StorageBaseImpl_DestroyElement
,
4161 StorageBaseImpl_RenameElement
,
4162 StorageBaseImpl_SetElementTimes
,
4163 StorageBaseImpl_SetClass
,
4164 StorageBaseImpl_SetStateBits
,
4165 StorageBaseImpl_Stat
4168 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4170 TransactedSnapshotImpl_Destroy
,
4171 TransactedSnapshotImpl_Invalidate
,
4172 TransactedSnapshotImpl_CreateDirEntry
,
4173 TransactedSnapshotImpl_WriteDirEntry
,
4174 TransactedSnapshotImpl_ReadDirEntry
,
4175 TransactedSnapshotImpl_DestroyDirEntry
,
4176 TransactedSnapshotImpl_StreamReadAt
,
4177 TransactedSnapshotImpl_StreamWriteAt
,
4178 TransactedSnapshotImpl_StreamSetSize
4181 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4182 TransactedSnapshotImpl
** result
)
4186 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4189 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4191 /* This is OK because the property set storage functions use the IStorage functions. */
4192 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4194 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4196 list_init(&(*result
)->base
.strmHead
);
4198 list_init(&(*result
)->base
.storageHead
);
4200 (*result
)->base
.ref
= 1;
4202 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4204 (*result
)->base
.filename
= parentStorage
->filename
;
4206 /* Create a new temporary storage to act as the snapshot */
4207 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4211 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4213 /* parentStorage already has 1 reference, which we take over here. */
4214 (*result
)->transactedParent
= parentStorage
;
4216 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4219 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4224 return E_OUTOFMEMORY
;
4227 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4228 StorageBaseImpl
** result
)
4232 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4234 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4237 return TransactedSnapshotImpl_Construct(parentStorage
,
4238 (TransactedSnapshotImpl
**)result
);
4241 static HRESULT
Storage_Construct(
4248 StorageBaseImpl
** result
)
4250 StorageImpl
*newStorage
;
4251 StorageBaseImpl
*newTransactedStorage
;
4254 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, &newStorage
);
4255 if (FAILED(hr
)) goto end
;
4257 if (openFlags
& STGM_TRANSACTED
)
4259 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4261 IStorage_Release((IStorage
*)newStorage
);
4263 *result
= newTransactedStorage
;
4266 *result
= &newStorage
->base
;
4272 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4274 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4276 if (!This
->base
.reverted
)
4278 TRACE("Storage invalidated (stg=%p)\n", This
);
4280 This
->base
.reverted
= 1;
4282 This
->parentStorage
= NULL
;
4284 StorageBaseImpl_DeleteAll(&This
->base
);
4286 list_remove(&This
->ParentListEntry
);
4290 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4292 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4294 StorageInternalImpl_Invalidate(&This
->base
);
4296 HeapFree(GetProcessHeap(), 0, This
);
4299 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4300 const DirEntry
*newData
, DirRef
*index
)
4302 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4304 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4308 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4309 DirRef index
, const DirEntry
*data
)
4311 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4313 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4317 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4318 DirRef index
, DirEntry
*data
)
4320 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4322 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4326 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4329 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4331 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4335 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4336 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4338 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4340 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4341 index
, offset
, size
, buffer
, bytesRead
);
4344 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4345 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4347 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4349 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4350 index
, offset
, size
, buffer
, bytesWritten
);
4353 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4354 DirRef index
, ULARGE_INTEGER newsize
)
4356 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4358 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4362 /******************************************************************************
4364 ** Storage32InternalImpl_Commit
4367 static HRESULT WINAPI
StorageInternalImpl_Commit(
4369 DWORD grfCommitFlags
) /* [in] */
4371 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4375 /******************************************************************************
4377 ** Storage32InternalImpl_Revert
4380 static HRESULT WINAPI
StorageInternalImpl_Revert(
4383 FIXME("(%p): stub\n", iface
);
4387 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4389 IStorage_Release((IStorage
*)This
->parentStorage
);
4390 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
4391 HeapFree(GetProcessHeap(), 0, This
);
4394 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4395 IEnumSTATSTG
* iface
,
4399 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4402 return E_INVALIDARG
;
4406 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4407 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4410 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4414 return E_NOINTERFACE
;
4417 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4418 IEnumSTATSTG
* iface
)
4420 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4421 return InterlockedIncrement(&This
->ref
);
4424 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4425 IEnumSTATSTG
* iface
)
4427 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4431 newRef
= InterlockedDecrement(&This
->ref
);
4435 IEnumSTATSTGImpl_Destroy(This
);
4441 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4442 IEnumSTATSTG
* iface
,
4445 ULONG
* pceltFetched
)
4447 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4449 DirEntry currentEntry
;
4450 STATSTG
* currentReturnStruct
= rgelt
;
4451 ULONG objectFetched
= 0;
4452 DirRef currentSearchNode
;
4454 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4455 return E_INVALIDARG
;
4458 * To avoid the special case, get another pointer to a ULONG value if
4459 * the caller didn't supply one.
4461 if (pceltFetched
==0)
4462 pceltFetched
= &objectFetched
;
4465 * Start the iteration, we will iterate until we hit the end of the
4466 * linked list or until we hit the number of items to iterate through
4471 * Start with the node at the top of the stack.
4473 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4475 while ( ( *pceltFetched
< celt
) &&
4476 ( currentSearchNode
!=DIRENTRY_NULL
) )
4479 * Remove the top node from the stack
4481 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4484 * Read the entry from the storage.
4486 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4491 * Copy the information to the return buffer.
4493 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4494 currentReturnStruct
,
4499 * Step to the next item in the iteration
4502 currentReturnStruct
++;
4505 * Push the next search node in the search stack.
4507 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
4510 * continue the iteration.
4512 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4515 if (*pceltFetched
== celt
)
4522 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4523 IEnumSTATSTG
* iface
,
4526 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4528 DirEntry currentEntry
;
4529 ULONG objectFetched
= 0;
4530 DirRef currentSearchNode
;
4533 * Start with the node at the top of the stack.
4535 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4537 while ( (objectFetched
< celt
) &&
4538 (currentSearchNode
!=DIRENTRY_NULL
) )
4541 * Remove the top node from the stack
4543 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4546 * Read the entry from the storage.
4548 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4553 * Step to the next item in the iteration
4558 * Push the next search node in the search stack.
4560 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
4563 * continue the iteration.
4565 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4568 if (objectFetched
== celt
)
4574 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4575 IEnumSTATSTG
* iface
)
4577 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4579 DirEntry storageEntry
;
4583 * Re-initialize the search stack to an empty stack
4585 This
->stackSize
= 0;
4588 * Read the storage entry from the top-level storage.
4590 hr
= StorageBaseImpl_ReadDirEntry(
4591 This
->parentStorage
,
4592 This
->storageDirEntry
,
4597 assert(storageEntry
.sizeOfNameString
!=0);
4600 * Push the search node in the search stack.
4602 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.dirRootEntry
);
4608 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4609 IEnumSTATSTG
* iface
,
4610 IEnumSTATSTG
** ppenum
)
4612 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4614 IEnumSTATSTGImpl
* newClone
;
4617 * Perform a sanity check on the parameters.
4620 return E_INVALIDARG
;
4622 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4623 This
->storageDirEntry
);
4627 * The new clone enumeration must point to the same current node as
4630 newClone
->stackSize
= This
->stackSize
;
4631 newClone
->stackMaxSize
= This
->stackMaxSize
;
4632 newClone
->stackToVisit
=
4633 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
4636 newClone
->stackToVisit
,
4638 sizeof(DirRef
) * newClone
->stackSize
);
4640 *ppenum
= (IEnumSTATSTG
*)newClone
;
4643 * Don't forget to nail down a reference to the clone before
4646 IEnumSTATSTGImpl_AddRef(*ppenum
);
4651 static void IEnumSTATSTGImpl_PushSearchNode(
4652 IEnumSTATSTGImpl
* This
,
4655 DirEntry storageEntry
;
4659 * First, make sure we're not trying to push an unexisting node.
4661 if (nodeToPush
==DIRENTRY_NULL
)
4665 * First push the node to the stack
4667 if (This
->stackSize
== This
->stackMaxSize
)
4669 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4671 This
->stackToVisit
= HeapReAlloc(
4675 sizeof(DirRef
) * This
->stackMaxSize
);
4678 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4682 * Read the storage entry from the top-level storage.
4684 hr
= StorageBaseImpl_ReadDirEntry(
4685 This
->parentStorage
,
4691 assert(storageEntry
.sizeOfNameString
!=0);
4694 * Push the previous search node in the search stack.
4696 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.leftChild
);
4700 static DirRef
IEnumSTATSTGImpl_PopSearchNode(
4701 IEnumSTATSTGImpl
* This
,
4706 if (This
->stackSize
== 0)
4707 return DIRENTRY_NULL
;
4709 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4718 * Virtual function table for the IEnumSTATSTGImpl class.
4720 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4722 IEnumSTATSTGImpl_QueryInterface
,
4723 IEnumSTATSTGImpl_AddRef
,
4724 IEnumSTATSTGImpl_Release
,
4725 IEnumSTATSTGImpl_Next
,
4726 IEnumSTATSTGImpl_Skip
,
4727 IEnumSTATSTGImpl_Reset
,
4728 IEnumSTATSTGImpl_Clone
4731 /******************************************************************************
4732 ** IEnumSTATSTGImpl implementation
4735 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4736 StorageBaseImpl
* parentStorage
,
4737 DirRef storageDirEntry
)
4739 IEnumSTATSTGImpl
* newEnumeration
;
4741 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4743 if (newEnumeration
!=0)
4746 * Set-up the virtual function table and reference count.
4748 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4749 newEnumeration
->ref
= 0;
4752 * We want to nail-down the reference to the storage in case the
4753 * enumeration out-lives the storage in the client application.
4755 newEnumeration
->parentStorage
= parentStorage
;
4756 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4758 newEnumeration
->storageDirEntry
= storageDirEntry
;
4761 * Initialize the search stack
4763 newEnumeration
->stackSize
= 0;
4764 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4765 newEnumeration
->stackToVisit
=
4766 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef
)*ENUMSTATSGT_SIZE_INCREMENT
);
4769 * Make sure the current node of the iterator is the first one.
4771 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4774 return newEnumeration
;
4778 * Virtual function table for the Storage32InternalImpl class.
4780 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4782 StorageBaseImpl_QueryInterface
,
4783 StorageBaseImpl_AddRef
,
4784 StorageBaseImpl_Release
,
4785 StorageBaseImpl_CreateStream
,
4786 StorageBaseImpl_OpenStream
,
4787 StorageBaseImpl_CreateStorage
,
4788 StorageBaseImpl_OpenStorage
,
4789 StorageBaseImpl_CopyTo
,
4790 StorageBaseImpl_MoveElementTo
,
4791 StorageInternalImpl_Commit
,
4792 StorageInternalImpl_Revert
,
4793 StorageBaseImpl_EnumElements
,
4794 StorageBaseImpl_DestroyElement
,
4795 StorageBaseImpl_RenameElement
,
4796 StorageBaseImpl_SetElementTimes
,
4797 StorageBaseImpl_SetClass
,
4798 StorageBaseImpl_SetStateBits
,
4799 StorageBaseImpl_Stat
4802 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4804 StorageInternalImpl_Destroy
,
4805 StorageInternalImpl_Invalidate
,
4806 StorageInternalImpl_CreateDirEntry
,
4807 StorageInternalImpl_WriteDirEntry
,
4808 StorageInternalImpl_ReadDirEntry
,
4809 StorageInternalImpl_DestroyDirEntry
,
4810 StorageInternalImpl_StreamReadAt
,
4811 StorageInternalImpl_StreamWriteAt
,
4812 StorageInternalImpl_StreamSetSize
4815 /******************************************************************************
4816 ** Storage32InternalImpl implementation
4819 static StorageInternalImpl
* StorageInternalImpl_Construct(
4820 StorageBaseImpl
* parentStorage
,
4822 DirRef storageDirEntry
)
4824 StorageInternalImpl
* newStorage
;
4826 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4830 list_init(&newStorage
->base
.strmHead
);
4832 list_init(&newStorage
->base
.storageHead
);
4835 * Initialize the virtual function table.
4837 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4838 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4839 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4841 newStorage
->base
.reverted
= 0;
4843 newStorage
->base
.ref
= 1;
4845 newStorage
->parentStorage
= parentStorage
;
4848 * Keep a reference to the directory entry of this storage
4850 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4852 newStorage
->base
.create
= 0;
4860 /******************************************************************************
4861 ** StorageUtl implementation
4864 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4868 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4869 *value
= lendian16toh(tmp
);
4872 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4874 value
= htole16(value
);
4875 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4878 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4882 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4883 *value
= lendian32toh(tmp
);
4886 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4888 value
= htole32(value
);
4889 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4892 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4893 ULARGE_INTEGER
* value
)
4895 #ifdef WORDS_BIGENDIAN
4898 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4899 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4900 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4902 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4906 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4907 const ULARGE_INTEGER
*value
)
4909 #ifdef WORDS_BIGENDIAN
4912 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4913 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4914 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4916 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4920 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4922 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4923 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4924 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4926 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4929 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4931 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4932 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4933 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4935 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4938 void StorageUtl_CopyDirEntryToSTATSTG(
4939 StorageBaseImpl
* storage
,
4940 STATSTG
* destination
,
4941 const DirEntry
* source
,
4946 if (source
->stgType
== STGTY_ROOT
)
4948 /* replace the name of root entry (often "Root Entry") by the file name */
4949 entryName
= storage
->filename
;
4953 entryName
= source
->name
;
4957 * The copy of the string occurs only when the flag is not set
4959 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4960 (entryName
== NULL
) ||
4961 (entryName
[0] == 0) )
4963 destination
->pwcsName
= 0;
4967 destination
->pwcsName
=
4968 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4970 strcpyW(destination
->pwcsName
, entryName
);
4973 switch (source
->stgType
)
4977 destination
->type
= STGTY_STORAGE
;
4980 destination
->type
= STGTY_STREAM
;
4983 destination
->type
= STGTY_STREAM
;
4987 destination
->cbSize
= source
->size
;
4989 currentReturnStruct->mtime = {0}; TODO
4990 currentReturnStruct->ctime = {0};
4991 currentReturnStruct->atime = {0};
4993 destination
->grfMode
= 0;
4994 destination
->grfLocksSupported
= 0;
4995 destination
->clsid
= source
->clsid
;
4996 destination
->grfStateBits
= 0;
4997 destination
->reserved
= 0;
5000 /******************************************************************************
5001 ** BlockChainStream implementation
5004 BlockChainStream
* BlockChainStream_Construct(
5005 StorageImpl
* parentStorage
,
5006 ULONG
* headOfStreamPlaceHolder
,
5009 BlockChainStream
* newStream
;
5012 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5014 newStream
->parentStorage
= parentStorage
;
5015 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5016 newStream
->ownerDirEntry
= dirEntry
;
5017 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
5018 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
5019 newStream
->numBlocks
= 0;
5021 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
5023 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5025 newStream
->numBlocks
++;
5026 newStream
->tailIndex
= blockIndex
;
5028 if(FAILED(StorageImpl_GetNextBlockInChain(
5033 HeapFree(GetProcessHeap(), 0, newStream
);
5041 void BlockChainStream_Destroy(BlockChainStream
* This
)
5043 HeapFree(GetProcessHeap(), 0, This
);
5046 /******************************************************************************
5047 * BlockChainStream_GetHeadOfChain
5049 * Returns the head of this stream chain.
5050 * Some special chains don't have directory entries, their heads are kept in
5051 * This->headOfStreamPlaceHolder.
5054 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5056 DirEntry chainEntry
;
5059 if (This
->headOfStreamPlaceHolder
!= 0)
5060 return *(This
->headOfStreamPlaceHolder
);
5062 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5064 hr
= StorageImpl_ReadDirEntry(
5065 This
->parentStorage
,
5066 This
->ownerDirEntry
,
5071 return chainEntry
.startingBlock
;
5075 return BLOCK_END_OF_CHAIN
;
5078 /******************************************************************************
5079 * BlockChainStream_GetCount
5081 * Returns the number of blocks that comprises this chain.
5082 * This is not the size of the stream as the last block may not be full!
5085 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5090 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5092 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5096 if(FAILED(StorageImpl_GetNextBlockInChain(
5097 This
->parentStorage
,
5106 /******************************************************************************
5107 * BlockChainStream_ReadAt
5109 * Reads a specified number of bytes from this chain at the specified offset.
5110 * bytesRead may be NULL.
5111 * Failure will be returned if the specified number of bytes has not been read.
5113 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5114 ULARGE_INTEGER offset
,
5119 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5120 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5121 ULONG bytesToReadInBuffer
;
5125 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5128 * Find the first block in the stream that contains part of the buffer.
5130 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5131 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5132 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5134 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5135 This
->lastBlockNoInSequence
= blockNoInSequence
;
5139 ULONG temp
= blockNoInSequence
;
5141 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5142 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5143 This
->lastBlockNoInSequence
= temp
;
5146 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5148 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5149 return STG_E_DOCFILECORRUPT
;
5150 blockNoInSequence
--;
5153 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
5154 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5156 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5159 * Start reading the buffer.
5162 bufferWalker
= buffer
;
5164 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5166 ULARGE_INTEGER ulOffset
;
5169 * Calculate how many bytes we can copy from this big block.
5171 bytesToReadInBuffer
=
5172 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5174 TRACE("block %i\n",blockIndex
);
5175 ulOffset
.u
.HighPart
= 0;
5176 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5179 StorageImpl_ReadAt(This
->parentStorage
,
5182 bytesToReadInBuffer
,
5185 * Step to the next big block.
5187 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5188 return STG_E_DOCFILECORRUPT
;
5190 bufferWalker
+= bytesReadAt
;
5191 size
-= bytesReadAt
;
5192 *bytesRead
+= bytesReadAt
;
5193 offsetInBlock
= 0; /* There is no offset on the next block */
5195 if (bytesToReadInBuffer
!= bytesReadAt
)
5199 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5202 /******************************************************************************
5203 * BlockChainStream_WriteAt
5205 * Writes the specified number of bytes to this chain at the specified offset.
5206 * Will fail if not all specified number of bytes have been written.
5208 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5209 ULARGE_INTEGER offset
,
5212 ULONG
* bytesWritten
)
5214 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5215 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5218 const BYTE
* bufferWalker
;
5221 * Find the first block in the stream that contains part of the buffer.
5223 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
5224 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
5225 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
5227 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5228 This
->lastBlockNoInSequence
= blockNoInSequence
;
5232 ULONG temp
= blockNoInSequence
;
5234 blockIndex
= This
->lastBlockNoInSequenceIndex
;
5235 blockNoInSequence
-= This
->lastBlockNoInSequence
;
5236 This
->lastBlockNoInSequence
= temp
;
5239 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5241 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5243 return STG_E_DOCFILECORRUPT
;
5244 blockNoInSequence
--;
5247 This
->lastBlockNoInSequenceIndex
= blockIndex
;
5249 /* BlockChainStream_SetSize should have already been called to ensure we have
5250 * enough blocks in the chain to write into */
5251 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5253 ERR("not enough blocks in chain to write data\n");
5254 return STG_E_DOCFILECORRUPT
;
5258 bufferWalker
= buffer
;
5260 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5262 ULARGE_INTEGER ulOffset
;
5263 DWORD bytesWrittenAt
;
5265 * Calculate how many bytes we can copy from this big block.
5268 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5270 TRACE("block %i\n",blockIndex
);
5271 ulOffset
.u
.HighPart
= 0;
5272 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
5275 StorageImpl_WriteAt(This
->parentStorage
,
5282 * Step to the next big block.
5284 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5286 return STG_E_DOCFILECORRUPT
;
5288 bufferWalker
+= bytesWrittenAt
;
5289 size
-= bytesWrittenAt
;
5290 *bytesWritten
+= bytesWrittenAt
;
5291 offsetInBlock
= 0; /* There is no offset on the next block */
5293 if (bytesWrittenAt
!= bytesToWrite
)
5297 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5300 /******************************************************************************
5301 * BlockChainStream_Shrink
5303 * Shrinks this chain in the big block depot.
5305 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5306 ULARGE_INTEGER newSize
)
5308 ULONG blockIndex
, extraBlock
;
5313 * Reset the last accessed block cache.
5315 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
5316 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
5319 * Figure out how many blocks are needed to contain the new size
5321 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5323 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5326 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5329 * Go to the new end of chain
5331 while (count
< numBlocks
)
5333 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5339 /* Get the next block before marking the new end */
5340 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5344 /* Mark the new end of chain */
5345 StorageImpl_SetNextBlockInChain(
5346 This
->parentStorage
,
5348 BLOCK_END_OF_CHAIN
);
5350 This
->tailIndex
= blockIndex
;
5351 This
->numBlocks
= numBlocks
;
5354 * Mark the extra blocks as free
5356 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5358 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
5361 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
5362 extraBlock
= blockIndex
;
5368 /******************************************************************************
5369 * BlockChainStream_Enlarge
5371 * Grows this chain in the big block depot.
5373 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5374 ULARGE_INTEGER newSize
)
5376 ULONG blockIndex
, currentBlock
;
5378 ULONG oldNumBlocks
= 0;
5380 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5383 * Empty chain. Create the head.
5385 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5387 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5388 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5390 BLOCK_END_OF_CHAIN
);
5392 if (This
->headOfStreamPlaceHolder
!= 0)
5394 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5398 DirEntry chainEntry
;
5399 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5401 StorageImpl_ReadDirEntry(
5402 This
->parentStorage
,
5403 This
->ownerDirEntry
,
5406 chainEntry
.startingBlock
= blockIndex
;
5408 StorageImpl_WriteDirEntry(
5409 This
->parentStorage
,
5410 This
->ownerDirEntry
,
5414 This
->tailIndex
= blockIndex
;
5415 This
->numBlocks
= 1;
5419 * Figure out how many blocks are needed to contain this stream
5421 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5423 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5427 * Go to the current end of chain
5429 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5431 currentBlock
= blockIndex
;
5433 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5436 currentBlock
= blockIndex
;
5438 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5443 This
->tailIndex
= currentBlock
;
5446 currentBlock
= This
->tailIndex
;
5447 oldNumBlocks
= This
->numBlocks
;
5450 * Add new blocks to the chain
5452 if (oldNumBlocks
< newNumBlocks
)
5454 while (oldNumBlocks
< newNumBlocks
)
5456 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5458 StorageImpl_SetNextBlockInChain(
5459 This
->parentStorage
,
5463 StorageImpl_SetNextBlockInChain(
5464 This
->parentStorage
,
5466 BLOCK_END_OF_CHAIN
);
5468 currentBlock
= blockIndex
;
5472 This
->tailIndex
= blockIndex
;
5473 This
->numBlocks
= newNumBlocks
;
5479 /******************************************************************************
5480 * BlockChainStream_SetSize
5482 * Sets the size of this stream. The big block depot will be updated.
5483 * The file will grow if we grow the chain.
5485 * TODO: Free the actual blocks in the file when we shrink the chain.
5486 * Currently, the blocks are still in the file. So the file size
5487 * doesn't shrink even if we shrink streams.
5489 BOOL
BlockChainStream_SetSize(
5490 BlockChainStream
* This
,
5491 ULARGE_INTEGER newSize
)
5493 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5495 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5498 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5500 BlockChainStream_Shrink(This
, newSize
);
5504 BlockChainStream_Enlarge(This
, newSize
);
5510 /******************************************************************************
5511 * BlockChainStream_GetSize
5513 * Returns the size of this chain.
5514 * Will return the block count if this chain doesn't have a directory entry.
5516 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5518 DirEntry chainEntry
;
5520 if(This
->headOfStreamPlaceHolder
== NULL
)
5523 * This chain has a directory entry so use the size value from there.
5525 StorageImpl_ReadDirEntry(
5526 This
->parentStorage
,
5527 This
->ownerDirEntry
,
5530 return chainEntry
.size
;
5535 * this chain is a chain that does not have a directory entry, figure out the
5536 * size by making the product number of used blocks times the
5539 ULARGE_INTEGER result
;
5540 result
.u
.HighPart
= 0;
5543 BlockChainStream_GetCount(This
) *
5544 This
->parentStorage
->bigBlockSize
;
5550 /******************************************************************************
5551 ** SmallBlockChainStream implementation
5554 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5555 StorageImpl
* parentStorage
,
5556 ULONG
* headOfStreamPlaceHolder
,
5559 SmallBlockChainStream
* newStream
;
5561 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5563 newStream
->parentStorage
= parentStorage
;
5564 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5565 newStream
->ownerDirEntry
= dirEntry
;
5570 void SmallBlockChainStream_Destroy(
5571 SmallBlockChainStream
* This
)
5573 HeapFree(GetProcessHeap(), 0, This
);
5576 /******************************************************************************
5577 * SmallBlockChainStream_GetHeadOfChain
5579 * Returns the head of this chain of small blocks.
5581 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5582 SmallBlockChainStream
* This
)
5584 DirEntry chainEntry
;
5587 if (This
->headOfStreamPlaceHolder
!= NULL
)
5588 return *(This
->headOfStreamPlaceHolder
);
5590 if (This
->ownerDirEntry
)
5592 hr
= StorageImpl_ReadDirEntry(
5593 This
->parentStorage
,
5594 This
->ownerDirEntry
,
5599 return chainEntry
.startingBlock
;
5604 return BLOCK_END_OF_CHAIN
;
5607 /******************************************************************************
5608 * SmallBlockChainStream_GetNextBlockInChain
5610 * Returns the index of the next small block in this chain.
5613 * - BLOCK_END_OF_CHAIN: end of this chain
5614 * - BLOCK_UNUSED: small block 'blockIndex' is free
5616 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5617 SmallBlockChainStream
* This
,
5619 ULONG
* nextBlockInChain
)
5621 ULARGE_INTEGER offsetOfBlockInDepot
;
5626 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5628 offsetOfBlockInDepot
.u
.HighPart
= 0;
5629 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5632 * Read those bytes in the buffer from the small block file.
5634 res
= BlockChainStream_ReadAt(
5635 This
->parentStorage
->smallBlockDepotChain
,
5636 offsetOfBlockInDepot
,
5643 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5650 /******************************************************************************
5651 * SmallBlockChainStream_SetNextBlockInChain
5653 * Writes the index of the next block of the specified block in the small
5655 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5656 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5658 static void SmallBlockChainStream_SetNextBlockInChain(
5659 SmallBlockChainStream
* This
,
5663 ULARGE_INTEGER offsetOfBlockInDepot
;
5667 offsetOfBlockInDepot
.u
.HighPart
= 0;
5668 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5670 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5673 * Read those bytes in the buffer from the small block file.
5675 BlockChainStream_WriteAt(
5676 This
->parentStorage
->smallBlockDepotChain
,
5677 offsetOfBlockInDepot
,
5683 /******************************************************************************
5684 * SmallBlockChainStream_FreeBlock
5686 * Flag small block 'blockIndex' as free in the small block depot.
5688 static void SmallBlockChainStream_FreeBlock(
5689 SmallBlockChainStream
* This
,
5692 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5695 /******************************************************************************
5696 * SmallBlockChainStream_GetNextFreeBlock
5698 * Returns the index of a free small block. The small block depot will be
5699 * enlarged if necessary. The small block chain will also be enlarged if
5702 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5703 SmallBlockChainStream
* This
)
5705 ULARGE_INTEGER offsetOfBlockInDepot
;
5708 ULONG blockIndex
= 0;
5709 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5711 ULONG smallBlocksPerBigBlock
;
5713 offsetOfBlockInDepot
.u
.HighPart
= 0;
5716 * Scan the small block depot for a free block
5718 while (nextBlockIndex
!= BLOCK_UNUSED
)
5720 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5722 res
= BlockChainStream_ReadAt(
5723 This
->parentStorage
->smallBlockDepotChain
,
5724 offsetOfBlockInDepot
,
5730 * If we run out of space for the small block depot, enlarge it
5734 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5736 if (nextBlockIndex
!= BLOCK_UNUSED
)
5742 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5744 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5745 ULONG nextBlock
, newsbdIndex
;
5746 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5748 nextBlock
= sbdIndex
;
5749 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5751 sbdIndex
= nextBlock
;
5752 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5755 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5756 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5757 StorageImpl_SetNextBlockInChain(
5758 This
->parentStorage
,
5762 StorageImpl_SetNextBlockInChain(
5763 This
->parentStorage
,
5765 BLOCK_END_OF_CHAIN
);
5768 * Initialize all the small blocks to free
5770 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5771 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5776 * We have just created the small block depot.
5782 * Save it in the header
5784 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5785 StorageImpl_SaveFileHeader(This
->parentStorage
);
5788 * And allocate the first big block that will contain small blocks
5791 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5793 StorageImpl_SetNextBlockInChain(
5794 This
->parentStorage
,
5796 BLOCK_END_OF_CHAIN
);
5798 StorageImpl_ReadDirEntry(
5799 This
->parentStorage
,
5800 This
->parentStorage
->base
.storageDirEntry
,
5803 rootEntry
.startingBlock
= sbStartIndex
;
5804 rootEntry
.size
.u
.HighPart
= 0;
5805 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5807 StorageImpl_WriteDirEntry(
5808 This
->parentStorage
,
5809 This
->parentStorage
->base
.storageDirEntry
,
5813 StorageImpl_SaveFileHeader(This
->parentStorage
);
5817 smallBlocksPerBigBlock
=
5818 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5821 * Verify if we have to allocate big blocks to contain small blocks
5823 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5826 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5828 StorageImpl_ReadDirEntry(
5829 This
->parentStorage
,
5830 This
->parentStorage
->base
.storageDirEntry
,
5833 if (rootEntry
.size
.u
.LowPart
<
5834 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5836 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5838 BlockChainStream_SetSize(
5839 This
->parentStorage
->smallBlockRootChain
,
5842 StorageImpl_WriteDirEntry(
5843 This
->parentStorage
,
5844 This
->parentStorage
->base
.storageDirEntry
,
5852 /******************************************************************************
5853 * SmallBlockChainStream_ReadAt
5855 * Reads a specified number of bytes from this chain at the specified offset.
5856 * bytesRead may be NULL.
5857 * Failure will be returned if the specified number of bytes has not been read.
5859 HRESULT
SmallBlockChainStream_ReadAt(
5860 SmallBlockChainStream
* This
,
5861 ULARGE_INTEGER offset
,
5867 ULARGE_INTEGER offsetInBigBlockFile
;
5868 ULONG blockNoInSequence
=
5869 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5871 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5872 ULONG bytesToReadInBuffer
;
5874 ULONG bytesReadFromBigBlockFile
;
5878 * This should never happen on a small block file.
5880 assert(offset
.u
.HighPart
==0);
5883 * Find the first block in the stream that contains part of the buffer.
5885 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5887 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5889 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5892 blockNoInSequence
--;
5896 * Start reading the buffer.
5899 bufferWalker
= buffer
;
5901 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5904 * Calculate how many bytes we can copy from this small block.
5906 bytesToReadInBuffer
=
5907 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5910 * Calculate the offset of the small block in the small block file.
5912 offsetInBigBlockFile
.u
.HighPart
= 0;
5913 offsetInBigBlockFile
.u
.LowPart
=
5914 blockIndex
* This
->parentStorage
->smallBlockSize
;
5916 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5919 * Read those bytes in the buffer from the small block file.
5920 * The small block has already been identified so it shouldn't fail
5921 * unless the file is corrupt.
5923 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5924 offsetInBigBlockFile
,
5925 bytesToReadInBuffer
,
5927 &bytesReadFromBigBlockFile
);
5933 * Step to the next big block.
5935 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5937 return STG_E_DOCFILECORRUPT
;
5939 bufferWalker
+= bytesReadFromBigBlockFile
;
5940 size
-= bytesReadFromBigBlockFile
;
5941 *bytesRead
+= bytesReadFromBigBlockFile
;
5942 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5945 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5948 /******************************************************************************
5949 * SmallBlockChainStream_WriteAt
5951 * Writes the specified number of bytes to this chain at the specified offset.
5952 * Will fail if not all specified number of bytes have been written.
5954 HRESULT
SmallBlockChainStream_WriteAt(
5955 SmallBlockChainStream
* This
,
5956 ULARGE_INTEGER offset
,
5959 ULONG
* bytesWritten
)
5961 ULARGE_INTEGER offsetInBigBlockFile
;
5962 ULONG blockNoInSequence
=
5963 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5965 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5966 ULONG bytesToWriteInBuffer
;
5968 ULONG bytesWrittenToBigBlockFile
;
5969 const BYTE
* bufferWalker
;
5973 * This should never happen on a small block file.
5975 assert(offset
.u
.HighPart
==0);
5978 * Find the first block in the stream that contains part of the buffer.
5980 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5982 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5984 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5985 return STG_E_DOCFILECORRUPT
;
5986 blockNoInSequence
--;
5990 * Start writing the buffer.
5993 bufferWalker
= buffer
;
5994 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5997 * Calculate how many bytes we can copy to this small block.
5999 bytesToWriteInBuffer
=
6000 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6003 * Calculate the offset of the small block in the small block file.
6005 offsetInBigBlockFile
.u
.HighPart
= 0;
6006 offsetInBigBlockFile
.u
.LowPart
=
6007 blockIndex
* This
->parentStorage
->smallBlockSize
;
6009 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6012 * Write those bytes in the buffer to the small block file.
6014 res
= BlockChainStream_WriteAt(
6015 This
->parentStorage
->smallBlockRootChain
,
6016 offsetInBigBlockFile
,
6017 bytesToWriteInBuffer
,
6019 &bytesWrittenToBigBlockFile
);
6024 * Step to the next big block.
6026 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6029 bufferWalker
+= bytesWrittenToBigBlockFile
;
6030 size
-= bytesWrittenToBigBlockFile
;
6031 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6032 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6035 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6038 /******************************************************************************
6039 * SmallBlockChainStream_Shrink
6041 * Shrinks this chain in the small block depot.
6043 static BOOL
SmallBlockChainStream_Shrink(
6044 SmallBlockChainStream
* This
,
6045 ULARGE_INTEGER newSize
)
6047 ULONG blockIndex
, extraBlock
;
6051 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6053 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6056 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6059 * Go to the new end of chain
6061 while (count
< numBlocks
)
6063 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6070 * If the count is 0, we have a special case, the head of the chain was
6075 DirEntry chainEntry
;
6077 StorageImpl_ReadDirEntry(This
->parentStorage
,
6078 This
->ownerDirEntry
,
6081 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6083 StorageImpl_WriteDirEntry(This
->parentStorage
,
6084 This
->ownerDirEntry
,
6088 * We start freeing the chain at the head block.
6090 extraBlock
= blockIndex
;
6094 /* Get the next block before marking the new end */
6095 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6099 /* Mark the new end of chain */
6100 SmallBlockChainStream_SetNextBlockInChain(
6103 BLOCK_END_OF_CHAIN
);
6107 * Mark the extra blocks as free
6109 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6111 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6114 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6115 extraBlock
= blockIndex
;
6121 /******************************************************************************
6122 * SmallBlockChainStream_Enlarge
6124 * Grows this chain in the small block depot.
6126 static BOOL
SmallBlockChainStream_Enlarge(
6127 SmallBlockChainStream
* This
,
6128 ULARGE_INTEGER newSize
)
6130 ULONG blockIndex
, currentBlock
;
6132 ULONG oldNumBlocks
= 0;
6134 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6137 * Empty chain. Create the head.
6139 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6141 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6142 SmallBlockChainStream_SetNextBlockInChain(
6145 BLOCK_END_OF_CHAIN
);
6147 if (This
->headOfStreamPlaceHolder
!= NULL
)
6149 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6153 DirEntry chainEntry
;
6155 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6158 chainEntry
.startingBlock
= blockIndex
;
6160 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6165 currentBlock
= blockIndex
;
6168 * Figure out how many blocks are needed to contain this stream
6170 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6172 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6176 * Go to the current end of chain
6178 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6181 currentBlock
= blockIndex
;
6182 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6187 * Add new blocks to the chain
6189 while (oldNumBlocks
< newNumBlocks
)
6191 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6192 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6194 SmallBlockChainStream_SetNextBlockInChain(
6197 BLOCK_END_OF_CHAIN
);
6199 currentBlock
= blockIndex
;
6206 /******************************************************************************
6207 * SmallBlockChainStream_SetSize
6209 * Sets the size of this stream.
6210 * The file will grow if we grow the chain.
6212 * TODO: Free the actual blocks in the file when we shrink the chain.
6213 * Currently, the blocks are still in the file. So the file size
6214 * doesn't shrink even if we shrink streams.
6216 BOOL
SmallBlockChainStream_SetSize(
6217 SmallBlockChainStream
* This
,
6218 ULARGE_INTEGER newSize
)
6220 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6222 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6225 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6227 SmallBlockChainStream_Shrink(This
, newSize
);
6231 SmallBlockChainStream_Enlarge(This
, newSize
);
6237 /******************************************************************************
6238 * SmallBlockChainStream_GetCount
6240 * Returns the number of small blocks that comprises this chain.
6241 * This is not the size of the stream as the last block may not be full!
6244 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6249 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6251 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6255 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6256 blockIndex
, &blockIndex
)))
6263 /******************************************************************************
6264 * SmallBlockChainStream_GetSize
6266 * Returns the size of this chain.
6268 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6270 DirEntry chainEntry
;
6272 if(This
->headOfStreamPlaceHolder
!= NULL
)
6274 ULARGE_INTEGER result
;
6275 result
.u
.HighPart
= 0;
6277 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6278 This
->parentStorage
->smallBlockSize
;
6283 StorageImpl_ReadDirEntry(
6284 This
->parentStorage
,
6285 This
->ownerDirEntry
,
6288 return chainEntry
.size
;
6291 /******************************************************************************
6292 * StgCreateDocfile [OLE32.@]
6293 * Creates a new compound file storage object
6296 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6297 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6298 * reserved [ ?] unused?, usually 0
6299 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6302 * S_OK if the file was successfully created
6303 * some STG_E_ value if error
6305 * if pwcsName is NULL, create file with new unique name
6306 * the function can returns
6307 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6310 HRESULT WINAPI
StgCreateDocfile(
6314 IStorage
**ppstgOpen
)
6316 StorageBaseImpl
* newStorage
= 0;
6317 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6318 HRESULT hr
= STG_E_INVALIDFLAG
;
6322 DWORD fileAttributes
;
6323 WCHAR tempFileName
[MAX_PATH
];
6325 TRACE("(%s, %x, %d, %p)\n",
6326 debugstr_w(pwcsName
), grfMode
,
6327 reserved
, ppstgOpen
);
6330 return STG_E_INVALIDPOINTER
;
6332 return STG_E_INVALIDPARAMETER
;
6334 /* if no share mode given then DENY_NONE is the default */
6335 if (STGM_SHARE_MODE(grfMode
) == 0)
6336 grfMode
|= STGM_SHARE_DENY_NONE
;
6338 if ( FAILED( validateSTGM(grfMode
) ))
6341 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6342 switch(STGM_ACCESS_MODE(grfMode
))
6345 case STGM_READWRITE
:
6351 /* in direct mode, can only use SHARE_EXCLUSIVE */
6352 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6355 /* but in transacted mode, any share mode is valid */
6358 * Generate a unique name.
6362 WCHAR tempPath
[MAX_PATH
];
6363 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6365 memset(tempPath
, 0, sizeof(tempPath
));
6366 memset(tempFileName
, 0, sizeof(tempFileName
));
6368 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6371 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6372 pwcsName
= tempFileName
;
6375 hr
= STG_E_INSUFFICIENTMEMORY
;
6379 creationMode
= TRUNCATE_EXISTING
;
6383 creationMode
= GetCreationModeFromSTGM(grfMode
);
6387 * Interpret the STGM value grfMode
6389 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6390 accessMode
= GetAccessModeFromSTGM(grfMode
);
6392 if (grfMode
& STGM_DELETEONRELEASE
)
6393 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6395 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6397 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6398 FIXME("Storage share mode not implemented.\n");
6400 if (grfMode
& STGM_TRANSACTED
)
6401 FIXME("Transacted mode not implemented.\n");
6405 hFile
= CreateFileW(pwcsName
,
6413 if (hFile
== INVALID_HANDLE_VALUE
)
6415 if(GetLastError() == ERROR_FILE_EXISTS
)
6416 hr
= STG_E_FILEALREADYEXISTS
;
6423 * Allocate and initialize the new IStorage32object.
6425 hr
= Storage_Construct(
6440 * Get an "out" pointer for the caller.
6442 *ppstgOpen
= (IStorage
*)newStorage
;
6445 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6450 /******************************************************************************
6451 * StgCreateStorageEx [OLE32.@]
6453 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6455 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6456 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6458 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6460 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6461 return STG_E_INVALIDPARAMETER
;
6464 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6466 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6467 return STG_E_INVALIDPARAMETER
;
6470 if (stgfmt
== STGFMT_FILE
)
6472 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6473 return STG_E_INVALIDPARAMETER
;
6476 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6478 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6479 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
6482 ERR("Invalid stgfmt argument\n");
6483 return STG_E_INVALIDPARAMETER
;
6486 /******************************************************************************
6487 * StgCreatePropSetStg [OLE32.@]
6489 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6490 IPropertySetStorage
**ppPropSetStg
)
6494 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6496 hr
= STG_E_INVALIDPARAMETER
;
6498 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6499 (void**)ppPropSetStg
);
6503 /******************************************************************************
6504 * StgOpenStorageEx [OLE32.@]
6506 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6508 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6509 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6511 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6513 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6514 return STG_E_INVALIDPARAMETER
;
6520 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6521 return STG_E_INVALIDPARAMETER
;
6523 case STGFMT_STORAGE
:
6526 case STGFMT_DOCFILE
:
6527 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6529 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6530 return STG_E_INVALIDPARAMETER
;
6532 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6536 WARN("STGFMT_ANY assuming storage\n");
6540 return STG_E_INVALIDPARAMETER
;
6543 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6547 /******************************************************************************
6548 * StgOpenStorage [OLE32.@]
6550 HRESULT WINAPI
StgOpenStorage(
6551 const OLECHAR
*pwcsName
,
6552 IStorage
*pstgPriority
,
6556 IStorage
**ppstgOpen
)
6558 StorageBaseImpl
* newStorage
= 0;
6564 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6565 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6566 snbExclude
, reserved
, ppstgOpen
);
6570 hr
= STG_E_INVALIDNAME
;
6576 hr
= STG_E_INVALIDPOINTER
;
6582 hr
= STG_E_INVALIDPARAMETER
;
6586 if (grfMode
& STGM_PRIORITY
)
6588 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6589 return STG_E_INVALIDFLAG
;
6590 if (grfMode
& STGM_DELETEONRELEASE
)
6591 return STG_E_INVALIDFUNCTION
;
6592 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6593 return STG_E_INVALIDFLAG
;
6594 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6595 grfMode
|= STGM_SHARE_DENY_NONE
;
6597 /* STGM_PRIORITY stops other IStorage objects on the same file from
6598 * committing until the STGM_PRIORITY IStorage is closed. it also
6599 * stops non-transacted mode StgOpenStorage calls with write access from
6600 * succeeding. obviously, both of these cannot be achieved through just
6601 * file share flags */
6602 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6606 * Validate the sharing mode
6608 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6609 switch(STGM_SHARE_MODE(grfMode
))
6611 case STGM_SHARE_EXCLUSIVE
:
6612 case STGM_SHARE_DENY_WRITE
:
6615 hr
= STG_E_INVALIDFLAG
;
6619 if ( FAILED( validateSTGM(grfMode
) ) ||
6620 (grfMode
&STGM_CREATE
))
6622 hr
= STG_E_INVALIDFLAG
;
6626 /* shared reading requires transacted mode */
6627 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6628 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6629 !(grfMode
&STGM_TRANSACTED
) )
6631 hr
= STG_E_INVALIDFLAG
;
6636 * Interpret the STGM value grfMode
6638 shareMode
= GetShareModeFromSTGM(grfMode
);
6639 accessMode
= GetAccessModeFromSTGM(grfMode
);
6643 hFile
= CreateFileW( pwcsName
,
6648 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6651 if (hFile
==INVALID_HANDLE_VALUE
)
6653 DWORD last_error
= GetLastError();
6659 case ERROR_FILE_NOT_FOUND
:
6660 hr
= STG_E_FILENOTFOUND
;
6663 case ERROR_PATH_NOT_FOUND
:
6664 hr
= STG_E_PATHNOTFOUND
;
6667 case ERROR_ACCESS_DENIED
:
6668 case ERROR_WRITE_PROTECT
:
6669 hr
= STG_E_ACCESSDENIED
;
6672 case ERROR_SHARING_VIOLATION
:
6673 hr
= STG_E_SHAREVIOLATION
;
6684 * Refuse to open the file if it's too small to be a structured storage file
6685 * FIXME: verify the file when reading instead of here
6687 if (GetFileSize(hFile
, NULL
) < 0x100)
6690 hr
= STG_E_FILEALREADYEXISTS
;
6695 * Allocate and initialize the new IStorage32object.
6697 hr
= Storage_Construct(
6709 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6711 if(hr
== STG_E_INVALIDHEADER
)
6712 hr
= STG_E_FILEALREADYEXISTS
;
6717 * Get an "out" pointer for the caller.
6719 *ppstgOpen
= (IStorage
*)newStorage
;
6722 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6726 /******************************************************************************
6727 * StgCreateDocfileOnILockBytes [OLE32.@]
6729 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6733 IStorage
** ppstgOpen
)
6735 StorageBaseImpl
* newStorage
= 0;
6738 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6739 return STG_E_INVALIDPOINTER
;
6742 * Allocate and initialize the new IStorage object.
6744 hr
= Storage_Construct(
6759 * Get an "out" pointer for the caller.
6761 *ppstgOpen
= (IStorage
*)newStorage
;
6766 /******************************************************************************
6767 * StgOpenStorageOnILockBytes [OLE32.@]
6769 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6771 IStorage
*pstgPriority
,
6775 IStorage
**ppstgOpen
)
6777 StorageBaseImpl
* newStorage
= 0;
6780 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6781 return STG_E_INVALIDPOINTER
;
6783 if ( FAILED( validateSTGM(grfMode
) ))
6784 return STG_E_INVALIDFLAG
;
6789 * Allocate and initialize the new IStorage object.
6791 hr
= Storage_Construct(
6806 * Get an "out" pointer for the caller.
6808 *ppstgOpen
= (IStorage
*)newStorage
;
6813 /******************************************************************************
6814 * StgSetTimes [ole32.@]
6815 * StgSetTimes [OLE32.@]
6819 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6820 FILETIME
const *patime
, FILETIME
const *pmtime
)
6822 IStorage
*stg
= NULL
;
6825 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6827 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6831 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6832 IStorage_Release(stg
);
6838 /******************************************************************************
6839 * StgIsStorageILockBytes [OLE32.@]
6841 * Determines if the ILockBytes contains a storage object.
6843 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6846 ULARGE_INTEGER offset
;
6848 offset
.u
.HighPart
= 0;
6849 offset
.u
.LowPart
= 0;
6851 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6853 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6859 /******************************************************************************
6860 * WriteClassStg [OLE32.@]
6862 * This method will store the specified CLSID in the specified storage object
6864 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6869 return E_INVALIDARG
;
6872 return STG_E_INVALIDPOINTER
;
6874 hRes
= IStorage_SetClass(pStg
, rclsid
);
6879 /***********************************************************************
6880 * ReadClassStg (OLE32.@)
6882 * This method reads the CLSID previously written to a storage object with
6883 * the WriteClassStg.
6886 * pstg [I] IStorage pointer
6887 * pclsid [O] Pointer to where the CLSID is written
6891 * Failure: HRESULT code.
6893 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6898 TRACE("(%p, %p)\n", pstg
, pclsid
);
6900 if(!pstg
|| !pclsid
)
6901 return E_INVALIDARG
;
6904 * read a STATSTG structure (contains the clsid) from the storage
6906 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6909 *pclsid
=pstatstg
.clsid
;
6914 /***********************************************************************
6915 * OleLoadFromStream (OLE32.@)
6917 * This function loads an object from stream
6919 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6923 LPPERSISTSTREAM xstm
;
6925 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6927 res
=ReadClassStm(pStm
,&clsid
);
6930 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6933 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6935 IUnknown_Release((IUnknown
*)*ppvObj
);
6938 res
=IPersistStream_Load(xstm
,pStm
);
6939 IPersistStream_Release(xstm
);
6940 /* FIXME: all refcounts ok at this point? I think they should be:
6943 * xstm : 0 (released)
6948 /***********************************************************************
6949 * OleSaveToStream (OLE32.@)
6951 * This function saves an object with the IPersistStream interface on it
6952 * to the specified stream.
6954 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6960 TRACE("(%p,%p)\n",pPStm
,pStm
);
6962 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6964 if (SUCCEEDED(res
)){
6966 res
=WriteClassStm(pStm
,&clsid
);
6970 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6973 TRACE("Finished Save\n");
6977 /****************************************************************************
6978 * This method validate a STGM parameter that can contain the values below
6980 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6981 * The stgm values contained in 0xffff0000 are bitmasks.
6983 * STGM_DIRECT 0x00000000
6984 * STGM_TRANSACTED 0x00010000
6985 * STGM_SIMPLE 0x08000000
6987 * STGM_READ 0x00000000
6988 * STGM_WRITE 0x00000001
6989 * STGM_READWRITE 0x00000002
6991 * STGM_SHARE_DENY_NONE 0x00000040
6992 * STGM_SHARE_DENY_READ 0x00000030
6993 * STGM_SHARE_DENY_WRITE 0x00000020
6994 * STGM_SHARE_EXCLUSIVE 0x00000010
6996 * STGM_PRIORITY 0x00040000
6997 * STGM_DELETEONRELEASE 0x04000000
6999 * STGM_CREATE 0x00001000
7000 * STGM_CONVERT 0x00020000
7001 * STGM_FAILIFTHERE 0x00000000
7003 * STGM_NOSCRATCH 0x00100000
7004 * STGM_NOSNAPSHOT 0x00200000
7006 static HRESULT
validateSTGM(DWORD stgm
)
7008 DWORD access
= STGM_ACCESS_MODE(stgm
);
7009 DWORD share
= STGM_SHARE_MODE(stgm
);
7010 DWORD create
= STGM_CREATE_MODE(stgm
);
7012 if (stgm
&~STGM_KNOWN_FLAGS
)
7014 ERR("unknown flags %08x\n", stgm
);
7022 case STGM_READWRITE
:
7030 case STGM_SHARE_DENY_NONE
:
7031 case STGM_SHARE_DENY_READ
:
7032 case STGM_SHARE_DENY_WRITE
:
7033 case STGM_SHARE_EXCLUSIVE
:
7042 case STGM_FAILIFTHERE
:
7049 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7051 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7055 * STGM_CREATE | STGM_CONVERT
7056 * if both are false, STGM_FAILIFTHERE is set to TRUE
7058 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7062 * STGM_NOSCRATCH requires STGM_TRANSACTED
7064 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7068 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7069 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7071 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7072 (!(stgm
& STGM_TRANSACTED
) ||
7073 share
== STGM_SHARE_EXCLUSIVE
||
7074 share
== STGM_SHARE_DENY_WRITE
) )
7080 /****************************************************************************
7081 * GetShareModeFromSTGM
7083 * This method will return a share mode flag from a STGM value.
7084 * The STGM value is assumed valid.
7086 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7088 switch (STGM_SHARE_MODE(stgm
))
7090 case STGM_SHARE_DENY_NONE
:
7091 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7092 case STGM_SHARE_DENY_READ
:
7093 return FILE_SHARE_WRITE
;
7094 case STGM_SHARE_DENY_WRITE
:
7095 return FILE_SHARE_READ
;
7096 case STGM_SHARE_EXCLUSIVE
:
7099 ERR("Invalid share mode!\n");
7104 /****************************************************************************
7105 * GetAccessModeFromSTGM
7107 * This method will return an access mode flag from a STGM value.
7108 * The STGM value is assumed valid.
7110 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7112 switch (STGM_ACCESS_MODE(stgm
))
7115 return GENERIC_READ
;
7117 case STGM_READWRITE
:
7118 return GENERIC_READ
| GENERIC_WRITE
;
7120 ERR("Invalid access mode!\n");
7125 /****************************************************************************
7126 * GetCreationModeFromSTGM
7128 * This method will return a creation mode flag from a STGM value.
7129 * The STGM value is assumed valid.
7131 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7133 switch(STGM_CREATE_MODE(stgm
))
7136 return CREATE_ALWAYS
;
7138 FIXME("STGM_CONVERT not implemented!\n");
7140 case STGM_FAILIFTHERE
:
7143 ERR("Invalid create mode!\n");
7149 /*************************************************************************
7150 * OLECONVERT_LoadOLE10 [Internal]
7152 * Loads the OLE10 STREAM to memory
7155 * pOleStream [I] The OLESTREAM
7156 * pData [I] Data Structure for the OLESTREAM Data
7160 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7161 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7164 * This function is used by OleConvertOLESTREAMToIStorage only.
7166 * Memory allocated for pData must be freed by the caller
7168 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7171 HRESULT hRes
= S_OK
;
7175 pData
->pData
= NULL
;
7176 pData
->pstrOleObjFileName
= NULL
;
7178 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7181 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7182 if(dwSize
!= sizeof(pData
->dwOleID
))
7184 hRes
= CONVERT10_E_OLESTREAM_GET
;
7186 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7188 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7199 /* Get the TypeID... more info needed for this field */
7200 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7201 if(dwSize
!= sizeof(pData
->dwTypeID
))
7203 hRes
= CONVERT10_E_OLESTREAM_GET
;
7208 if(pData
->dwTypeID
!= 0)
7210 /* Get the length of the OleTypeName */
7211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7212 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7214 hRes
= CONVERT10_E_OLESTREAM_GET
;
7219 if(pData
->dwOleTypeNameLength
> 0)
7221 /* Get the OleTypeName */
7222 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7223 if(dwSize
!= pData
->dwOleTypeNameLength
)
7225 hRes
= CONVERT10_E_OLESTREAM_GET
;
7231 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7232 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7234 hRes
= CONVERT10_E_OLESTREAM_GET
;
7238 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7239 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7240 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7241 if(pData
->pstrOleObjFileName
)
7243 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7244 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7246 hRes
= CONVERT10_E_OLESTREAM_GET
;
7250 hRes
= CONVERT10_E_OLESTREAM_GET
;
7255 /* Get the Width of the Metafile */
7256 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7257 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7259 hRes
= CONVERT10_E_OLESTREAM_GET
;
7263 /* Get the Height of the Metafile */
7264 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7265 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7267 hRes
= CONVERT10_E_OLESTREAM_GET
;
7273 /* Get the Length of the Data */
7274 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7275 if(dwSize
!= sizeof(pData
->dwDataLength
))
7277 hRes
= CONVERT10_E_OLESTREAM_GET
;
7281 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7283 if(!bStrem1
) /* if it is a second OLE stream data */
7285 pData
->dwDataLength
-= 8;
7286 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7287 if(dwSize
!= sizeof(pData
->strUnknown
))
7289 hRes
= CONVERT10_E_OLESTREAM_GET
;
7295 if(pData
->dwDataLength
> 0)
7297 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7299 /* Get Data (ex. IStorage, Metafile, or BMP) */
7302 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7303 if(dwSize
!= pData
->dwDataLength
)
7305 hRes
= CONVERT10_E_OLESTREAM_GET
;
7310 hRes
= CONVERT10_E_OLESTREAM_GET
;
7319 /*************************************************************************
7320 * OLECONVERT_SaveOLE10 [Internal]
7322 * Saves the OLE10 STREAM From memory
7325 * pData [I] Data Structure for the OLESTREAM Data
7326 * pOleStream [I] The OLESTREAM to save
7330 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7333 * This function is used by OleConvertIStorageToOLESTREAM only.
7336 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7339 HRESULT hRes
= S_OK
;
7343 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7344 if(dwSize
!= sizeof(pData
->dwOleID
))
7346 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7351 /* Set the TypeID */
7352 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7353 if(dwSize
!= sizeof(pData
->dwTypeID
))
7355 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7359 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7361 /* Set the Length of the OleTypeName */
7362 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7363 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7365 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7370 if(pData
->dwOleTypeNameLength
> 0)
7372 /* Set the OleTypeName */
7373 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7374 if(dwSize
!= pData
->dwOleTypeNameLength
)
7376 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7383 /* Set the width of the Metafile */
7384 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7385 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7387 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7393 /* Set the height of the Metafile */
7394 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7395 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7397 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7403 /* Set the length of the Data */
7404 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7405 if(dwSize
!= sizeof(pData
->dwDataLength
))
7407 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7413 if(pData
->dwDataLength
> 0)
7415 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7416 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7417 if(dwSize
!= pData
->dwDataLength
)
7419 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7427 /*************************************************************************
7428 * OLECONVERT_GetOLE20FromOLE10[Internal]
7430 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7431 * opens it, and copies the content to the dest IStorage for
7432 * OleConvertOLESTREAMToIStorage
7436 * pDestStorage [I] The IStorage to copy the data to
7437 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7438 * nBufferLength [I] The size of the buffer
7447 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7451 IStorage
*pTempStorage
;
7452 DWORD dwNumOfBytesWritten
;
7453 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7454 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7456 /* Create a temp File */
7457 GetTempPathW(MAX_PATH
, wstrTempDir
);
7458 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7459 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7461 if(hFile
!= INVALID_HANDLE_VALUE
)
7463 /* Write IStorage Data to File */
7464 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7467 /* Open and copy temp storage to the Dest Storage */
7468 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7471 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7472 IStorage_Release(pTempStorage
);
7474 DeleteFileW(wstrTempFile
);
7479 /*************************************************************************
7480 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7482 * Saves the OLE10 STREAM From memory
7485 * pStorage [I] The Src IStorage to copy
7486 * pData [I] The Dest Memory to write to.
7489 * The size in bytes allocated for pData
7492 * Memory allocated for pData must be freed by the caller
7494 * Used by OleConvertIStorageToOLESTREAM only.
7497 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7501 DWORD nDataLength
= 0;
7502 IStorage
*pTempStorage
;
7503 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7504 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7508 /* Create temp Storage */
7509 GetTempPathW(MAX_PATH
, wstrTempDir
);
7510 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7511 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7515 /* Copy Src Storage to the Temp Storage */
7516 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7517 IStorage_Release(pTempStorage
);
7519 /* Open Temp Storage as a file and copy to memory */
7520 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7521 if(hFile
!= INVALID_HANDLE_VALUE
)
7523 nDataLength
= GetFileSize(hFile
, NULL
);
7524 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7525 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7528 DeleteFileW(wstrTempFile
);
7533 /*************************************************************************
7534 * OLECONVERT_CreateOleStream [Internal]
7536 * Creates the "\001OLE" stream in the IStorage if necessary.
7539 * pStorage [I] Dest storage to create the stream in
7545 * This function is used by OleConvertOLESTREAMToIStorage only.
7547 * This stream is still unknown, MS Word seems to have extra data
7548 * but since the data is stored in the OLESTREAM there should be
7549 * no need to recreate the stream. If the stream is manually
7550 * deleted it will create it with this default data.
7553 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7557 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7558 BYTE pOleStreamHeader
[] =
7560 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7562 0x00, 0x00, 0x00, 0x00
7565 /* Create stream if not present */
7566 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7567 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7571 /* Write default Data */
7572 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7573 IStream_Release(pStream
);
7577 /* write a string to a stream, preceded by its length */
7578 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7585 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7586 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7591 str
= CoTaskMemAlloc( len
);
7592 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7593 r
= IStream_Write( stm
, str
, len
, NULL
);
7594 CoTaskMemFree( str
);
7598 /* read a string preceded by its length from a stream */
7599 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7602 DWORD len
, count
= 0;
7606 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7609 if( count
!= sizeof(len
) )
7610 return E_OUTOFMEMORY
;
7612 TRACE("%d bytes\n",len
);
7614 str
= CoTaskMemAlloc( len
);
7616 return E_OUTOFMEMORY
;
7618 r
= IStream_Read( stm
, str
, len
, &count
);
7623 CoTaskMemFree( str
);
7624 return E_OUTOFMEMORY
;
7627 TRACE("Read string %s\n",debugstr_an(str
,len
));
7629 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7630 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7632 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7633 CoTaskMemFree( str
);
7641 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7642 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7646 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7648 static const BYTE unknown1
[12] =
7649 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7650 0xFF, 0xFF, 0xFF, 0xFF};
7651 static const BYTE unknown2
[16] =
7652 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7653 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7655 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7656 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7657 debugstr_w(szProgIDName
));
7659 /* Create a CompObj stream */
7660 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7661 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7665 /* Write CompObj Structure to stream */
7666 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7668 if( SUCCEEDED( r
) )
7669 r
= WriteClassStm( pstm
, clsid
);
7671 if( SUCCEEDED( r
) )
7672 r
= STREAM_WriteString( pstm
, lpszUserType
);
7673 if( SUCCEEDED( r
) )
7674 r
= STREAM_WriteString( pstm
, szClipName
);
7675 if( SUCCEEDED( r
) )
7676 r
= STREAM_WriteString( pstm
, szProgIDName
);
7677 if( SUCCEEDED( r
) )
7678 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7680 IStream_Release( pstm
);
7685 /***********************************************************************
7686 * WriteFmtUserTypeStg (OLE32.@)
7688 HRESULT WINAPI
WriteFmtUserTypeStg(
7689 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7692 WCHAR szwClipName
[0x40];
7693 CLSID clsid
= CLSID_NULL
;
7694 LPWSTR wstrProgID
= NULL
;
7697 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7699 /* get the clipboard format name */
7700 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7703 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7705 /* FIXME: There's room to save a CLSID and its ProgID, but
7706 the CLSID is not looked up in the registry and in all the
7707 tests I wrote it was CLSID_NULL. Where does it come from?
7710 /* get the real program ID. This may fail, but that's fine */
7711 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7713 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7715 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7716 lpszUserType
, szwClipName
, wstrProgID
);
7718 CoTaskMemFree(wstrProgID
);
7724 /******************************************************************************
7725 * ReadFmtUserTypeStg [OLE32.@]
7727 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7731 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7732 unsigned char unknown1
[12];
7733 unsigned char unknown2
[16];
7735 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7738 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7740 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7741 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7744 WARN("Failed to open stream r = %08x\n", r
);
7748 /* read the various parts of the structure */
7749 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7750 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7752 r
= ReadClassStm( stm
, &clsid
);
7756 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7760 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7764 r
= STREAM_ReadString( stm
, &szProgIDName
);
7768 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7769 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7772 /* ok, success... now we just need to store what we found */
7774 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7775 CoTaskMemFree( szOleTypeName
);
7777 if( lplpszUserType
)
7778 *lplpszUserType
= szCLSIDName
;
7779 CoTaskMemFree( szProgIDName
);
7782 IStream_Release( stm
);
7788 /*************************************************************************
7789 * OLECONVERT_CreateCompObjStream [Internal]
7791 * Creates a "\001CompObj" is the destination IStorage if necessary.
7794 * pStorage [I] The dest IStorage to create the CompObj Stream
7796 * strOleTypeName [I] The ProgID
7800 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7803 * This function is used by OleConvertOLESTREAMToIStorage only.
7805 * The stream data is stored in the OLESTREAM and there should be
7806 * no need to recreate the stream. If the stream is manually
7807 * deleted it will attempt to create it by querying the registry.
7811 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7814 HRESULT hStorageRes
, hRes
= S_OK
;
7815 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7816 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7817 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7819 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7820 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7822 /* Initialize the CompObj structure */
7823 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7824 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7825 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7828 /* Create a CompObj stream if it doesn't exist */
7829 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7830 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7831 if(hStorageRes
== S_OK
)
7833 /* copy the OleTypeName to the compobj struct */
7834 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7835 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7837 /* copy the OleTypeName to the compobj struct */
7838 /* Note: in the test made, these were Identical */
7839 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7840 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7843 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7844 bufferW
, OLESTREAM_MAX_STR_LEN
);
7845 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7851 /* Get the CLSID Default Name from the Registry */
7852 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7853 if(hErr
== ERROR_SUCCESS
)
7855 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7856 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7857 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7858 if(hErr
== ERROR_SUCCESS
)
7860 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7866 /* Write CompObj Structure to stream */
7867 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7869 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7871 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7872 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7874 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7876 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7877 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7879 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7881 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7882 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7884 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7886 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7887 IStream_Release(pStream
);
7893 /*************************************************************************
7894 * OLECONVERT_CreateOlePresStream[Internal]
7896 * Creates the "\002OlePres000" Stream with the Metafile data
7899 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7900 * dwExtentX [I] Width of the Metafile
7901 * dwExtentY [I] Height of the Metafile
7902 * pData [I] Metafile data
7903 * dwDataLength [I] Size of the Metafile data
7907 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7910 * This function is used by OleConvertOLESTREAMToIStorage only.
7913 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7917 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7918 BYTE pOlePresStreamHeader
[] =
7920 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7921 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7922 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7923 0x00, 0x00, 0x00, 0x00
7926 BYTE pOlePresStreamHeaderEmpty
[] =
7928 0x00, 0x00, 0x00, 0x00,
7929 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7930 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7931 0x00, 0x00, 0x00, 0x00
7934 /* Create the OlePres000 Stream */
7935 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7936 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7941 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7943 memset(&OlePres
, 0, sizeof(OlePres
));
7944 /* Do we have any metafile data to save */
7945 if(dwDataLength
> 0)
7947 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7948 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7952 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7953 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7955 /* Set width and height of the metafile */
7956 OlePres
.dwExtentX
= dwExtentX
;
7957 OlePres
.dwExtentY
= -dwExtentY
;
7959 /* Set Data and Length */
7960 if(dwDataLength
> sizeof(METAFILEPICT16
))
7962 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7963 OlePres
.pData
= &(pData
[8]);
7965 /* Save OlePres000 Data to Stream */
7966 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7967 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7968 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7969 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7970 if(OlePres
.dwSize
> 0)
7972 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7974 IStream_Release(pStream
);
7978 /*************************************************************************
7979 * OLECONVERT_CreateOle10NativeStream [Internal]
7981 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7984 * pStorage [I] Dest storage to create the stream in
7985 * pData [I] Ole10 Native Data (ex. bmp)
7986 * dwDataLength [I] Size of the Ole10 Native Data
7992 * This function is used by OleConvertOLESTREAMToIStorage only.
7994 * Might need to verify the data and return appropriate error message
7997 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8001 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8003 /* Create the Ole10Native Stream */
8004 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8005 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8009 /* Write info to stream */
8010 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8011 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8012 IStream_Release(pStream
);
8017 /*************************************************************************
8018 * OLECONVERT_GetOLE10ProgID [Internal]
8020 * Finds the ProgID (or OleTypeID) from the IStorage
8023 * pStorage [I] The Src IStorage to get the ProgID
8024 * strProgID [I] the ProgID string to get
8025 * dwSize [I] the size of the string
8029 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8032 * This function is used by OleConvertIStorageToOLESTREAM only.
8036 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8040 LARGE_INTEGER iSeekPos
;
8041 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8042 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8044 /* Open the CompObj Stream */
8045 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8046 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8050 /*Get the OleType from the CompObj Stream */
8051 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8052 iSeekPos
.u
.HighPart
= 0;
8054 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8055 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8056 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8057 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8058 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8059 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8060 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8062 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8065 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8067 IStream_Release(pStream
);
8072 LPOLESTR wstrProgID
;
8074 /* Get the OleType from the registry */
8075 REFCLSID clsid
= &(stat
.clsid
);
8076 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8077 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8080 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8087 /*************************************************************************
8088 * OLECONVERT_GetOle10PresData [Internal]
8090 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8093 * pStorage [I] Src IStroage
8094 * pOleStream [I] Dest OleStream Mem Struct
8100 * This function is used by OleConvertIStorageToOLESTREAM only.
8102 * Memory allocated for pData must be freed by the caller
8106 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8111 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8113 /* Initialize Default data for OLESTREAM */
8114 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8115 pOleStreamData
[0].dwTypeID
= 2;
8116 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8117 pOleStreamData
[1].dwTypeID
= 0;
8118 pOleStreamData
[0].dwMetaFileWidth
= 0;
8119 pOleStreamData
[0].dwMetaFileHeight
= 0;
8120 pOleStreamData
[0].pData
= NULL
;
8121 pOleStreamData
[1].pData
= NULL
;
8123 /* Open Ole10Native Stream */
8124 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8125 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8129 /* Read Size and Data */
8130 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8131 if(pOleStreamData
->dwDataLength
> 0)
8133 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8134 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8136 IStream_Release(pStream
);
8142 /*************************************************************************
8143 * OLECONVERT_GetOle20PresData[Internal]
8145 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8148 * pStorage [I] Src IStroage
8149 * pOleStreamData [I] Dest OleStream Mem Struct
8155 * This function is used by OleConvertIStorageToOLESTREAM only.
8157 * Memory allocated for pData must be freed by the caller
8159 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8163 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8164 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8166 /* Initialize Default data for OLESTREAM */
8167 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8168 pOleStreamData
[0].dwTypeID
= 2;
8169 pOleStreamData
[0].dwMetaFileWidth
= 0;
8170 pOleStreamData
[0].dwMetaFileHeight
= 0;
8171 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8172 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8173 pOleStreamData
[1].dwTypeID
= 0;
8174 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8175 pOleStreamData
[1].strOleTypeName
[0] = 0;
8176 pOleStreamData
[1].dwMetaFileWidth
= 0;
8177 pOleStreamData
[1].dwMetaFileHeight
= 0;
8178 pOleStreamData
[1].pData
= NULL
;
8179 pOleStreamData
[1].dwDataLength
= 0;
8182 /* Open OlePress000 stream */
8183 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8184 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8187 LARGE_INTEGER iSeekPos
;
8188 METAFILEPICT16 MetaFilePict
;
8189 static const char strMetafilePictName
[] = "METAFILEPICT";
8191 /* Set the TypeID for a Metafile */
8192 pOleStreamData
[1].dwTypeID
= 5;
8194 /* Set the OleTypeName to Metafile */
8195 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8196 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8198 iSeekPos
.u
.HighPart
= 0;
8199 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8201 /* Get Presentation Data */
8202 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8203 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8204 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8205 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8207 /*Set width and Height */
8208 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8209 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8210 if(olePress
.dwSize
> 0)
8213 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8215 /* Set MetaFilePict struct */
8216 MetaFilePict
.mm
= 8;
8217 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8218 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8219 MetaFilePict
.hMF
= 0;
8221 /* Get Metafile Data */
8222 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8223 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8224 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8226 IStream_Release(pStream
);
8230 /*************************************************************************
8231 * OleConvertOLESTREAMToIStorage [OLE32.@]
8236 * DVTARGETDEVICE parameter is not handled
8237 * Still unsure of some mem fields for OLE 10 Stream
8238 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8239 * and "\001OLE" streams
8242 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8243 LPOLESTREAM pOleStream
,
8245 const DVTARGETDEVICE
* ptd
)
8249 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8251 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8253 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8257 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8260 if(pstg
== NULL
|| pOleStream
== NULL
)
8262 hRes
= E_INVALIDARG
;
8267 /* Load the OLESTREAM to Memory */
8268 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8273 /* Load the OLESTREAM to Memory (part 2)*/
8274 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8280 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8282 /* Do we have the IStorage Data in the OLESTREAM */
8283 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8285 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8286 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8290 /* It must be an original OLE 1.0 source */
8291 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8296 /* It must be an original OLE 1.0 source */
8297 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8300 /* Create CompObj Stream if necessary */
8301 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8304 /*Create the Ole Stream if necessary */
8305 OLECONVERT_CreateOleStream(pstg
);
8310 /* Free allocated memory */
8311 for(i
=0; i
< 2; i
++)
8313 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8314 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8315 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8320 /*************************************************************************
8321 * OleConvertIStorageToOLESTREAM [OLE32.@]
8328 * Still unsure of some mem fields for OLE 10 Stream
8329 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8330 * and "\001OLE" streams.
8333 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8335 LPOLESTREAM pOleStream
)
8338 HRESULT hRes
= S_OK
;
8340 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8341 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8343 TRACE("%p %p\n", pstg
, pOleStream
);
8345 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8347 if(pstg
== NULL
|| pOleStream
== NULL
)
8349 hRes
= E_INVALIDARG
;
8353 /* Get the ProgID */
8354 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8355 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8359 /* Was it originally Ole10 */
8360 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8363 IStream_Release(pStream
);
8364 /* Get Presentation Data for Ole10Native */
8365 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8369 /* Get Presentation Data (OLE20) */
8370 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8373 /* Save OLESTREAM */
8374 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8377 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8382 /* Free allocated memory */
8383 for(i
=0; i
< 2; i
++)
8385 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8391 /***********************************************************************
8392 * GetConvertStg (OLE32.@)
8394 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8395 FIXME("unimplemented stub!\n");
8399 /******************************************************************************
8400 * StgIsStorageFile [OLE32.@]
8401 * Verify if the file contains a storage object
8407 * S_OK if file has magic bytes as a storage object
8408 * S_FALSE if file is not storage
8411 StgIsStorageFile(LPCOLESTR fn
)
8417 TRACE("%s\n", debugstr_w(fn
));
8418 hf
= CreateFileW(fn
, GENERIC_READ
,
8419 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8420 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8422 if (hf
== INVALID_HANDLE_VALUE
)
8423 return STG_E_FILENOTFOUND
;
8425 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8427 WARN(" unable to read file\n");
8434 if (bytes_read
!= 8) {
8435 WARN(" too short\n");
8439 if (!memcmp(magic
,STORAGE_magic
,8)) {
8444 WARN(" -> Invalid header.\n");
8448 /***********************************************************************
8449 * WriteClassStm (OLE32.@)
8451 * Writes a CLSID to a stream.
8454 * pStm [I] Stream to write to.
8455 * rclsid [I] CLSID to write.
8459 * Failure: HRESULT code.
8461 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8463 TRACE("(%p,%p)\n",pStm
,rclsid
);
8465 if (!pStm
|| !rclsid
)
8466 return E_INVALIDARG
;
8468 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8471 /***********************************************************************
8472 * ReadClassStm (OLE32.@)
8474 * Reads a CLSID from a stream.
8477 * pStm [I] Stream to read from.
8478 * rclsid [O] CLSID to read.
8482 * Failure: HRESULT code.
8484 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8489 TRACE("(%p,%p)\n",pStm
,pclsid
);
8491 if (!pStm
|| !pclsid
)
8492 return E_INVALIDARG
;
8494 /* clear the output args */
8495 *pclsid
= CLSID_NULL
;
8497 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8502 if (nbByte
!= sizeof(CLSID
))
8503 return STG_E_READFAULT
;