2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry
;
86 StorageBaseImpl
*parentStorage
;
88 typedef struct StorageInternalImpl StorageInternalImpl
;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
92 DWORD openFlags
, DirRef storageDirEntry
);
93 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
94 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
95 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
96 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
98 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
99 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
101 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
102 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
103 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
104 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
107 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
108 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
109 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
111 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
112 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
113 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
114 ULONG blockIndex
, ULONG offset
, DWORD value
);
115 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
116 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
118 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
119 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base
;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl
*snapshot
;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl
*transactedParent
;
137 } TransactedSnapshotImpl
;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, StorageBaseImpl
** result
);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength
;
149 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
150 CHAR
*pstrOleObjFileName
;
151 DWORD dwOleObjFileNameLength
;
152 DWORD dwMetaFileWidth
;
153 DWORD dwMetaFileHeight
;
154 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA
;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength
;
166 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
167 DWORD dwOleTypeNameLength
;
168 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
169 DWORD dwProgIDNameLength
;
170 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
172 }OLECONVERT_ISTORAGE_COMPOBJ
;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES
;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT
deleteStorageContents(
192 StorageBaseImpl
*parentStorage
,
193 DirRef indexToDelete
,
194 DirEntry entryDataToDelete
);
196 static HRESULT
deleteStreamContents(
197 StorageBaseImpl
*parentStorage
,
198 DirRef indexToDelete
,
199 DirEntry entryDataToDelete
);
201 static HRESULT
removeFromTree(
202 StorageBaseImpl
*This
,
203 DirRef parentStorageIndex
,
204 DirRef deletedIndex
);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT
insertIntoTree(
211 StorageBaseImpl
*This
,
212 DirRef parentStorageIndex
,
213 DirRef newEntryIndex
);
215 static LONG
entryNameCmp(
216 const OLECHAR
*name1
,
217 const OLECHAR
*name2
);
219 static DirRef
findElement(
220 StorageBaseImpl
*storage
,
225 static HRESULT
findTreeParent(
226 StorageBaseImpl
*storage
,
228 const OLECHAR
*childName
,
229 DirEntry
*parentData
,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT
validateSTGM(DWORD stgmValue
);
238 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
239 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
240 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref
; /* Reference count */
258 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
259 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
261 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
265 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
268 /************************************************************************
272 static ULONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
274 return (index
+1) * This
->bigBlockSize
;
277 /************************************************************************
278 ** Storage32BaseImpl implementation
280 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
281 ULARGE_INTEGER offset
,
286 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
289 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
290 ULARGE_INTEGER offset
,
295 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
298 /************************************************************************
299 * Storage32BaseImpl_QueryInterface (IUnknown)
301 * This method implements the common QueryInterface for all IStorage32
302 * implementations contained in this file.
304 * See Windows documentation for more details on IUnknown methods.
306 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
311 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
313 if ( (This
==0) || (ppvObject
==0) )
318 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
319 IsEqualGUID(&IID_IStorage
, riid
))
323 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
325 *ppvObject
= &This
->pssVtbl
;
329 return E_NOINTERFACE
;
331 IStorage_AddRef(iface
);
336 /************************************************************************
337 * Storage32BaseImpl_AddRef (IUnknown)
339 * This method implements the common AddRef for all IStorage32
340 * implementations contained in this file.
342 * See Windows documentation for more details on IUnknown methods.
344 static ULONG WINAPI
StorageBaseImpl_AddRef(
347 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
348 ULONG ref
= InterlockedIncrement(&This
->ref
);
350 TRACE("(%p) AddRef to %d\n", This
, ref
);
355 /************************************************************************
356 * Storage32BaseImpl_Release (IUnknown)
358 * This method implements the common Release for all IStorage32
359 * implementations contained in this file.
361 * See Windows documentation for more details on IUnknown methods.
363 static ULONG WINAPI
StorageBaseImpl_Release(
366 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
368 ULONG ref
= InterlockedDecrement(&This
->ref
);
370 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
375 * Since we are using a system of base-classes, we want to call the
376 * destructor of the appropriate derived class. To do this, we are
377 * using virtual functions to implement the destructor.
379 StorageBaseImpl_Destroy(This
);
385 /************************************************************************
386 * Storage32BaseImpl_OpenStream (IStorage)
388 * This method will open the specified stream object from the current storage.
390 * See Windows documentation for more details on IStorage methods.
392 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
394 const OLECHAR
* pwcsName
, /* [string][in] */
395 void* reserved1
, /* [unique][in] */
396 DWORD grfMode
, /* [in] */
397 DWORD reserved2
, /* [in] */
398 IStream
** ppstm
) /* [out] */
400 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
401 StgStreamImpl
* newStream
;
402 DirEntry currentEntry
;
403 DirRef streamEntryRef
;
404 HRESULT res
= STG_E_UNKNOWN
;
406 TRACE("(%p, %s, %p, %x, %d, %p)\n",
407 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
409 if ( (pwcsName
==NULL
) || (ppstm
==0) )
417 if ( FAILED( validateSTGM(grfMode
) ) ||
418 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
420 res
= STG_E_INVALIDFLAG
;
427 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
429 res
= STG_E_INVALIDFUNCTION
;
435 res
= STG_E_REVERTED
;
440 * Check that we're compatible with the parent's storage mode, but
441 * only if we are not in transacted mode
443 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
444 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
446 res
= STG_E_INVALIDFLAG
;
452 * Search for the element with the given name
454 streamEntryRef
= findElement(
456 This
->storageDirEntry
,
461 * If it was found, construct the stream object and return a pointer to it.
463 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
464 (currentEntry
.stgType
==STGTY_STREAM
) )
466 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
468 /* A single stream cannot be opened a second time. */
469 res
= STG_E_ACCESSDENIED
;
473 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
477 newStream
->grfMode
= grfMode
;
478 *ppstm
= (IStream
*)newStream
;
480 IStream_AddRef(*ppstm
);
490 res
= STG_E_FILENOTFOUND
;
494 TRACE("<-- IStream %p\n", *ppstm
);
495 TRACE("<-- %08x\n", res
);
499 /************************************************************************
500 * Storage32BaseImpl_OpenStorage (IStorage)
502 * This method will open a new storage object from the current storage.
504 * See Windows documentation for more details on IStorage methods.
506 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
508 const OLECHAR
* pwcsName
, /* [string][unique][in] */
509 IStorage
* pstgPriority
, /* [unique][in] */
510 DWORD grfMode
, /* [in] */
511 SNB snbExclude
, /* [unique][in] */
512 DWORD reserved
, /* [in] */
513 IStorage
** ppstg
) /* [out] */
515 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
516 StorageInternalImpl
* newStorage
;
517 StorageBaseImpl
* newTransactedStorage
;
518 DirEntry currentEntry
;
519 DirRef storageEntryRef
;
520 HRESULT res
= STG_E_UNKNOWN
;
522 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
523 iface
, debugstr_w(pwcsName
), pstgPriority
,
524 grfMode
, snbExclude
, reserved
, ppstg
);
526 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
532 if (This
->openFlags
& STGM_SIMPLE
)
534 res
= STG_E_INVALIDFUNCTION
;
539 if (snbExclude
!= NULL
)
541 res
= STG_E_INVALIDPARAMETER
;
545 if ( FAILED( validateSTGM(grfMode
) ))
547 res
= STG_E_INVALIDFLAG
;
554 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
555 (grfMode
& STGM_DELETEONRELEASE
) ||
556 (grfMode
& STGM_PRIORITY
) )
558 res
= STG_E_INVALIDFUNCTION
;
563 return STG_E_REVERTED
;
566 * Check that we're compatible with the parent's storage mode,
567 * but only if we are not transacted
569 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
570 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
572 res
= STG_E_ACCESSDENIED
;
579 storageEntryRef
= findElement(
581 This
->storageDirEntry
,
585 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
586 (currentEntry
.stgType
==STGTY_STORAGE
) )
588 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
590 /* A single storage cannot be opened a second time. */
591 res
= STG_E_ACCESSDENIED
;
595 newStorage
= StorageInternalImpl_Construct(
602 if (grfMode
& STGM_TRANSACTED
)
604 res
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
608 HeapFree(GetProcessHeap(), 0, newStorage
);
612 *ppstg
= (IStorage
*)newTransactedStorage
;
616 *ppstg
= (IStorage
*)newStorage
;
619 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
625 res
= STG_E_INSUFFICIENTMEMORY
;
629 res
= STG_E_FILENOTFOUND
;
632 TRACE("<-- %08x\n", res
);
636 /************************************************************************
637 * Storage32BaseImpl_EnumElements (IStorage)
639 * This method will create an enumerator object that can be used to
640 * retrieve information about all the elements in the storage object.
642 * See Windows documentation for more details on IStorage methods.
644 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
646 DWORD reserved1
, /* [in] */
647 void* reserved2
, /* [size_is][unique][in] */
648 DWORD reserved3
, /* [in] */
649 IEnumSTATSTG
** ppenum
) /* [out] */
651 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
652 IEnumSTATSTGImpl
* newEnum
;
654 TRACE("(%p, %d, %p, %d, %p)\n",
655 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
657 if ( (This
==0) || (ppenum
==0))
661 return STG_E_REVERTED
;
663 newEnum
= IEnumSTATSTGImpl_Construct(
665 This
->storageDirEntry
);
669 *ppenum
= (IEnumSTATSTG
*)newEnum
;
671 IEnumSTATSTG_AddRef(*ppenum
);
676 return E_OUTOFMEMORY
;
679 /************************************************************************
680 * Storage32BaseImpl_Stat (IStorage)
682 * This method will retrieve information about this storage object.
684 * See Windows documentation for more details on IStorage methods.
686 static HRESULT WINAPI
StorageBaseImpl_Stat(
688 STATSTG
* pstatstg
, /* [out] */
689 DWORD grfStatFlag
) /* [in] */
691 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
692 DirEntry currentEntry
;
693 HRESULT res
= STG_E_UNKNOWN
;
695 TRACE("(%p, %p, %x)\n",
696 iface
, pstatstg
, grfStatFlag
);
698 if ( (This
==0) || (pstatstg
==0))
706 res
= STG_E_REVERTED
;
710 res
= StorageBaseImpl_ReadDirEntry(
712 This
->storageDirEntry
,
717 StorageUtl_CopyDirEntryToSTATSTG(
723 pstatstg
->grfMode
= This
->openFlags
;
724 pstatstg
->grfStateBits
= This
->stateBits
;
730 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
732 TRACE("<-- %08x\n", res
);
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
745 const OLECHAR
* pwcsOldName
, /* [in] */
746 const OLECHAR
* pwcsNewName
) /* [in] */
748 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
749 DirEntry currentEntry
;
750 DirRef currentEntryRef
;
752 TRACE("(%p, %s, %s)\n",
753 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
756 return STG_E_REVERTED
;
758 currentEntryRef
= findElement(This
,
759 This
->storageDirEntry
,
763 if (currentEntryRef
!= DIRENTRY_NULL
)
766 * There is already an element with the new name
768 return STG_E_FILEALREADYEXISTS
;
772 * Search for the old element name
774 currentEntryRef
= findElement(This
,
775 This
->storageDirEntry
,
779 if (currentEntryRef
!= DIRENTRY_NULL
)
781 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
782 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
784 WARN("Element is already open; cannot rename.\n");
785 return STG_E_ACCESSDENIED
;
788 /* Remove the element from its current position in the tree */
789 removeFromTree(This
, This
->storageDirEntry
,
792 /* Change the name of the element */
793 strcpyW(currentEntry
.name
, pwcsNewName
);
795 /* Delete any sibling links */
796 currentEntry
.leftChild
= DIRENTRY_NULL
;
797 currentEntry
.rightChild
= DIRENTRY_NULL
;
799 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
802 /* Insert the element in a new position in the tree */
803 insertIntoTree(This
, This
->storageDirEntry
,
809 * There is no element with the old name
811 return STG_E_FILENOTFOUND
;
817 /************************************************************************
818 * Storage32BaseImpl_CreateStream (IStorage)
820 * This method will create a stream object within this storage
822 * See Windows documentation for more details on IStorage methods.
824 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
826 const OLECHAR
* pwcsName
, /* [string][in] */
827 DWORD grfMode
, /* [in] */
828 DWORD reserved1
, /* [in] */
829 DWORD reserved2
, /* [in] */
830 IStream
** ppstm
) /* [out] */
832 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
833 StgStreamImpl
* newStream
;
834 DirEntry currentEntry
, newStreamEntry
;
835 DirRef currentEntryRef
, newStreamEntryRef
;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface
, debugstr_w(pwcsName
), grfMode
,
840 reserved1
, reserved2
, ppstm
);
843 return STG_E_INVALIDPOINTER
;
846 return STG_E_INVALIDNAME
;
848 if (reserved1
|| reserved2
)
849 return STG_E_INVALIDPARAMETER
;
851 if ( FAILED( validateSTGM(grfMode
) ))
852 return STG_E_INVALIDFLAG
;
854 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
855 return STG_E_INVALIDFLAG
;
858 return STG_E_REVERTED
;
863 if ((grfMode
& STGM_DELETEONRELEASE
) ||
864 (grfMode
& STGM_TRANSACTED
))
865 return STG_E_INVALIDFUNCTION
;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
874 return STG_E_ACCESSDENIED
;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
878 return STG_E_ACCESSDENIED
;
881 if(This
->openFlags
& STGM_SIMPLE
)
882 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
886 currentEntryRef
= findElement(This
,
887 This
->storageDirEntry
,
891 if (currentEntryRef
!= DIRENTRY_NULL
)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
898 IStorage_DestroyElement(iface
, pwcsName
);
901 return STG_E_FILEALREADYEXISTS
;
905 * memset the empty entry
907 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
909 newStreamEntry
.sizeOfNameString
=
910 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
912 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
913 return STG_E_INVALIDNAME
;
915 strcpyW(newStreamEntry
.name
, pwcsName
);
917 newStreamEntry
.stgType
= STGTY_STREAM
;
918 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
919 newStreamEntry
.size
.u
.LowPart
= 0;
920 newStreamEntry
.size
.u
.HighPart
= 0;
922 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
923 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
924 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
926 /* call CoFileTime to get the current time
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
941 * Insert the new entry in the parent storage's tree.
945 This
->storageDirEntry
,
949 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
954 * Open the stream to return it.
956 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
960 *ppstm
= (IStream
*)newStream
;
962 IStream_AddRef(*ppstm
);
966 return STG_E_INSUFFICIENTMEMORY
;
972 /************************************************************************
973 * Storage32BaseImpl_SetClass (IStorage)
975 * This method will write the specified CLSID in the directory entry of this
978 * See Windows documentation for more details on IStorage methods.
980 static HRESULT WINAPI
StorageBaseImpl_SetClass(
982 REFCLSID clsid
) /* [in] */
984 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
986 DirEntry currentEntry
;
988 TRACE("(%p, %p)\n", iface
, clsid
);
991 return STG_E_REVERTED
;
993 hRes
= StorageBaseImpl_ReadDirEntry(This
,
994 This
->storageDirEntry
,
998 currentEntry
.clsid
= *clsid
;
1000 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1001 This
->storageDirEntry
,
1008 /************************************************************************
1009 ** Storage32Impl implementation
1012 /************************************************************************
1013 * Storage32BaseImpl_CreateStorage (IStorage)
1015 * This method will create the storage object within the provided storage.
1017 * See Windows documentation for more details on IStorage methods.
1019 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1021 const OLECHAR
*pwcsName
, /* [string][in] */
1022 DWORD grfMode
, /* [in] */
1023 DWORD reserved1
, /* [in] */
1024 DWORD reserved2
, /* [in] */
1025 IStorage
**ppstg
) /* [out] */
1027 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1029 DirEntry currentEntry
;
1031 DirRef currentEntryRef
;
1035 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1036 iface
, debugstr_w(pwcsName
), grfMode
,
1037 reserved1
, reserved2
, ppstg
);
1040 return STG_E_INVALIDPOINTER
;
1042 if (This
->openFlags
& STGM_SIMPLE
)
1044 return STG_E_INVALIDFUNCTION
;
1048 return STG_E_INVALIDNAME
;
1052 if ( FAILED( validateSTGM(grfMode
) ) ||
1053 (grfMode
& STGM_DELETEONRELEASE
) )
1055 WARN("bad grfMode: 0x%x\n", grfMode
);
1056 return STG_E_INVALIDFLAG
;
1060 return STG_E_REVERTED
;
1063 * Check that we're compatible with the parent's storage mode
1065 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1066 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1068 WARN("access denied\n");
1069 return STG_E_ACCESSDENIED
;
1072 currentEntryRef
= findElement(This
,
1073 This
->storageDirEntry
,
1077 if (currentEntryRef
!= DIRENTRY_NULL
)
1080 * An element with this name already exists
1082 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1083 ((This
->openFlags
& STGM_TRANSACTED
) ||
1084 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1086 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1092 WARN("file already exists\n");
1093 return STG_E_FILEALREADYEXISTS
;
1096 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1097 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1099 WARN("read-only storage\n");
1100 return STG_E_ACCESSDENIED
;
1103 memset(&newEntry
, 0, sizeof(DirEntry
));
1105 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1107 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1109 FIXME("name too long\n");
1110 return STG_E_INVALIDNAME
;
1113 strcpyW(newEntry
.name
, pwcsName
);
1115 newEntry
.stgType
= STGTY_STORAGE
;
1116 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1117 newEntry
.size
.u
.LowPart
= 0;
1118 newEntry
.size
.u
.HighPart
= 0;
1120 newEntry
.leftChild
= DIRENTRY_NULL
;
1121 newEntry
.rightChild
= DIRENTRY_NULL
;
1122 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1124 /* call CoFileTime to get the current time
1129 /* newEntry.clsid */
1132 * Create a new directory entry for the storage
1134 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1139 * Insert the new directory entry into the parent storage's tree
1141 hr
= insertIntoTree(
1143 This
->storageDirEntry
,
1147 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1152 * Open it to get a pointer to return.
1154 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1156 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1166 /***************************************************************************
1170 * Reserve a directory entry in the file and initialize it.
1172 static HRESULT
StorageImpl_CreateDirEntry(
1173 StorageBaseImpl
*base
,
1174 const DirEntry
*newData
,
1177 StorageImpl
*storage
= (StorageImpl
*)base
;
1178 ULONG currentEntryIndex
= 0;
1179 ULONG newEntryIndex
= DIRENTRY_NULL
;
1181 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1182 WORD sizeOfNameString
;
1186 hr
= StorageImpl_ReadRawDirEntry(storage
,
1192 StorageUtl_ReadWord(
1194 OFFSET_PS_NAMELENGTH
,
1197 if (sizeOfNameString
== 0)
1200 * The entry exists and is available, we found it.
1202 newEntryIndex
= currentEntryIndex
;
1208 * We exhausted the directory entries, we will create more space below
1210 newEntryIndex
= currentEntryIndex
;
1212 currentEntryIndex
++;
1214 } while (newEntryIndex
== DIRENTRY_NULL
);
1217 * grow the directory stream
1221 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1222 ULARGE_INTEGER newSize
;
1224 ULONG lastEntry
= 0;
1225 ULONG blockCount
= 0;
1228 * obtain the new count of blocks in the directory stream
1230 blockCount
= BlockChainStream_GetCount(
1231 storage
->rootBlockChain
)+1;
1234 * initialize the size used by the directory stream
1236 newSize
.u
.HighPart
= 0;
1237 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1240 * add a block to the directory stream
1242 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1245 * memset the empty entry in order to initialize the unused newly
1248 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1253 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1256 entryIndex
= newEntryIndex
+ 1;
1257 entryIndex
< lastEntry
;
1260 StorageImpl_WriteRawDirEntry(
1267 UpdateRawDirEntry(currentData
, newData
);
1269 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1272 *index
= newEntryIndex
;
1277 /***************************************************************************
1281 * Mark a directory entry in the file as free.
1283 static HRESULT
StorageImpl_DestroyDirEntry(
1284 StorageBaseImpl
*base
,
1288 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1289 StorageImpl
*storage
= (StorageImpl
*)base
;
1291 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1293 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1299 /***************************************************************************
1303 * Destroy an entry, its attached data, and all entries reachable from it.
1305 static HRESULT
DestroyReachableEntries(
1306 StorageBaseImpl
*base
,
1311 ULARGE_INTEGER zero
;
1315 if (index
!= DIRENTRY_NULL
)
1317 hr
= StorageBaseImpl_ReadDirEntry(base
, index
, &data
);
1320 hr
= DestroyReachableEntries(base
, data
.dirRootEntry
);
1323 hr
= DestroyReachableEntries(base
, data
.leftChild
);
1326 hr
= DestroyReachableEntries(base
, data
.rightChild
);
1329 hr
= StorageBaseImpl_StreamSetSize(base
, index
, zero
);
1332 hr
= StorageBaseImpl_DestroyDirEntry(base
, index
);
1339 /****************************************************************************
1343 * Case insensitive comparison of DirEntry.name by first considering
1346 * Returns <0 when name1 < name2
1347 * >0 when name1 > name2
1348 * 0 when name1 == name2
1350 static LONG
entryNameCmp(
1351 const OLECHAR
*name1
,
1352 const OLECHAR
*name2
)
1354 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1356 while (diff
== 0 && *name1
!= 0)
1359 * We compare the string themselves only when they are of the same length
1361 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1367 /****************************************************************************
1371 * Add a directory entry to a storage
1373 static HRESULT
insertIntoTree(
1374 StorageBaseImpl
*This
,
1375 DirRef parentStorageIndex
,
1376 DirRef newEntryIndex
)
1378 DirEntry currentEntry
;
1382 * Read the inserted entry
1384 StorageBaseImpl_ReadDirEntry(This
,
1389 * Read the storage entry
1391 StorageBaseImpl_ReadDirEntry(This
,
1395 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1398 * The root storage contains some element, therefore, start the research
1399 * for the appropriate location.
1402 DirRef current
, next
, previous
, currentEntryId
;
1405 * Keep a reference to the root of the storage's element tree
1407 currentEntryId
= currentEntry
.dirRootEntry
;
1412 StorageBaseImpl_ReadDirEntry(This
,
1413 currentEntry
.dirRootEntry
,
1416 previous
= currentEntry
.leftChild
;
1417 next
= currentEntry
.rightChild
;
1418 current
= currentEntryId
;
1422 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1426 if (previous
!= DIRENTRY_NULL
)
1428 StorageBaseImpl_ReadDirEntry(This
,
1435 currentEntry
.leftChild
= newEntryIndex
;
1436 StorageBaseImpl_WriteDirEntry(This
,
1444 if (next
!= DIRENTRY_NULL
)
1446 StorageBaseImpl_ReadDirEntry(This
,
1453 currentEntry
.rightChild
= newEntryIndex
;
1454 StorageBaseImpl_WriteDirEntry(This
,
1463 * Trying to insert an item with the same name in the
1464 * subtree structure.
1466 return STG_E_FILEALREADYEXISTS
;
1469 previous
= currentEntry
.leftChild
;
1470 next
= currentEntry
.rightChild
;
1476 * The storage is empty, make the new entry the root of its element tree
1478 currentEntry
.dirRootEntry
= newEntryIndex
;
1479 StorageBaseImpl_WriteDirEntry(This
,
1487 /****************************************************************************
1491 * Find and read the element of a storage with the given name.
1493 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1494 const OLECHAR
*name
, DirEntry
*data
)
1496 DirRef currentEntry
;
1498 /* Read the storage entry to find the root of the tree. */
1499 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1501 currentEntry
= data
->dirRootEntry
;
1503 while (currentEntry
!= DIRENTRY_NULL
)
1507 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1509 cmp
= entryNameCmp(name
, data
->name
);
1516 currentEntry
= data
->leftChild
;
1519 currentEntry
= data
->rightChild
;
1522 return currentEntry
;
1525 /****************************************************************************
1529 * Find and read the binary tree parent of the element with the given name.
1531 * If there is no such element, find a place where it could be inserted and
1532 * return STG_E_FILENOTFOUND.
1534 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1535 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1541 /* Read the storage entry to find the root of the tree. */
1542 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1544 *parentEntry
= storageEntry
;
1545 *relation
= DIRENTRY_RELATION_DIR
;
1547 childEntry
= parentData
->dirRootEntry
;
1549 while (childEntry
!= DIRENTRY_NULL
)
1553 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1555 cmp
= entryNameCmp(childName
, childData
.name
);
1563 *parentData
= childData
;
1564 *parentEntry
= childEntry
;
1565 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1567 childEntry
= parentData
->leftChild
;
1572 *parentData
= childData
;
1573 *parentEntry
= childEntry
;
1574 *relation
= DIRENTRY_RELATION_NEXT
;
1576 childEntry
= parentData
->rightChild
;
1580 if (childEntry
== DIRENTRY_NULL
)
1581 return STG_E_FILENOTFOUND
;
1587 /*************************************************************************
1590 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1592 DWORD ciidExclude
, /* [in] */
1593 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1594 SNB snbExclude
, /* [unique][in] */
1595 IStorage
* pstgDest
) /* [unique][in] */
1597 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1599 IEnumSTATSTG
*elements
= 0;
1600 STATSTG curElement
, strStat
;
1602 IStorage
*pstgTmp
, *pstgChild
;
1603 IStream
*pstrTmp
, *pstrChild
;
1606 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1609 TRACE("(%p, %d, %p, %p, %p)\n",
1610 iface
, ciidExclude
, rgiidExclude
,
1611 snbExclude
, pstgDest
);
1613 if ( pstgDest
== 0 )
1614 return STG_E_INVALIDPOINTER
;
1617 * Enumerate the elements
1619 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1627 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1628 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1630 for(i
= 0; i
< ciidExclude
; ++i
)
1632 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1633 skip_storage
= TRUE
;
1634 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1637 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1643 * Obtain the next element
1645 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1647 if ( hr
== S_FALSE
)
1649 hr
= S_OK
; /* done, every element has been copied */
1655 WCHAR
**snb
= snbExclude
;
1657 while ( *snb
!= NULL
&& !skip
)
1659 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1668 if (curElement
.type
== STGTY_STORAGE
)
1674 * open child source storage
1676 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1677 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1678 NULL
, 0, &pstgChild
);
1684 * create a new storage in destination storage
1686 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1687 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1691 * if it already exist, don't create a new one use this one
1693 if (hr
== STG_E_FILEALREADYEXISTS
)
1695 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1696 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1697 NULL
, 0, &pstgTmp
);
1703 * do the copy recursively
1705 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1708 IStorage_Release( pstgTmp
);
1711 IStorage_Release( pstgChild
);
1713 else if (curElement
.type
== STGTY_STREAM
)
1719 * create a new stream in destination storage. If the stream already
1720 * exist, it will be deleted and a new one will be created.
1722 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1723 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1730 * open child stream storage. This operation must succeed even if the
1731 * stream is already open, so we use internal functions to do it.
1733 srcEntryRef
= findElement( This
, This
->storageDirEntry
, curElement
.pwcsName
,
1737 ERR("source stream not found\n");
1738 hr
= STG_E_DOCFILECORRUPT
;
1743 pstrChild
= (IStream
*)StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntryRef
);
1745 IStream_AddRef(pstrChild
);
1753 * Get the size of the source stream
1755 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1758 * Set the size of the destination stream.
1760 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1765 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1768 IStream_Release( pstrChild
);
1771 IStream_Release( pstrTmp
);
1775 WARN("unknown element type: %d\n", curElement
.type
);
1779 CoTaskMemFree(curElement
.pwcsName
);
1780 } while (hr
== S_OK
);
1785 IEnumSTATSTG_Release(elements
);
1790 /*************************************************************************
1791 * MoveElementTo (IStorage)
1793 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1795 const OLECHAR
*pwcsName
, /* [string][in] */
1796 IStorage
*pstgDest
, /* [unique][in] */
1797 const OLECHAR
*pwcsNewName
,/* [string][in] */
1798 DWORD grfFlags
) /* [in] */
1800 FIXME("(%p %s %p %s %u): stub\n", iface
,
1801 debugstr_w(pwcsName
), pstgDest
,
1802 debugstr_w(pwcsNewName
), grfFlags
);
1806 /*************************************************************************
1809 * Ensures that any changes made to a storage object open in transacted mode
1810 * are reflected in the parent storage
1813 * Wine doesn't implement transacted mode, which seems to be a basic
1814 * optimization, so we can ignore this stub for now.
1816 static HRESULT WINAPI
StorageImpl_Commit(
1818 DWORD grfCommitFlags
)/* [in] */
1820 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1824 /*************************************************************************
1827 * Discard all changes that have been made since the last commit operation
1829 static HRESULT WINAPI
StorageImpl_Revert(
1832 TRACE("(%p)\n", iface
);
1836 /*************************************************************************
1837 * DestroyElement (IStorage)
1839 * Strategy: This implementation is built this way for simplicity not for speed.
1840 * I always delete the topmost element of the enumeration and adjust
1841 * the deleted element pointer all the time. This takes longer to
1842 * do but allow to reinvoke DestroyElement whenever we encounter a
1843 * storage object. The optimisation resides in the usage of another
1844 * enumeration strategy that would give all the leaves of a storage
1845 * first. (postfix order)
1847 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1849 const OLECHAR
*pwcsName
)/* [string][in] */
1851 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1854 DirEntry entryToDelete
;
1855 DirRef entryToDeleteRef
;
1858 iface
, debugstr_w(pwcsName
));
1861 return STG_E_INVALIDPOINTER
;
1864 return STG_E_REVERTED
;
1866 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1867 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1868 return STG_E_ACCESSDENIED
;
1870 entryToDeleteRef
= findElement(
1872 This
->storageDirEntry
,
1876 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1878 return STG_E_FILENOTFOUND
;
1881 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1883 hr
= deleteStorageContents(
1888 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1890 hr
= deleteStreamContents(
1900 * Remove the entry from its parent storage
1902 hr
= removeFromTree(
1904 This
->storageDirEntry
,
1908 * Invalidate the entry
1911 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1917 /******************************************************************************
1918 * Internal stream list handlers
1921 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1923 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1924 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1927 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1929 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1930 list_remove(&(strm
->StrmListEntry
));
1933 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1935 StgStreamImpl
*strm
;
1937 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1939 if (strm
->dirEntry
== streamEntry
)
1948 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1950 StorageInternalImpl
*childstg
;
1952 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1954 if (childstg
->base
.storageDirEntry
== storageEntry
)
1963 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1965 struct list
*cur
, *cur2
;
1966 StgStreamImpl
*strm
=NULL
;
1967 StorageInternalImpl
*childstg
=NULL
;
1969 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1970 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1971 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1972 strm
->parentStorage
= NULL
;
1976 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1977 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1978 StorageBaseImpl_Invalidate( &childstg
->base
);
1981 if (stg
->transactedChild
)
1983 StorageBaseImpl_Invalidate(stg
->transactedChild
);
1985 stg
->transactedChild
= NULL
;
1990 /*********************************************************************
1994 * Delete the contents of a storage entry.
1997 static HRESULT
deleteStorageContents(
1998 StorageBaseImpl
*parentStorage
,
1999 DirRef indexToDelete
,
2000 DirEntry entryDataToDelete
)
2002 IEnumSTATSTG
*elements
= 0;
2003 IStorage
*childStorage
= 0;
2004 STATSTG currentElement
;
2006 HRESULT destroyHr
= S_OK
;
2007 StorageInternalImpl
*stg
, *stg2
;
2009 /* Invalidate any open storage objects. */
2010 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2012 if (stg
->base
.storageDirEntry
== indexToDelete
)
2014 StorageBaseImpl_Invalidate(&stg
->base
);
2019 * Open the storage and enumerate it
2021 hr
= StorageBaseImpl_OpenStorage(
2022 (IStorage
*)parentStorage
,
2023 entryDataToDelete
.name
,
2025 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2036 * Enumerate the elements
2038 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2043 * Obtain the next element
2045 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2048 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2050 CoTaskMemFree(currentElement
.pwcsName
);
2054 * We need to Reset the enumeration every time because we delete elements
2055 * and the enumeration could be invalid
2057 IEnumSTATSTG_Reset(elements
);
2059 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2061 IStorage_Release(childStorage
);
2062 IEnumSTATSTG_Release(elements
);
2067 /*********************************************************************
2071 * Perform the deletion of a stream's data
2074 static HRESULT
deleteStreamContents(
2075 StorageBaseImpl
*parentStorage
,
2076 DirRef indexToDelete
,
2077 DirEntry entryDataToDelete
)
2081 ULARGE_INTEGER size
;
2082 StgStreamImpl
*strm
, *strm2
;
2084 /* Invalidate any open stream objects. */
2085 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2087 if (strm
->dirEntry
== indexToDelete
)
2089 TRACE("Stream deleted %p\n", strm
);
2090 strm
->parentStorage
= NULL
;
2091 list_remove(&strm
->StrmListEntry
);
2095 size
.u
.HighPart
= 0;
2098 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2099 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2109 hr
= IStream_SetSize(pis
, size
);
2117 * Release the stream object.
2119 IStream_Release(pis
);
2124 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2128 case DIRENTRY_RELATION_PREVIOUS
:
2129 entry
->leftChild
= new_target
;
2131 case DIRENTRY_RELATION_NEXT
:
2132 entry
->rightChild
= new_target
;
2134 case DIRENTRY_RELATION_DIR
:
2135 entry
->dirRootEntry
= new_target
;
2142 /*************************************************************************
2146 * This method removes a directory entry from its parent storage tree without
2147 * freeing any resources attached to it.
2149 static HRESULT
removeFromTree(
2150 StorageBaseImpl
*This
,
2151 DirRef parentStorageIndex
,
2152 DirRef deletedIndex
)
2155 DirEntry entryToDelete
;
2156 DirEntry parentEntry
;
2157 DirRef parentEntryRef
;
2158 ULONG typeOfRelation
;
2160 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2166 * Find the element that links to the one we want to delete.
2168 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2169 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2174 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2177 * Replace the deleted entry with its left child
2179 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2181 hr
= StorageBaseImpl_WriteDirEntry(
2190 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2193 * We need to reinsert the right child somewhere. We already know it and
2194 * its children are greater than everything in the left tree, so we
2195 * insert it at the rightmost point in the left tree.
2197 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2198 DirEntry newRightChildParentEntry
;
2202 hr
= StorageBaseImpl_ReadDirEntry(
2204 newRightChildParent
,
2205 &newRightChildParentEntry
);
2211 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2212 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2213 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2215 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2217 hr
= StorageBaseImpl_WriteDirEntry(
2219 newRightChildParent
,
2220 &newRightChildParentEntry
);
2230 * Replace the deleted entry with its right child
2232 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2234 hr
= StorageBaseImpl_WriteDirEntry(
2248 /******************************************************************************
2249 * SetElementTimes (IStorage)
2251 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2253 const OLECHAR
*pwcsName
,/* [string][in] */
2254 const FILETIME
*pctime
, /* [in] */
2255 const FILETIME
*patime
, /* [in] */
2256 const FILETIME
*pmtime
) /* [in] */
2258 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2262 /******************************************************************************
2263 * SetStateBits (IStorage)
2265 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2267 DWORD grfStateBits
,/* [in] */
2268 DWORD grfMask
) /* [in] */
2270 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2273 return STG_E_REVERTED
;
2275 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2279 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2280 DirRef index
, const DirEntry
*data
)
2282 StorageImpl
*This
= (StorageImpl
*)base
;
2283 return StorageImpl_WriteDirEntry(This
, index
, data
);
2286 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2287 DirRef index
, DirEntry
*data
)
2289 StorageImpl
*This
= (StorageImpl
*)base
;
2290 return StorageImpl_ReadDirEntry(This
, index
, data
);
2293 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2297 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2299 if (!This
->blockChainCache
[i
])
2301 return &This
->blockChainCache
[i
];
2305 i
= This
->blockChainToEvict
;
2307 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2308 This
->blockChainCache
[i
] = NULL
;
2310 This
->blockChainToEvict
++;
2311 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2312 This
->blockChainToEvict
= 0;
2314 return &This
->blockChainCache
[i
];
2317 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2320 int i
, free_index
=-1;
2322 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2324 if (!This
->blockChainCache
[i
])
2326 if (free_index
== -1) free_index
= i
;
2328 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2330 return &This
->blockChainCache
[i
];
2334 if (free_index
== -1)
2336 free_index
= This
->blockChainToEvict
;
2338 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2339 This
->blockChainCache
[free_index
] = NULL
;
2341 This
->blockChainToEvict
++;
2342 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2343 This
->blockChainToEvict
= 0;
2346 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2347 return &This
->blockChainCache
[free_index
];
2350 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2351 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2353 StorageImpl
*This
= (StorageImpl
*)base
;
2358 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2359 if (FAILED(hr
)) return hr
;
2361 if (data
.size
.QuadPart
== 0)
2367 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2369 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2376 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2378 SmallBlockChainStream
*stream
;
2380 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2381 if (!stream
) return E_OUTOFMEMORY
;
2383 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2385 SmallBlockChainStream_Destroy(stream
);
2391 BlockChainStream
*stream
= NULL
;
2393 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2394 if (!stream
) return E_OUTOFMEMORY
;
2396 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2402 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2403 ULARGE_INTEGER newsize
)
2405 StorageImpl
*This
= (StorageImpl
*)base
;
2408 SmallBlockChainStream
*smallblock
=NULL
;
2409 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2411 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2412 if (FAILED(hr
)) return hr
;
2414 /* In simple mode keep the stream size above the small block limit */
2415 if (This
->base
.openFlags
& STGM_SIMPLE
)
2416 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2418 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2421 /* Create a block chain object of the appropriate type */
2422 if (data
.size
.QuadPart
== 0)
2424 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2426 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2427 if (!smallblock
) return E_OUTOFMEMORY
;
2431 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2432 bigblock
= *pbigblock
;
2433 if (!bigblock
) return E_OUTOFMEMORY
;
2436 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2438 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2439 if (!smallblock
) return E_OUTOFMEMORY
;
2443 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2444 bigblock
= *pbigblock
;
2445 if (!bigblock
) return E_OUTOFMEMORY
;
2448 /* Change the block chain type if necessary. */
2449 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2451 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2454 SmallBlockChainStream_Destroy(smallblock
);
2458 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2459 *pbigblock
= bigblock
;
2461 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2463 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
);
2468 /* Set the size of the block chain. */
2471 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2472 SmallBlockChainStream_Destroy(smallblock
);
2476 BlockChainStream_SetSize(bigblock
, newsize
);
2479 /* Set the size in the directory entry. */
2480 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2483 data
.size
= newsize
;
2485 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2490 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2491 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2493 StorageImpl
*This
= (StorageImpl
*)base
;
2496 ULARGE_INTEGER newSize
;
2498 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2499 if (FAILED(hr
)) return hr
;
2501 /* Grow the stream if necessary */
2502 newSize
.QuadPart
= 0;
2503 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2505 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2507 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2511 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2512 if (FAILED(hr
)) return hr
;
2515 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2517 SmallBlockChainStream
*stream
;
2519 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2520 if (!stream
) return E_OUTOFMEMORY
;
2522 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2524 SmallBlockChainStream_Destroy(stream
);
2530 BlockChainStream
*stream
;
2532 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2533 if (!stream
) return E_OUTOFMEMORY
;
2535 hr
= BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2542 * Virtual function table for the IStorage32Impl class.
2544 static const IStorageVtbl Storage32Impl_Vtbl
=
2546 StorageBaseImpl_QueryInterface
,
2547 StorageBaseImpl_AddRef
,
2548 StorageBaseImpl_Release
,
2549 StorageBaseImpl_CreateStream
,
2550 StorageBaseImpl_OpenStream
,
2551 StorageBaseImpl_CreateStorage
,
2552 StorageBaseImpl_OpenStorage
,
2553 StorageBaseImpl_CopyTo
,
2554 StorageBaseImpl_MoveElementTo
,
2557 StorageBaseImpl_EnumElements
,
2558 StorageBaseImpl_DestroyElement
,
2559 StorageBaseImpl_RenameElement
,
2560 StorageBaseImpl_SetElementTimes
,
2561 StorageBaseImpl_SetClass
,
2562 StorageBaseImpl_SetStateBits
,
2563 StorageBaseImpl_Stat
2566 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2568 StorageImpl_Destroy
,
2569 StorageImpl_Invalidate
,
2570 StorageImpl_CreateDirEntry
,
2571 StorageImpl_BaseWriteDirEntry
,
2572 StorageImpl_BaseReadDirEntry
,
2573 StorageImpl_DestroyDirEntry
,
2574 StorageImpl_StreamReadAt
,
2575 StorageImpl_StreamWriteAt
,
2576 StorageImpl_StreamSetSize
2579 static HRESULT
StorageImpl_Construct(
2587 StorageImpl
** result
)
2591 DirEntry currentEntry
;
2592 DirRef currentEntryRef
;
2593 WCHAR fullpath
[MAX_PATH
];
2595 if ( FAILED( validateSTGM(openFlags
) ))
2596 return STG_E_INVALIDFLAG
;
2598 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2600 return E_OUTOFMEMORY
;
2602 memset(This
, 0, sizeof(StorageImpl
));
2604 list_init(&This
->base
.strmHead
);
2606 list_init(&This
->base
.storageHead
);
2608 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2609 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2610 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2611 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2613 This
->base
.create
= create
;
2615 This
->base
.reverted
= 0;
2617 This
->hFile
= hFile
;
2620 if (!GetFullPathNameW(pwcsName
, MAX_PATH
, fullpath
, NULL
))
2622 lstrcpynW(fullpath
, pwcsName
, MAX_PATH
);
2624 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2625 (lstrlenW(fullpath
)+1)*sizeof(WCHAR
));
2626 if (!This
->pwcsName
)
2628 hr
= STG_E_INSUFFICIENTMEMORY
;
2631 strcpyW(This
->pwcsName
, fullpath
);
2632 This
->base
.filename
= This
->pwcsName
;
2636 * Initialize the big block cache.
2638 This
->bigBlockSize
= sector_size
;
2639 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2640 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2645 if (This
->bigBlockFile
== 0)
2653 ULARGE_INTEGER size
;
2654 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
2657 * Initialize all header variables:
2658 * - The big block depot consists of one block and it is at block 0
2659 * - The directory table starts at block 1
2660 * - There is no small block depot
2662 memset( This
->bigBlockDepotStart
,
2664 sizeof(This
->bigBlockDepotStart
));
2666 This
->bigBlockDepotCount
= 1;
2667 This
->bigBlockDepotStart
[0] = 0;
2668 This
->rootStartBlock
= 1;
2669 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
2670 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2671 if (sector_size
== 4096)
2672 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
2674 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
2675 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2676 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2677 This
->extBigBlockDepotCount
= 0;
2679 StorageImpl_SaveFileHeader(This
);
2682 * Add one block for the big block depot and one block for the directory table
2684 size
.u
.HighPart
= 0;
2685 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2686 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2689 * Initialize the big block depot
2691 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2692 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2693 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2694 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2699 * Load the header for the file.
2701 hr
= StorageImpl_LoadFileHeader(This
);
2710 * There is no block depot cached yet.
2712 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2715 * Start searching for free blocks with block 0.
2717 This
->prevFreeBlock
= 0;
2719 This
->firstFreeSmallBlock
= 0;
2722 * Create the block chain abstractions.
2724 if(!(This
->rootBlockChain
=
2725 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2727 hr
= STG_E_READFAULT
;
2731 if(!(This
->smallBlockDepotChain
=
2732 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2735 hr
= STG_E_READFAULT
;
2740 * Write the root storage entry (memory only)
2746 * Initialize the directory table
2748 memset(&rootEntry
, 0, sizeof(rootEntry
));
2749 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2750 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2751 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2752 rootEntry
.stgType
= STGTY_ROOT
;
2753 rootEntry
.leftChild
= DIRENTRY_NULL
;
2754 rootEntry
.rightChild
= DIRENTRY_NULL
;
2755 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2756 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2757 rootEntry
.size
.u
.HighPart
= 0;
2758 rootEntry
.size
.u
.LowPart
= 0;
2760 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2764 * Find the ID of the root storage.
2766 currentEntryRef
= 0;
2770 hr
= StorageImpl_ReadDirEntry(
2777 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2778 (currentEntry
.stgType
== STGTY_ROOT
) )
2780 This
->base
.storageDirEntry
= currentEntryRef
;
2786 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2790 hr
= STG_E_READFAULT
;
2795 * Create the block chain abstraction for the small block root chain.
2797 if(!(This
->smallBlockRootChain
=
2798 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2800 hr
= STG_E_READFAULT
;
2806 IStorage_Release((IStorage
*)This
);
2815 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
2817 StorageImpl
*This
= (StorageImpl
*) iface
;
2819 StorageBaseImpl_DeleteAll(&This
->base
);
2821 This
->base
.reverted
= 1;
2824 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2826 StorageImpl
*This
= (StorageImpl
*) iface
;
2828 TRACE("(%p)\n", This
);
2830 StorageImpl_Invalidate(iface
);
2832 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2834 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2835 BlockChainStream_Destroy(This
->rootBlockChain
);
2836 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2838 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2839 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2841 if (This
->bigBlockFile
)
2842 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2843 HeapFree(GetProcessHeap(), 0, This
);
2846 /******************************************************************************
2847 * Storage32Impl_GetNextFreeBigBlock
2849 * Returns the index of the next free big block.
2850 * If the big block depot is filled, this method will enlarge it.
2853 static ULONG
StorageImpl_GetNextFreeBigBlock(
2856 ULONG depotBlockIndexPos
;
2857 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
2859 ULONG depotBlockOffset
;
2860 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2861 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2863 ULONG freeBlock
= BLOCK_UNUSED
;
2864 ULARGE_INTEGER neededSize
;
2866 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2867 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2870 * Scan the entire big block depot until we find a block marked free
2872 while (nextBlockIndex
!= BLOCK_UNUSED
)
2874 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2876 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2879 * Grow the primary depot.
2881 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2883 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2886 * Add a block depot.
2888 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2889 This
->bigBlockDepotCount
++;
2890 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2893 * Flag it as a block depot.
2895 StorageImpl_SetNextBlockInChain(This
,
2899 /* Save new header information.
2901 StorageImpl_SaveFileHeader(This
);
2906 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2908 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2911 * Grow the extended depot.
2913 ULONG extIndex
= BLOCK_UNUSED
;
2914 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2915 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2917 if (extBlockOffset
== 0)
2919 /* We need an extended block.
2921 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2922 This
->extBigBlockDepotCount
++;
2923 depotBlockIndexPos
= extIndex
+ 1;
2926 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2929 * Add a block depot and mark it in the extended block.
2931 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2932 This
->bigBlockDepotCount
++;
2933 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2935 /* Flag the block depot.
2937 StorageImpl_SetNextBlockInChain(This
,
2941 /* If necessary, flag the extended depot block.
2943 if (extIndex
!= BLOCK_UNUSED
)
2944 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2946 /* Save header information.
2948 StorageImpl_SaveFileHeader(This
);
2952 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2956 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2957 ( nextBlockIndex
!= BLOCK_UNUSED
))
2959 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2961 if (nextBlockIndex
== BLOCK_UNUSED
)
2963 freeBlock
= (depotIndex
* blocksPerDepot
) +
2964 (depotBlockOffset
/sizeof(ULONG
));
2967 depotBlockOffset
+= sizeof(ULONG
);
2972 depotBlockOffset
= 0;
2976 * make sure that the block physically exists before using it
2978 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
2979 BIGBLOCKFILE_Expand(This
->bigBlockFile
, neededSize
);
2981 This
->prevFreeBlock
= freeBlock
;
2986 /******************************************************************************
2987 * Storage32Impl_AddBlockDepot
2989 * This will create a depot block, essentially it is a block initialized
2992 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2994 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
2997 * Initialize blocks as free
2999 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3000 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3003 /******************************************************************************
3004 * Storage32Impl_GetExtDepotBlock
3006 * Returns the index of the block that corresponds to the specified depot
3007 * index. This method is only for depot indexes equal or greater than
3008 * COUNT_BBDEPOTINHEADER.
3010 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3012 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3013 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3014 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3015 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3016 ULONG blockIndex
= BLOCK_UNUSED
;
3017 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3019 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3021 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
3022 return BLOCK_UNUSED
;
3024 while (extBlockCount
> 0)
3026 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3030 if (extBlockIndex
!= BLOCK_UNUSED
)
3031 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
3032 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
3037 /******************************************************************************
3038 * Storage32Impl_SetExtDepotBlock
3040 * Associates the specified block index to the specified depot index.
3041 * This method is only for depot indexes equal or greater than
3042 * COUNT_BBDEPOTINHEADER.
3044 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3046 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3047 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3048 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3049 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3050 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
3052 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3054 while (extBlockCount
> 0)
3056 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
3060 if (extBlockIndex
!= BLOCK_UNUSED
)
3062 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3063 extBlockOffset
* sizeof(ULONG
),
3068 /******************************************************************************
3069 * Storage32Impl_AddExtBlockDepot
3071 * Creates an extended depot block.
3073 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3075 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3076 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3077 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3078 ULONG index
= BLOCK_UNUSED
;
3079 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3080 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3081 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3083 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3084 blocksPerDepotBlock
;
3086 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3089 * The first extended block.
3091 This
->extBigBlockDepotStart
= index
;
3097 * Follow the chain to the last one.
3099 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
3101 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
3105 * Add the new extended block to the chain.
3107 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3112 * Initialize this block.
3114 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3115 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3120 /******************************************************************************
3121 * Storage32Impl_FreeBigBlock
3123 * This method will flag the specified block as free in the big block depot.
3125 static void StorageImpl_FreeBigBlock(
3129 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3131 if (blockIndex
< This
->prevFreeBlock
)
3132 This
->prevFreeBlock
= blockIndex
;
3135 /************************************************************************
3136 * Storage32Impl_GetNextBlockInChain
3138 * This method will retrieve the block index of the next big block in
3141 * Params: This - Pointer to the Storage object.
3142 * blockIndex - Index of the block to retrieve the chain
3144 * nextBlockIndex - receives the return value.
3146 * Returns: This method returns the index of the next block in the chain.
3147 * It will return the constants:
3148 * BLOCK_SPECIAL - If the block given was not part of a
3150 * BLOCK_END_OF_CHAIN - If the block given was the last in
3152 * BLOCK_UNUSED - If the block given was not past of a chain
3154 * BLOCK_EXTBBDEPOT - This block is part of the extended
3157 * See Windows documentation for more details on IStorage methods.
3159 static HRESULT
StorageImpl_GetNextBlockInChain(
3162 ULONG
* nextBlockIndex
)
3164 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3165 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3166 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3167 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3169 ULONG depotBlockIndexPos
;
3170 int index
, num_blocks
;
3172 *nextBlockIndex
= BLOCK_SPECIAL
;
3174 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3176 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3177 This
->bigBlockDepotCount
);
3178 return STG_E_READFAULT
;
3182 * Cache the currently accessed depot block.
3184 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3186 This
->indexBlockDepotCached
= depotBlockCount
;
3188 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3190 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3195 * We have to look in the extended depot.
3197 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3200 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
3203 return STG_E_READFAULT
;
3205 num_blocks
= This
->bigBlockSize
/ 4;
3207 for (index
= 0; index
< num_blocks
; index
++)
3209 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3210 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3214 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3219 /******************************************************************************
3220 * Storage32Impl_GetNextExtendedBlock
3222 * Given an extended block this method will return the next extended block.
3225 * The last ULONG of an extended block is the block index of the next
3226 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3230 * - The index of the next extended block
3231 * - BLOCK_UNUSED: there is no next extended block.
3232 * - Any other return values denotes failure.
3234 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3236 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3237 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3239 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3242 return nextBlockIndex
;
3245 /******************************************************************************
3246 * Storage32Impl_SetNextBlockInChain
3248 * This method will write the index of the specified block's next block
3249 * in the big block depot.
3251 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3254 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3255 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3256 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3259 static void StorageImpl_SetNextBlockInChain(
3264 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3265 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3266 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3267 ULONG depotBlockIndexPos
;
3269 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3270 assert(blockIndex
!= nextBlock
);
3272 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3274 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3279 * We have to look in the extended depot.
3281 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3284 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3287 * Update the cached block depot, if necessary.
3289 if (depotBlockCount
== This
->indexBlockDepotCached
)
3291 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3295 /******************************************************************************
3296 * Storage32Impl_LoadFileHeader
3298 * This method will read in the file header
3300 static HRESULT
StorageImpl_LoadFileHeader(
3304 BYTE headerBigBlock
[HEADER_SIZE
];
3306 ULARGE_INTEGER offset
;
3311 * Get a pointer to the big block of data containing the header.
3313 offset
.u
.HighPart
= 0;
3314 offset
.u
.LowPart
= 0;
3315 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3316 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3317 hr
= STG_E_FILENOTFOUND
;
3320 * Extract the information from the header.
3325 * Check for the "magic number" signature and return an error if it is not
3328 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3330 return STG_E_OLDFORMAT
;
3333 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3335 return STG_E_INVALIDHEADER
;
3338 StorageUtl_ReadWord(
3340 OFFSET_BIGBLOCKSIZEBITS
,
3341 &This
->bigBlockSizeBits
);
3343 StorageUtl_ReadWord(
3345 OFFSET_SMALLBLOCKSIZEBITS
,
3346 &This
->smallBlockSizeBits
);
3348 StorageUtl_ReadDWord(
3350 OFFSET_BBDEPOTCOUNT
,
3351 &This
->bigBlockDepotCount
);
3353 StorageUtl_ReadDWord(
3355 OFFSET_ROOTSTARTBLOCK
,
3356 &This
->rootStartBlock
);
3358 StorageUtl_ReadDWord(
3360 OFFSET_SMALLBLOCKLIMIT
,
3361 &This
->smallBlockLimit
);
3363 StorageUtl_ReadDWord(
3365 OFFSET_SBDEPOTSTART
,
3366 &This
->smallBlockDepotStart
);
3368 StorageUtl_ReadDWord(
3370 OFFSET_EXTBBDEPOTSTART
,
3371 &This
->extBigBlockDepotStart
);
3373 StorageUtl_ReadDWord(
3375 OFFSET_EXTBBDEPOTCOUNT
,
3376 &This
->extBigBlockDepotCount
);
3378 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3380 StorageUtl_ReadDWord(
3382 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3383 &(This
->bigBlockDepotStart
[index
]));
3387 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3389 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3390 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3393 * Right now, the code is making some assumptions about the size of the
3394 * blocks, just make sure they are what we're expecting.
3396 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3397 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3398 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3400 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3401 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3402 hr
= STG_E_INVALIDHEADER
;
3411 /******************************************************************************
3412 * Storage32Impl_SaveFileHeader
3414 * This method will save to the file the header
3416 static void StorageImpl_SaveFileHeader(
3419 BYTE headerBigBlock
[HEADER_SIZE
];
3422 ULARGE_INTEGER offset
;
3423 DWORD bytes_read
, bytes_written
;
3426 * Get a pointer to the big block of data containing the header.
3428 offset
.u
.HighPart
= 0;
3429 offset
.u
.LowPart
= 0;
3430 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3431 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3432 hr
= STG_E_FILENOTFOUND
;
3435 * If the block read failed, the file is probably new.
3440 * Initialize for all unknown fields.
3442 memset(headerBigBlock
, 0, HEADER_SIZE
);
3445 * Initialize the magic number.
3447 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3450 * And a bunch of things we don't know what they mean
3452 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3453 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3454 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3458 * Write the information to the header.
3460 StorageUtl_WriteWord(
3462 OFFSET_BIGBLOCKSIZEBITS
,
3463 This
->bigBlockSizeBits
);
3465 StorageUtl_WriteWord(
3467 OFFSET_SMALLBLOCKSIZEBITS
,
3468 This
->smallBlockSizeBits
);
3470 StorageUtl_WriteDWord(
3472 OFFSET_BBDEPOTCOUNT
,
3473 This
->bigBlockDepotCount
);
3475 StorageUtl_WriteDWord(
3477 OFFSET_ROOTSTARTBLOCK
,
3478 This
->rootStartBlock
);
3480 StorageUtl_WriteDWord(
3482 OFFSET_SMALLBLOCKLIMIT
,
3483 This
->smallBlockLimit
);
3485 StorageUtl_WriteDWord(
3487 OFFSET_SBDEPOTSTART
,
3488 This
->smallBlockDepotStart
);
3490 StorageUtl_WriteDWord(
3492 OFFSET_SBDEPOTCOUNT
,
3493 This
->smallBlockDepotChain
?
3494 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3496 StorageUtl_WriteDWord(
3498 OFFSET_EXTBBDEPOTSTART
,
3499 This
->extBigBlockDepotStart
);
3501 StorageUtl_WriteDWord(
3503 OFFSET_EXTBBDEPOTCOUNT
,
3504 This
->extBigBlockDepotCount
);
3506 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3508 StorageUtl_WriteDWord(
3510 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3511 (This
->bigBlockDepotStart
[index
]));
3515 * Write the big block back to the file.
3517 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
3520 /******************************************************************************
3521 * StorageImpl_ReadRawDirEntry
3523 * This method will read the raw data from a directory entry in the file.
3525 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3527 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3529 ULARGE_INTEGER offset
;
3533 offset
.u
.HighPart
= 0;
3534 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3536 hr
= BlockChainStream_ReadAt(
3537 This
->rootBlockChain
,
3546 /******************************************************************************
3547 * StorageImpl_WriteRawDirEntry
3549 * This method will write the raw data from a directory entry in the file.
3551 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3553 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3555 ULARGE_INTEGER offset
;
3559 offset
.u
.HighPart
= 0;
3560 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3562 hr
= BlockChainStream_WriteAt(
3563 This
->rootBlockChain
,
3572 /******************************************************************************
3575 * Update raw directory entry data from the fields in newData.
3577 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3579 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3581 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3584 buffer
+ OFFSET_PS_NAME
,
3586 DIRENTRY_NAME_BUFFER_LEN
);
3588 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3590 StorageUtl_WriteWord(
3592 OFFSET_PS_NAMELENGTH
,
3593 newData
->sizeOfNameString
);
3595 StorageUtl_WriteDWord(
3597 OFFSET_PS_LEFTCHILD
,
3598 newData
->leftChild
);
3600 StorageUtl_WriteDWord(
3602 OFFSET_PS_RIGHTCHILD
,
3603 newData
->rightChild
);
3605 StorageUtl_WriteDWord(
3608 newData
->dirRootEntry
);
3610 StorageUtl_WriteGUID(
3615 StorageUtl_WriteDWord(
3618 newData
->ctime
.dwLowDateTime
);
3620 StorageUtl_WriteDWord(
3622 OFFSET_PS_CTIMEHIGH
,
3623 newData
->ctime
.dwHighDateTime
);
3625 StorageUtl_WriteDWord(
3628 newData
->mtime
.dwLowDateTime
);
3630 StorageUtl_WriteDWord(
3632 OFFSET_PS_MTIMEHIGH
,
3633 newData
->ctime
.dwHighDateTime
);
3635 StorageUtl_WriteDWord(
3637 OFFSET_PS_STARTBLOCK
,
3638 newData
->startingBlock
);
3640 StorageUtl_WriteDWord(
3643 newData
->size
.u
.LowPart
);
3646 /******************************************************************************
3647 * Storage32Impl_ReadDirEntry
3649 * This method will read the specified directory entry.
3651 HRESULT
StorageImpl_ReadDirEntry(
3656 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3659 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3661 if (SUCCEEDED(readRes
))
3663 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3666 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3667 DIRENTRY_NAME_BUFFER_LEN
);
3668 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3670 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3672 StorageUtl_ReadWord(
3674 OFFSET_PS_NAMELENGTH
,
3675 &buffer
->sizeOfNameString
);
3677 StorageUtl_ReadDWord(
3679 OFFSET_PS_LEFTCHILD
,
3680 &buffer
->leftChild
);
3682 StorageUtl_ReadDWord(
3684 OFFSET_PS_RIGHTCHILD
,
3685 &buffer
->rightChild
);
3687 StorageUtl_ReadDWord(
3690 &buffer
->dirRootEntry
);
3692 StorageUtl_ReadGUID(
3697 StorageUtl_ReadDWord(
3700 &buffer
->ctime
.dwLowDateTime
);
3702 StorageUtl_ReadDWord(
3704 OFFSET_PS_CTIMEHIGH
,
3705 &buffer
->ctime
.dwHighDateTime
);
3707 StorageUtl_ReadDWord(
3710 &buffer
->mtime
.dwLowDateTime
);
3712 StorageUtl_ReadDWord(
3714 OFFSET_PS_MTIMEHIGH
,
3715 &buffer
->mtime
.dwHighDateTime
);
3717 StorageUtl_ReadDWord(
3719 OFFSET_PS_STARTBLOCK
,
3720 &buffer
->startingBlock
);
3722 StorageUtl_ReadDWord(
3725 &buffer
->size
.u
.LowPart
);
3727 buffer
->size
.u
.HighPart
= 0;
3733 /*********************************************************************
3734 * Write the specified directory entry to the file
3736 HRESULT
StorageImpl_WriteDirEntry(
3739 const DirEntry
* buffer
)
3741 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3744 UpdateRawDirEntry(currentEntry
, buffer
);
3746 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3750 static BOOL
StorageImpl_ReadBigBlock(
3755 ULARGE_INTEGER ulOffset
;
3758 ulOffset
.u
.HighPart
= 0;
3759 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3761 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3762 return (read
== This
->bigBlockSize
);
3765 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3771 ULARGE_INTEGER ulOffset
;
3775 ulOffset
.u
.HighPart
= 0;
3776 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3777 ulOffset
.u
.LowPart
+= offset
;
3779 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3780 *value
= lendian32toh(tmp
);
3781 return (read
== sizeof(DWORD
));
3784 static BOOL
StorageImpl_WriteBigBlock(
3789 ULARGE_INTEGER ulOffset
;
3792 ulOffset
.u
.HighPart
= 0;
3793 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3795 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3796 return (wrote
== This
->bigBlockSize
);
3799 static BOOL
StorageImpl_WriteDWordToBigBlock(
3805 ULARGE_INTEGER ulOffset
;
3808 ulOffset
.u
.HighPart
= 0;
3809 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
3810 ulOffset
.u
.LowPart
+= offset
;
3812 value
= htole32(value
);
3813 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3814 return (wrote
== sizeof(DWORD
));
3817 /******************************************************************************
3818 * Storage32Impl_SmallBlocksToBigBlocks
3820 * This method will convert a small block chain to a big block chain.
3821 * The small block chain will be destroyed.
3823 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3825 SmallBlockChainStream
** ppsbChain
)
3827 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3828 ULARGE_INTEGER size
, offset
;
3829 ULONG cbRead
, cbWritten
;
3830 ULARGE_INTEGER cbTotalRead
;
3831 DirRef streamEntryRef
;
3832 HRESULT resWrite
= S_OK
;
3834 DirEntry streamEntry
;
3836 BlockChainStream
*bbTempChain
= NULL
;
3837 BlockChainStream
*bigBlockChain
= NULL
;
3840 * Create a temporary big block chain that doesn't have
3841 * an associated directory entry. This temporary chain will be
3842 * used to copy data from small blocks to big blocks.
3844 bbTempChain
= BlockChainStream_Construct(This
,
3847 if(!bbTempChain
) return NULL
;
3849 * Grow the big block chain.
3851 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3852 BlockChainStream_SetSize(bbTempChain
, size
);
3855 * Copy the contents of the small block chain to the big block chain
3856 * by small block size increments.
3858 offset
.u
.LowPart
= 0;
3859 offset
.u
.HighPart
= 0;
3860 cbTotalRead
.QuadPart
= 0;
3862 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3865 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3867 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3870 if (FAILED(resRead
))
3875 cbTotalRead
.QuadPart
+= cbRead
;
3877 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3883 if (FAILED(resWrite
))
3886 offset
.u
.LowPart
+= cbRead
;
3888 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3889 HeapFree(GetProcessHeap(),0,buffer
);
3891 size
.u
.HighPart
= 0;
3894 if (FAILED(resRead
) || FAILED(resWrite
))
3896 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3897 BlockChainStream_SetSize(bbTempChain
, size
);
3898 BlockChainStream_Destroy(bbTempChain
);
3903 * Destroy the small block chain.
3905 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3906 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3907 SmallBlockChainStream_Destroy(*ppsbChain
);
3911 * Change the directory entry. This chain is now a big block chain
3912 * and it doesn't reside in the small blocks chain anymore.
3914 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3916 streamEntry
.startingBlock
= bbHeadOfChain
;
3918 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3921 * Destroy the temporary entryless big block chain.
3922 * Create a new big block chain associated with this entry.
3924 BlockChainStream_Destroy(bbTempChain
);
3925 bigBlockChain
= BlockChainStream_Construct(This
,
3929 return bigBlockChain
;
3932 /******************************************************************************
3933 * Storage32Impl_BigBlocksToSmallBlocks
3935 * This method will convert a big block chain to a small block chain.
3936 * The big block chain will be destroyed on success.
3938 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3940 BlockChainStream
** ppbbChain
)
3942 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3943 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3944 DirRef streamEntryRef
;
3945 HRESULT resWrite
= S_OK
, resRead
;
3946 DirEntry streamEntry
;
3948 SmallBlockChainStream
* sbTempChain
;
3950 TRACE("%p %p\n", This
, ppbbChain
);
3952 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3958 size
= BlockChainStream_GetSize(*ppbbChain
);
3959 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3961 offset
.u
.HighPart
= 0;
3962 offset
.u
.LowPart
= 0;
3963 cbTotalRead
.QuadPart
= 0;
3964 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3967 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3968 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3976 cbTotalRead
.QuadPart
+= cbRead
;
3978 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3979 cbRead
, buffer
, &cbWritten
);
3981 if(FAILED(resWrite
))
3984 offset
.u
.LowPart
+= cbRead
;
3986 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3987 HeapFree(GetProcessHeap(), 0, buffer
);
3989 size
.u
.HighPart
= 0;
3992 if(FAILED(resRead
) || FAILED(resWrite
))
3994 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3995 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3996 SmallBlockChainStream_Destroy(sbTempChain
);
4000 /* destroy the original big block chain */
4001 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4002 BlockChainStream_SetSize(*ppbbChain
, size
);
4003 BlockChainStream_Destroy(*ppbbChain
);
4006 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4007 streamEntry
.startingBlock
= sbHeadOfChain
;
4008 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4010 SmallBlockChainStream_Destroy(sbTempChain
);
4011 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4014 static HRESULT
CreateSnapshotFile(StorageBaseImpl
* original
, StorageBaseImpl
**snapshot
)
4017 DirEntry parentData
, snapshotData
;
4019 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_DELETEONRELEASE
,
4020 0, (IStorage
**)snapshot
);
4024 hr
= StorageBaseImpl_ReadDirEntry(original
,
4025 original
->storageDirEntry
, &parentData
);
4028 hr
= StorageBaseImpl_ReadDirEntry((*snapshot
),
4029 (*snapshot
)->storageDirEntry
, &snapshotData
);
4033 memcpy(snapshotData
.name
, parentData
.name
, sizeof(snapshotData
.name
));
4034 snapshotData
.sizeOfNameString
= parentData
.sizeOfNameString
;
4035 snapshotData
.stgType
= parentData
.stgType
;
4036 snapshotData
.clsid
= parentData
.clsid
;
4037 snapshotData
.ctime
= parentData
.ctime
;
4038 snapshotData
.mtime
= parentData
.mtime
;
4039 hr
= StorageBaseImpl_WriteDirEntry((*snapshot
),
4040 (*snapshot
)->storageDirEntry
, &snapshotData
);
4044 hr
= IStorage_CopyTo((IStorage
*)original
, 0, NULL
, NULL
,
4045 (IStorage
*)(*snapshot
));
4047 if (FAILED(hr
)) IStorage_Release((IStorage
*)(*snapshot
));
4053 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
4055 DWORD grfCommitFlags
) /* [in] */
4057 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4059 DirEntry data
, tempStorageData
, snapshotRootData
;
4060 DirRef tempStorageEntry
, oldDirRoot
;
4061 StorageInternalImpl
*tempStorage
;
4063 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
4065 /* Cannot commit a read-only transacted storage */
4066 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
4067 return STG_E_ACCESSDENIED
;
4069 /* To prevent data loss, we create the new structure in the file before we
4070 * delete the old one, so that in case of errors the old data is intact. We
4071 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4072 * needed in the rare situation where we have just enough free disk space to
4073 * overwrite the existing data. */
4075 /* Create an orphaned storage in the parent for the new directory structure. */
4076 memset(&data
, 0, sizeof(data
));
4078 data
.sizeOfNameString
= 1;
4079 data
.stgType
= STGTY_STORAGE
;
4080 data
.leftChild
= DIRENTRY_NULL
;
4081 data
.rightChild
= DIRENTRY_NULL
;
4082 data
.dirRootEntry
= DIRENTRY_NULL
;
4083 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &data
, &tempStorageEntry
);
4085 if (FAILED(hr
)) return hr
;
4087 tempStorage
= StorageInternalImpl_Construct(This
->transactedParent
,
4088 STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, tempStorageEntry
);
4091 hr
= IStorage_CopyTo((IStorage
*)This
->snapshot
, 0, NULL
, NULL
,
4092 (IStorage
*)tempStorage
);
4094 list_init(&tempStorage
->ParentListEntry
);
4096 IStorage_Release((IStorage
*) tempStorage
);
4103 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4107 /* Update the storage to use the new data in one step. */
4108 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4109 This
->transactedParent
->storageDirEntry
, &data
);
4113 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4114 tempStorageEntry
, &tempStorageData
);
4119 hr
= StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4120 This
->snapshot
->storageDirEntry
, &snapshotRootData
);
4125 oldDirRoot
= data
.dirRootEntry
;
4126 data
.dirRootEntry
= tempStorageData
.dirRootEntry
;
4127 data
.clsid
= snapshotRootData
.clsid
;
4128 data
.ctime
= snapshotRootData
.ctime
;
4129 data
.mtime
= snapshotRootData
.mtime
;
4131 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
4132 This
->transactedParent
->storageDirEntry
, &data
);
4137 /* Destroy the old now-orphaned data. */
4138 DestroyReachableEntries(This
->transactedParent
, oldDirRoot
);
4139 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
, tempStorageEntry
);
4143 DestroyReachableEntries(This
->transactedParent
, tempStorageEntry
);
4149 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
4152 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4153 StorageBaseImpl
*newSnapshot
;
4156 TRACE("(%p)\n", iface
);
4158 /* Create a new copy of the parent data. */
4159 hr
= CreateSnapshotFile(This
->transactedParent
, &newSnapshot
);
4160 if (FAILED(hr
)) return hr
;
4162 /* Destroy the open objects. */
4163 StorageBaseImpl_DeleteAll(&This
->base
);
4165 /* Replace our current snapshot. */
4166 IStorage_Release((IStorage
*)This
->snapshot
);
4167 This
->snapshot
= newSnapshot
;
4172 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
4174 if (!This
->reverted
)
4176 TRACE("Storage invalidated (stg=%p)\n", This
);
4180 StorageBaseImpl_DeleteAll(This
);
4184 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
4186 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
4188 TransactedSnapshotImpl_Invalidate(iface
);
4190 IStorage_Release((IStorage
*)This
->transactedParent
);
4192 IStorage_Release((IStorage
*)This
->snapshot
);
4194 HeapFree(GetProcessHeap(), 0, This
);
4197 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
4198 const DirEntry
*newData
, DirRef
*index
)
4200 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4202 return StorageBaseImpl_CreateDirEntry(This
->snapshot
,
4206 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
4207 DirRef index
, const DirEntry
*data
)
4209 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4211 return StorageBaseImpl_WriteDirEntry(This
->snapshot
,
4215 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
4216 DirRef index
, DirEntry
*data
)
4218 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4220 return StorageBaseImpl_ReadDirEntry(This
->snapshot
,
4224 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4227 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4229 return StorageBaseImpl_DestroyDirEntry(This
->snapshot
,
4233 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
4234 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4236 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4238 return StorageBaseImpl_StreamReadAt(This
->snapshot
,
4239 index
, offset
, size
, buffer
, bytesRead
);
4242 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
4243 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4245 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4247 return StorageBaseImpl_StreamWriteAt(This
->snapshot
,
4248 index
, offset
, size
, buffer
, bytesWritten
);
4251 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
4252 DirRef index
, ULARGE_INTEGER newsize
)
4254 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
4256 return StorageBaseImpl_StreamSetSize(This
->snapshot
,
4260 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
4262 StorageBaseImpl_QueryInterface
,
4263 StorageBaseImpl_AddRef
,
4264 StorageBaseImpl_Release
,
4265 StorageBaseImpl_CreateStream
,
4266 StorageBaseImpl_OpenStream
,
4267 StorageBaseImpl_CreateStorage
,
4268 StorageBaseImpl_OpenStorage
,
4269 StorageBaseImpl_CopyTo
,
4270 StorageBaseImpl_MoveElementTo
,
4271 TransactedSnapshotImpl_Commit
,
4272 TransactedSnapshotImpl_Revert
,
4273 StorageBaseImpl_EnumElements
,
4274 StorageBaseImpl_DestroyElement
,
4275 StorageBaseImpl_RenameElement
,
4276 StorageBaseImpl_SetElementTimes
,
4277 StorageBaseImpl_SetClass
,
4278 StorageBaseImpl_SetStateBits
,
4279 StorageBaseImpl_Stat
4282 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
4284 TransactedSnapshotImpl_Destroy
,
4285 TransactedSnapshotImpl_Invalidate
,
4286 TransactedSnapshotImpl_CreateDirEntry
,
4287 TransactedSnapshotImpl_WriteDirEntry
,
4288 TransactedSnapshotImpl_ReadDirEntry
,
4289 TransactedSnapshotImpl_DestroyDirEntry
,
4290 TransactedSnapshotImpl_StreamReadAt
,
4291 TransactedSnapshotImpl_StreamWriteAt
,
4292 TransactedSnapshotImpl_StreamSetSize
4295 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
4296 TransactedSnapshotImpl
** result
)
4300 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
4303 (*result
)->base
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
4305 /* This is OK because the property set storage functions use the IStorage functions. */
4306 (*result
)->base
.pssVtbl
= parentStorage
->pssVtbl
;
4308 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
4310 list_init(&(*result
)->base
.strmHead
);
4312 list_init(&(*result
)->base
.storageHead
);
4314 (*result
)->base
.ref
= 1;
4316 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
4318 (*result
)->base
.filename
= parentStorage
->filename
;
4320 /* Create a new temporary storage to act as the snapshot */
4321 hr
= CreateSnapshotFile(parentStorage
, &(*result
)->snapshot
);
4325 (*result
)->base
.storageDirEntry
= (*result
)->snapshot
->storageDirEntry
;
4327 /* parentStorage already has 1 reference, which we take over here. */
4328 (*result
)->transactedParent
= parentStorage
;
4330 parentStorage
->transactedChild
= (StorageBaseImpl
*)*result
;
4333 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, (*result
));
4338 return E_OUTOFMEMORY
;
4341 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
4342 StorageBaseImpl
** result
)
4346 if (parentStorage
->openFlags
& (STGM_NOSCRATCH
|STGM_NOSNAPSHOT
) && !fixme
++)
4348 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
4351 return TransactedSnapshotImpl_Construct(parentStorage
,
4352 (TransactedSnapshotImpl
**)result
);
4355 static HRESULT
Storage_Construct(
4363 StorageBaseImpl
** result
)
4365 StorageImpl
*newStorage
;
4366 StorageBaseImpl
*newTransactedStorage
;
4369 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
4370 if (FAILED(hr
)) goto end
;
4372 if (openFlags
& STGM_TRANSACTED
)
4374 hr
= Storage_ConstructTransacted(&newStorage
->base
, &newTransactedStorage
);
4376 IStorage_Release((IStorage
*)newStorage
);
4378 *result
= newTransactedStorage
;
4381 *result
= &newStorage
->base
;
4387 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
4389 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4391 if (!This
->base
.reverted
)
4393 TRACE("Storage invalidated (stg=%p)\n", This
);
4395 This
->base
.reverted
= 1;
4397 This
->parentStorage
= NULL
;
4399 StorageBaseImpl_DeleteAll(&This
->base
);
4401 list_remove(&This
->ParentListEntry
);
4405 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
4407 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
4409 StorageInternalImpl_Invalidate(&This
->base
);
4411 HeapFree(GetProcessHeap(), 0, This
);
4414 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
4415 const DirEntry
*newData
, DirRef
*index
)
4417 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4419 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
4423 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
4424 DirRef index
, const DirEntry
*data
)
4426 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4428 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
4432 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
4433 DirRef index
, DirEntry
*data
)
4435 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4437 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4441 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
4444 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4446 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
4450 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
4451 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
4453 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4455 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
4456 index
, offset
, size
, buffer
, bytesRead
);
4459 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
4460 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
4462 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4464 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
4465 index
, offset
, size
, buffer
, bytesWritten
);
4468 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
4469 DirRef index
, ULARGE_INTEGER newsize
)
4471 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
4473 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
4477 /******************************************************************************
4479 ** Storage32InternalImpl_Commit
4482 static HRESULT WINAPI
StorageInternalImpl_Commit(
4484 DWORD grfCommitFlags
) /* [in] */
4486 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
4490 /******************************************************************************
4492 ** Storage32InternalImpl_Revert
4495 static HRESULT WINAPI
StorageInternalImpl_Revert(
4498 FIXME("(%p): stub\n", iface
);
4502 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
4504 IStorage_Release((IStorage
*)This
->parentStorage
);
4505 HeapFree(GetProcessHeap(), 0, This
);
4508 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
4509 IEnumSTATSTG
* iface
,
4513 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4516 return E_INVALIDARG
;
4520 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
4521 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
4524 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
4528 return E_NOINTERFACE
;
4531 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
4532 IEnumSTATSTG
* iface
)
4534 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4535 return InterlockedIncrement(&This
->ref
);
4538 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
4539 IEnumSTATSTG
* iface
)
4541 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4545 newRef
= InterlockedDecrement(&This
->ref
);
4549 IEnumSTATSTGImpl_Destroy(This
);
4555 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
4556 IEnumSTATSTGImpl
* This
,
4559 DirRef result
= DIRENTRY_NULL
;
4563 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
4565 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4566 This
->parentStorage
->storageDirEntry
, &entry
);
4567 searchNode
= entry
.dirRootEntry
;
4569 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
4571 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
4575 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
4579 searchNode
= entry
.rightChild
;
4583 result
= searchNode
;
4584 memcpy(result_name
, entry
.name
, sizeof(result_name
));
4585 searchNode
= entry
.leftChild
;
4593 if (result
!= DIRENTRY_NULL
)
4594 memcpy(This
->name
, result_name
, sizeof(result_name
));
4600 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
4601 IEnumSTATSTG
* iface
,
4604 ULONG
* pceltFetched
)
4606 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4608 DirEntry currentEntry
;
4609 STATSTG
* currentReturnStruct
= rgelt
;
4610 ULONG objectFetched
= 0;
4611 DirRef currentSearchNode
;
4614 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
4615 return E_INVALIDARG
;
4617 if (This
->parentStorage
->reverted
)
4618 return STG_E_REVERTED
;
4621 * To avoid the special case, get another pointer to a ULONG value if
4622 * the caller didn't supply one.
4624 if (pceltFetched
==0)
4625 pceltFetched
= &objectFetched
;
4628 * Start the iteration, we will iterate until we hit the end of the
4629 * linked list or until we hit the number of items to iterate through
4633 while ( *pceltFetched
< celt
)
4635 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4637 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4641 * Read the entry from the storage.
4643 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
4648 * Copy the information to the return buffer.
4650 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
4651 currentReturnStruct
,
4656 * Step to the next item in the iteration
4659 currentReturnStruct
++;
4662 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
4669 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
4670 IEnumSTATSTG
* iface
,
4673 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4675 ULONG objectFetched
= 0;
4676 DirRef currentSearchNode
;
4679 if (This
->parentStorage
->reverted
)
4680 return STG_E_REVERTED
;
4682 while ( (objectFetched
< celt
) )
4684 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
4686 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
4692 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
4698 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
4699 IEnumSTATSTG
* iface
)
4701 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4703 if (This
->parentStorage
->reverted
)
4704 return STG_E_REVERTED
;
4711 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
4712 IEnumSTATSTG
* iface
,
4713 IEnumSTATSTG
** ppenum
)
4715 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
4717 IEnumSTATSTGImpl
* newClone
;
4719 if (This
->parentStorage
->reverted
)
4720 return STG_E_REVERTED
;
4723 * Perform a sanity check on the parameters.
4726 return E_INVALIDARG
;
4728 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
4729 This
->storageDirEntry
);
4733 * The new clone enumeration must point to the same current node as
4736 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
4738 *ppenum
= (IEnumSTATSTG
*)newClone
;
4741 * Don't forget to nail down a reference to the clone before
4744 IEnumSTATSTGImpl_AddRef(*ppenum
);
4750 * Virtual function table for the IEnumSTATSTGImpl class.
4752 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4754 IEnumSTATSTGImpl_QueryInterface
,
4755 IEnumSTATSTGImpl_AddRef
,
4756 IEnumSTATSTGImpl_Release
,
4757 IEnumSTATSTGImpl_Next
,
4758 IEnumSTATSTGImpl_Skip
,
4759 IEnumSTATSTGImpl_Reset
,
4760 IEnumSTATSTGImpl_Clone
4763 /******************************************************************************
4764 ** IEnumSTATSTGImpl implementation
4767 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4768 StorageBaseImpl
* parentStorage
,
4769 DirRef storageDirEntry
)
4771 IEnumSTATSTGImpl
* newEnumeration
;
4773 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4775 if (newEnumeration
!=0)
4778 * Set-up the virtual function table and reference count.
4780 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4781 newEnumeration
->ref
= 0;
4784 * We want to nail-down the reference to the storage in case the
4785 * enumeration out-lives the storage in the client application.
4787 newEnumeration
->parentStorage
= parentStorage
;
4788 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4790 newEnumeration
->storageDirEntry
= storageDirEntry
;
4793 * Make sure the current node of the iterator is the first one.
4795 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4798 return newEnumeration
;
4802 * Virtual function table for the Storage32InternalImpl class.
4804 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4806 StorageBaseImpl_QueryInterface
,
4807 StorageBaseImpl_AddRef
,
4808 StorageBaseImpl_Release
,
4809 StorageBaseImpl_CreateStream
,
4810 StorageBaseImpl_OpenStream
,
4811 StorageBaseImpl_CreateStorage
,
4812 StorageBaseImpl_OpenStorage
,
4813 StorageBaseImpl_CopyTo
,
4814 StorageBaseImpl_MoveElementTo
,
4815 StorageInternalImpl_Commit
,
4816 StorageInternalImpl_Revert
,
4817 StorageBaseImpl_EnumElements
,
4818 StorageBaseImpl_DestroyElement
,
4819 StorageBaseImpl_RenameElement
,
4820 StorageBaseImpl_SetElementTimes
,
4821 StorageBaseImpl_SetClass
,
4822 StorageBaseImpl_SetStateBits
,
4823 StorageBaseImpl_Stat
4826 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4828 StorageInternalImpl_Destroy
,
4829 StorageInternalImpl_Invalidate
,
4830 StorageInternalImpl_CreateDirEntry
,
4831 StorageInternalImpl_WriteDirEntry
,
4832 StorageInternalImpl_ReadDirEntry
,
4833 StorageInternalImpl_DestroyDirEntry
,
4834 StorageInternalImpl_StreamReadAt
,
4835 StorageInternalImpl_StreamWriteAt
,
4836 StorageInternalImpl_StreamSetSize
4839 /******************************************************************************
4840 ** Storage32InternalImpl implementation
4843 static StorageInternalImpl
* StorageInternalImpl_Construct(
4844 StorageBaseImpl
* parentStorage
,
4846 DirRef storageDirEntry
)
4848 StorageInternalImpl
* newStorage
;
4850 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4854 list_init(&newStorage
->base
.strmHead
);
4856 list_init(&newStorage
->base
.storageHead
);
4859 * Initialize the virtual function table.
4861 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4862 newStorage
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
4863 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4864 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4866 newStorage
->base
.reverted
= 0;
4868 newStorage
->base
.ref
= 1;
4870 newStorage
->parentStorage
= parentStorage
;
4873 * Keep a reference to the directory entry of this storage
4875 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4877 newStorage
->base
.create
= 0;
4885 /******************************************************************************
4886 ** StorageUtl implementation
4889 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4893 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4894 *value
= lendian16toh(tmp
);
4897 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4899 value
= htole16(value
);
4900 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4903 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4907 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4908 *value
= lendian32toh(tmp
);
4911 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4913 value
= htole32(value
);
4914 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4917 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4918 ULARGE_INTEGER
* value
)
4920 #ifdef WORDS_BIGENDIAN
4923 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4924 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4925 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4927 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4931 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4932 const ULARGE_INTEGER
*value
)
4934 #ifdef WORDS_BIGENDIAN
4937 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4938 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4939 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4941 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4945 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4947 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4948 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4949 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4951 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4954 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4956 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4957 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4958 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4960 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4963 void StorageUtl_CopyDirEntryToSTATSTG(
4964 StorageBaseImpl
* storage
,
4965 STATSTG
* destination
,
4966 const DirEntry
* source
,
4971 if (source
->stgType
== STGTY_ROOT
)
4973 /* replace the name of root entry (often "Root Entry") by the file name */
4974 entryName
= storage
->filename
;
4978 entryName
= source
->name
;
4982 * The copy of the string occurs only when the flag is not set
4984 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4985 (entryName
== NULL
) ||
4986 (entryName
[0] == 0) )
4988 destination
->pwcsName
= 0;
4992 destination
->pwcsName
=
4993 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4995 strcpyW(destination
->pwcsName
, entryName
);
4998 switch (source
->stgType
)
5002 destination
->type
= STGTY_STORAGE
;
5005 destination
->type
= STGTY_STREAM
;
5008 destination
->type
= STGTY_STREAM
;
5012 destination
->cbSize
= source
->size
;
5014 currentReturnStruct->mtime = {0}; TODO
5015 currentReturnStruct->ctime = {0};
5016 currentReturnStruct->atime = {0};
5018 destination
->grfMode
= 0;
5019 destination
->grfLocksSupported
= 0;
5020 destination
->clsid
= source
->clsid
;
5021 destination
->grfStateBits
= 0;
5022 destination
->reserved
= 0;
5025 /******************************************************************************
5026 ** BlockChainStream implementation
5029 /* Read and save the index of all blocks in this stream. */
5030 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
5032 ULONG next_sector
, next_offset
;
5034 struct BlockChainRun
*last_run
;
5036 if (This
->indexCacheLen
== 0)
5040 next_sector
= BlockChainStream_GetHeadOfChain(This
);
5044 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5045 next_offset
= last_run
->lastOffset
+1;
5046 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
5047 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
5049 if (FAILED(hr
)) return hr
;
5052 while (next_sector
!= BLOCK_END_OF_CHAIN
)
5054 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
5056 /* Add the current block to the cache. */
5057 if (This
->indexCacheSize
== 0)
5059 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
5060 if (!This
->indexCache
) return E_OUTOFMEMORY
;
5061 This
->indexCacheSize
= 16;
5063 else if (This
->indexCacheSize
== This
->indexCacheLen
)
5065 struct BlockChainRun
*new_cache
;
5068 new_size
= This
->indexCacheSize
* 2;
5069 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
5070 if (!new_cache
) return E_OUTOFMEMORY
;
5071 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
5073 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5074 This
->indexCache
= new_cache
;
5075 This
->indexCacheSize
= new_size
;
5078 This
->indexCacheLen
++;
5079 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5080 last_run
->firstSector
= next_sector
;
5081 last_run
->firstOffset
= next_offset
;
5084 last_run
->lastOffset
= next_offset
;
5086 /* Find the next block. */
5088 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
5089 if (FAILED(hr
)) return hr
;
5092 if (This
->indexCacheLen
)
5094 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
5095 This
->numBlocks
= last_run
->lastOffset
+1;
5099 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5100 This
->numBlocks
= 0;
5106 /* Locate the nth block in this stream. */
5107 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
5109 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
5110 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
5112 if (offset
>= This
->numBlocks
)
5113 return BLOCK_END_OF_CHAIN
;
5115 while (min_run
< max_run
)
5117 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
5118 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
5120 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
5121 max_run
= run_to_check
-1;
5123 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
5125 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
5126 min_run
= run_to_check
+1;
5129 /* Block is in this run. */
5130 min_run
= max_run
= run_to_check
;
5133 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
5136 BlockChainStream
* BlockChainStream_Construct(
5137 StorageImpl
* parentStorage
,
5138 ULONG
* headOfStreamPlaceHolder
,
5141 BlockChainStream
* newStream
;
5143 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
5145 newStream
->parentStorage
= parentStorage
;
5146 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5147 newStream
->ownerDirEntry
= dirEntry
;
5148 newStream
->indexCache
= NULL
;
5149 newStream
->indexCacheLen
= 0;
5150 newStream
->indexCacheSize
= 0;
5152 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
5154 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
5155 HeapFree(GetProcessHeap(), 0, newStream
);
5162 void BlockChainStream_Destroy(BlockChainStream
* This
)
5165 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
5166 HeapFree(GetProcessHeap(), 0, This
);
5169 /******************************************************************************
5170 * BlockChainStream_GetHeadOfChain
5172 * Returns the head of this stream chain.
5173 * Some special chains don't have directory entries, their heads are kept in
5174 * This->headOfStreamPlaceHolder.
5177 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
5179 DirEntry chainEntry
;
5182 if (This
->headOfStreamPlaceHolder
!= 0)
5183 return *(This
->headOfStreamPlaceHolder
);
5185 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
5187 hr
= StorageImpl_ReadDirEntry(
5188 This
->parentStorage
,
5189 This
->ownerDirEntry
,
5194 return chainEntry
.startingBlock
;
5198 return BLOCK_END_OF_CHAIN
;
5201 /******************************************************************************
5202 * BlockChainStream_GetCount
5204 * Returns the number of blocks that comprises this chain.
5205 * This is not the size of the stream as the last block may not be full!
5207 * FIXME: Use the cache to get this information.
5209 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
5214 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5216 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5220 if(FAILED(StorageImpl_GetNextBlockInChain(
5221 This
->parentStorage
,
5230 /******************************************************************************
5231 * BlockChainStream_ReadAt
5233 * Reads a specified number of bytes from this chain at the specified offset.
5234 * bytesRead may be NULL.
5235 * Failure will be returned if the specified number of bytes has not been read.
5237 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
5238 ULARGE_INTEGER offset
,
5243 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5244 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5245 ULONG bytesToReadInBuffer
;
5249 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
5252 * Find the first block in the stream that contains part of the buffer.
5254 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5256 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5257 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
5260 * Start reading the buffer.
5263 bufferWalker
= buffer
;
5265 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5267 ULARGE_INTEGER ulOffset
;
5270 * Calculate how many bytes we can copy from this big block.
5272 bytesToReadInBuffer
=
5273 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5275 TRACE("block %i\n",blockIndex
);
5276 ulOffset
.u
.HighPart
= 0;
5277 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5280 StorageImpl_ReadAt(This
->parentStorage
,
5283 bytesToReadInBuffer
,
5286 * Step to the next big block.
5288 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
5289 return STG_E_DOCFILECORRUPT
;
5291 bufferWalker
+= bytesReadAt
;
5292 size
-= bytesReadAt
;
5293 *bytesRead
+= bytesReadAt
;
5294 offsetInBlock
= 0; /* There is no offset on the next block */
5296 if (bytesToReadInBuffer
!= bytesReadAt
)
5300 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5303 /******************************************************************************
5304 * BlockChainStream_WriteAt
5306 * Writes the specified number of bytes to this chain at the specified offset.
5307 * Will fail if not all specified number of bytes have been written.
5309 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
5310 ULARGE_INTEGER offset
,
5313 ULONG
* bytesWritten
)
5315 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5316 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
5319 const BYTE
* bufferWalker
;
5322 * Find the first block in the stream that contains part of the buffer.
5324 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
5326 /* BlockChainStream_SetSize should have already been called to ensure we have
5327 * enough blocks in the chain to write into */
5328 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5330 ERR("not enough blocks in chain to write data\n");
5331 return STG_E_DOCFILECORRUPT
;
5335 bufferWalker
= buffer
;
5337 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5339 ULARGE_INTEGER ulOffset
;
5340 DWORD bytesWrittenAt
;
5342 * Calculate how many bytes we can copy from this big block.
5345 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
5347 TRACE("block %i\n",blockIndex
);
5348 ulOffset
.u
.HighPart
= 0;
5349 ulOffset
.u
.LowPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
5352 StorageImpl_WriteAt(This
->parentStorage
,
5359 * Step to the next big block.
5361 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
5363 return STG_E_DOCFILECORRUPT
;
5365 bufferWalker
+= bytesWrittenAt
;
5366 size
-= bytesWrittenAt
;
5367 *bytesWritten
+= bytesWrittenAt
;
5368 offsetInBlock
= 0; /* There is no offset on the next block */
5370 if (bytesWrittenAt
!= bytesToWrite
)
5374 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5377 /******************************************************************************
5378 * BlockChainStream_Shrink
5380 * Shrinks this chain in the big block depot.
5382 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
5383 ULARGE_INTEGER newSize
)
5389 * Figure out how many blocks are needed to contain the new size
5391 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5393 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5399 * Go to the new end of chain
5401 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
5403 /* Mark the new end of chain */
5404 StorageImpl_SetNextBlockInChain(
5405 This
->parentStorage
,
5407 BLOCK_END_OF_CHAIN
);
5409 This
->tailIndex
= blockIndex
;
5413 if (This
->headOfStreamPlaceHolder
!= 0)
5415 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
5419 DirEntry chainEntry
;
5420 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5422 StorageImpl_ReadDirEntry(
5423 This
->parentStorage
,
5424 This
->ownerDirEntry
,
5427 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
5429 StorageImpl_WriteDirEntry(
5430 This
->parentStorage
,
5431 This
->ownerDirEntry
,
5435 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
5438 This
->numBlocks
= numBlocks
;
5441 * Mark the extra blocks as free
5443 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
5445 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
5446 StorageImpl_FreeBigBlock(This
->parentStorage
,
5447 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
5448 if (last_run
->lastOffset
== last_run
->firstOffset
)
5449 This
->indexCacheLen
--;
5451 last_run
->lastOffset
--;
5457 /******************************************************************************
5458 * BlockChainStream_Enlarge
5460 * Grows this chain in the big block depot.
5462 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
5463 ULARGE_INTEGER newSize
)
5465 ULONG blockIndex
, currentBlock
;
5467 ULONG oldNumBlocks
= 0;
5469 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
5472 * Empty chain. Create the head.
5474 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5476 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5477 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
5479 BLOCK_END_OF_CHAIN
);
5481 if (This
->headOfStreamPlaceHolder
!= 0)
5483 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5487 DirEntry chainEntry
;
5488 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
5490 StorageImpl_ReadDirEntry(
5491 This
->parentStorage
,
5492 This
->ownerDirEntry
,
5495 chainEntry
.startingBlock
= blockIndex
;
5497 StorageImpl_WriteDirEntry(
5498 This
->parentStorage
,
5499 This
->ownerDirEntry
,
5503 This
->tailIndex
= blockIndex
;
5504 This
->numBlocks
= 1;
5508 * Figure out how many blocks are needed to contain this stream
5510 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
5512 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
5516 * Go to the current end of chain
5518 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
5520 currentBlock
= blockIndex
;
5522 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5525 currentBlock
= blockIndex
;
5527 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
5532 This
->tailIndex
= currentBlock
;
5535 currentBlock
= This
->tailIndex
;
5536 oldNumBlocks
= This
->numBlocks
;
5539 * Add new blocks to the chain
5541 if (oldNumBlocks
< newNumBlocks
)
5543 while (oldNumBlocks
< newNumBlocks
)
5545 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5547 StorageImpl_SetNextBlockInChain(
5548 This
->parentStorage
,
5552 StorageImpl_SetNextBlockInChain(
5553 This
->parentStorage
,
5555 BLOCK_END_OF_CHAIN
);
5557 currentBlock
= blockIndex
;
5561 This
->tailIndex
= blockIndex
;
5562 This
->numBlocks
= newNumBlocks
;
5565 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
5571 /******************************************************************************
5572 * BlockChainStream_SetSize
5574 * Sets the size of this stream. The big block depot will be updated.
5575 * The file will grow if we grow the chain.
5577 * TODO: Free the actual blocks in the file when we shrink the chain.
5578 * Currently, the blocks are still in the file. So the file size
5579 * doesn't shrink even if we shrink streams.
5581 BOOL
BlockChainStream_SetSize(
5582 BlockChainStream
* This
,
5583 ULARGE_INTEGER newSize
)
5585 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
5587 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5590 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5592 BlockChainStream_Shrink(This
, newSize
);
5596 BlockChainStream_Enlarge(This
, newSize
);
5602 /******************************************************************************
5603 * BlockChainStream_GetSize
5605 * Returns the size of this chain.
5606 * Will return the block count if this chain doesn't have a directory entry.
5608 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
5610 DirEntry chainEntry
;
5612 if(This
->headOfStreamPlaceHolder
== NULL
)
5615 * This chain has a directory entry so use the size value from there.
5617 StorageImpl_ReadDirEntry(
5618 This
->parentStorage
,
5619 This
->ownerDirEntry
,
5622 return chainEntry
.size
;
5627 * this chain is a chain that does not have a directory entry, figure out the
5628 * size by making the product number of used blocks times the
5631 ULARGE_INTEGER result
;
5632 result
.u
.HighPart
= 0;
5635 BlockChainStream_GetCount(This
) *
5636 This
->parentStorage
->bigBlockSize
;
5642 /******************************************************************************
5643 ** SmallBlockChainStream implementation
5646 SmallBlockChainStream
* SmallBlockChainStream_Construct(
5647 StorageImpl
* parentStorage
,
5648 ULONG
* headOfStreamPlaceHolder
,
5651 SmallBlockChainStream
* newStream
;
5653 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
5655 newStream
->parentStorage
= parentStorage
;
5656 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
5657 newStream
->ownerDirEntry
= dirEntry
;
5662 void SmallBlockChainStream_Destroy(
5663 SmallBlockChainStream
* This
)
5665 HeapFree(GetProcessHeap(), 0, This
);
5668 /******************************************************************************
5669 * SmallBlockChainStream_GetHeadOfChain
5671 * Returns the head of this chain of small blocks.
5673 static ULONG
SmallBlockChainStream_GetHeadOfChain(
5674 SmallBlockChainStream
* This
)
5676 DirEntry chainEntry
;
5679 if (This
->headOfStreamPlaceHolder
!= NULL
)
5680 return *(This
->headOfStreamPlaceHolder
);
5682 if (This
->ownerDirEntry
)
5684 hr
= StorageImpl_ReadDirEntry(
5685 This
->parentStorage
,
5686 This
->ownerDirEntry
,
5691 return chainEntry
.startingBlock
;
5696 return BLOCK_END_OF_CHAIN
;
5699 /******************************************************************************
5700 * SmallBlockChainStream_GetNextBlockInChain
5702 * Returns the index of the next small block in this chain.
5705 * - BLOCK_END_OF_CHAIN: end of this chain
5706 * - BLOCK_UNUSED: small block 'blockIndex' is free
5708 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5709 SmallBlockChainStream
* This
,
5711 ULONG
* nextBlockInChain
)
5713 ULARGE_INTEGER offsetOfBlockInDepot
;
5718 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5720 offsetOfBlockInDepot
.u
.HighPart
= 0;
5721 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5724 * Read those bytes in the buffer from the small block file.
5726 res
= BlockChainStream_ReadAt(
5727 This
->parentStorage
->smallBlockDepotChain
,
5728 offsetOfBlockInDepot
,
5735 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5742 /******************************************************************************
5743 * SmallBlockChainStream_SetNextBlockInChain
5745 * Writes the index of the next block of the specified block in the small
5747 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5748 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5750 static void SmallBlockChainStream_SetNextBlockInChain(
5751 SmallBlockChainStream
* This
,
5755 ULARGE_INTEGER offsetOfBlockInDepot
;
5759 offsetOfBlockInDepot
.u
.HighPart
= 0;
5760 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5762 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5765 * Read those bytes in the buffer from the small block file.
5767 BlockChainStream_WriteAt(
5768 This
->parentStorage
->smallBlockDepotChain
,
5769 offsetOfBlockInDepot
,
5775 /******************************************************************************
5776 * SmallBlockChainStream_FreeBlock
5778 * Flag small block 'blockIndex' as free in the small block depot.
5780 static void SmallBlockChainStream_FreeBlock(
5781 SmallBlockChainStream
* This
,
5784 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5787 /******************************************************************************
5788 * SmallBlockChainStream_GetNextFreeBlock
5790 * Returns the index of a free small block. The small block depot will be
5791 * enlarged if necessary. The small block chain will also be enlarged if
5794 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5795 SmallBlockChainStream
* This
)
5797 ULARGE_INTEGER offsetOfBlockInDepot
;
5800 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
5801 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5803 ULONG smallBlocksPerBigBlock
;
5805 offsetOfBlockInDepot
.u
.HighPart
= 0;
5808 * Scan the small block depot for a free block
5810 while (nextBlockIndex
!= BLOCK_UNUSED
)
5812 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5814 res
= BlockChainStream_ReadAt(
5815 This
->parentStorage
->smallBlockDepotChain
,
5816 offsetOfBlockInDepot
,
5822 * If we run out of space for the small block depot, enlarge it
5826 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5828 if (nextBlockIndex
!= BLOCK_UNUSED
)
5834 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5836 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
5837 ULARGE_INTEGER newSize
, offset
;
5840 newSize
.QuadPart
= (count
+ 1) * This
->parentStorage
->bigBlockSize
;
5841 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
5844 * Initialize all the small blocks to free
5846 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5847 offset
.QuadPart
= count
* This
->parentStorage
->bigBlockSize
;
5848 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
5849 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
5851 StorageImpl_SaveFileHeader(This
->parentStorage
);
5855 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
5857 smallBlocksPerBigBlock
=
5858 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5861 * Verify if we have to allocate big blocks to contain small blocks
5863 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5866 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5867 ULARGE_INTEGER old_size
, size_required
;
5869 size_required
.QuadPart
= blocksRequired
* This
->parentStorage
->bigBlockSize
;
5871 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
5873 if (size_required
.QuadPart
> old_size
.QuadPart
)
5875 BlockChainStream_SetSize(
5876 This
->parentStorage
->smallBlockRootChain
,
5879 StorageImpl_ReadDirEntry(
5880 This
->parentStorage
,
5881 This
->parentStorage
->base
.storageDirEntry
,
5884 rootEntry
.size
= size_required
;
5886 StorageImpl_WriteDirEntry(
5887 This
->parentStorage
,
5888 This
->parentStorage
->base
.storageDirEntry
,
5896 /******************************************************************************
5897 * SmallBlockChainStream_ReadAt
5899 * Reads a specified number of bytes from this chain at the specified offset.
5900 * bytesRead may be NULL.
5901 * Failure will be returned if the specified number of bytes has not been read.
5903 HRESULT
SmallBlockChainStream_ReadAt(
5904 SmallBlockChainStream
* This
,
5905 ULARGE_INTEGER offset
,
5911 ULARGE_INTEGER offsetInBigBlockFile
;
5912 ULONG blockNoInSequence
=
5913 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5915 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5916 ULONG bytesToReadInBuffer
;
5918 ULONG bytesReadFromBigBlockFile
;
5922 * This should never happen on a small block file.
5924 assert(offset
.u
.HighPart
==0);
5927 * Find the first block in the stream that contains part of the buffer.
5929 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5931 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5933 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5936 blockNoInSequence
--;
5940 * Start reading the buffer.
5943 bufferWalker
= buffer
;
5945 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5948 * Calculate how many bytes we can copy from this small block.
5950 bytesToReadInBuffer
=
5951 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5954 * Calculate the offset of the small block in the small block file.
5956 offsetInBigBlockFile
.u
.HighPart
= 0;
5957 offsetInBigBlockFile
.u
.LowPart
=
5958 blockIndex
* This
->parentStorage
->smallBlockSize
;
5960 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5963 * Read those bytes in the buffer from the small block file.
5964 * The small block has already been identified so it shouldn't fail
5965 * unless the file is corrupt.
5967 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5968 offsetInBigBlockFile
,
5969 bytesToReadInBuffer
,
5971 &bytesReadFromBigBlockFile
);
5977 * Step to the next big block.
5979 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5981 return STG_E_DOCFILECORRUPT
;
5983 bufferWalker
+= bytesReadFromBigBlockFile
;
5984 size
-= bytesReadFromBigBlockFile
;
5985 *bytesRead
+= bytesReadFromBigBlockFile
;
5986 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5989 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5992 /******************************************************************************
5993 * SmallBlockChainStream_WriteAt
5995 * Writes the specified number of bytes to this chain at the specified offset.
5996 * Will fail if not all specified number of bytes have been written.
5998 HRESULT
SmallBlockChainStream_WriteAt(
5999 SmallBlockChainStream
* This
,
6000 ULARGE_INTEGER offset
,
6003 ULONG
* bytesWritten
)
6005 ULARGE_INTEGER offsetInBigBlockFile
;
6006 ULONG blockNoInSequence
=
6007 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6009 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
6010 ULONG bytesToWriteInBuffer
;
6012 ULONG bytesWrittenToBigBlockFile
;
6013 const BYTE
* bufferWalker
;
6017 * This should never happen on a small block file.
6019 assert(offset
.u
.HighPart
==0);
6022 * Find the first block in the stream that contains part of the buffer.
6024 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6026 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
6028 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
6029 return STG_E_DOCFILECORRUPT
;
6030 blockNoInSequence
--;
6034 * Start writing the buffer.
6037 bufferWalker
= buffer
;
6038 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
6041 * Calculate how many bytes we can copy to this small block.
6043 bytesToWriteInBuffer
=
6044 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
6047 * Calculate the offset of the small block in the small block file.
6049 offsetInBigBlockFile
.u
.HighPart
= 0;
6050 offsetInBigBlockFile
.u
.LowPart
=
6051 blockIndex
* This
->parentStorage
->smallBlockSize
;
6053 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
6056 * Write those bytes in the buffer to the small block file.
6058 res
= BlockChainStream_WriteAt(
6059 This
->parentStorage
->smallBlockRootChain
,
6060 offsetInBigBlockFile
,
6061 bytesToWriteInBuffer
,
6063 &bytesWrittenToBigBlockFile
);
6068 * Step to the next big block.
6070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6073 bufferWalker
+= bytesWrittenToBigBlockFile
;
6074 size
-= bytesWrittenToBigBlockFile
;
6075 *bytesWritten
+= bytesWrittenToBigBlockFile
;
6076 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
6079 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
6082 /******************************************************************************
6083 * SmallBlockChainStream_Shrink
6085 * Shrinks this chain in the small block depot.
6087 static BOOL
SmallBlockChainStream_Shrink(
6088 SmallBlockChainStream
* This
,
6089 ULARGE_INTEGER newSize
)
6091 ULONG blockIndex
, extraBlock
;
6095 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6097 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6100 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6103 * Go to the new end of chain
6105 while (count
< numBlocks
)
6107 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6114 * If the count is 0, we have a special case, the head of the chain was
6119 DirEntry chainEntry
;
6121 StorageImpl_ReadDirEntry(This
->parentStorage
,
6122 This
->ownerDirEntry
,
6125 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
6127 StorageImpl_WriteDirEntry(This
->parentStorage
,
6128 This
->ownerDirEntry
,
6132 * We start freeing the chain at the head block.
6134 extraBlock
= blockIndex
;
6138 /* Get the next block before marking the new end */
6139 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
6143 /* Mark the new end of chain */
6144 SmallBlockChainStream_SetNextBlockInChain(
6147 BLOCK_END_OF_CHAIN
);
6151 * Mark the extra blocks as free
6153 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
6155 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
6158 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
6159 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
6160 extraBlock
= blockIndex
;
6166 /******************************************************************************
6167 * SmallBlockChainStream_Enlarge
6169 * Grows this chain in the small block depot.
6171 static BOOL
SmallBlockChainStream_Enlarge(
6172 SmallBlockChainStream
* This
,
6173 ULARGE_INTEGER newSize
)
6175 ULONG blockIndex
, currentBlock
;
6177 ULONG oldNumBlocks
= 0;
6179 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6182 * Empty chain. Create the head.
6184 if (blockIndex
== BLOCK_END_OF_CHAIN
)
6186 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6187 SmallBlockChainStream_SetNextBlockInChain(
6190 BLOCK_END_OF_CHAIN
);
6192 if (This
->headOfStreamPlaceHolder
!= NULL
)
6194 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
6198 DirEntry chainEntry
;
6200 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6203 chainEntry
.startingBlock
= blockIndex
;
6205 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
6210 currentBlock
= blockIndex
;
6213 * Figure out how many blocks are needed to contain this stream
6215 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
6217 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
6221 * Go to the current end of chain
6223 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
6226 currentBlock
= blockIndex
;
6227 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
6232 * Add new blocks to the chain
6234 while (oldNumBlocks
< newNumBlocks
)
6236 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
6237 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
6239 SmallBlockChainStream_SetNextBlockInChain(
6242 BLOCK_END_OF_CHAIN
);
6244 currentBlock
= blockIndex
;
6251 /******************************************************************************
6252 * SmallBlockChainStream_SetSize
6254 * Sets the size of this stream.
6255 * The file will grow if we grow the chain.
6257 * TODO: Free the actual blocks in the file when we shrink the chain.
6258 * Currently, the blocks are still in the file. So the file size
6259 * doesn't shrink even if we shrink streams.
6261 BOOL
SmallBlockChainStream_SetSize(
6262 SmallBlockChainStream
* This
,
6263 ULARGE_INTEGER newSize
)
6265 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
6267 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
6270 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
6272 SmallBlockChainStream_Shrink(This
, newSize
);
6276 SmallBlockChainStream_Enlarge(This
, newSize
);
6282 /******************************************************************************
6283 * SmallBlockChainStream_GetCount
6285 * Returns the number of small blocks that comprises this chain.
6286 * This is not the size of the stream as the last block may not be full!
6289 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
6294 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
6296 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
6300 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
6301 blockIndex
, &blockIndex
)))
6308 /******************************************************************************
6309 * SmallBlockChainStream_GetSize
6311 * Returns the size of this chain.
6313 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
6315 DirEntry chainEntry
;
6317 if(This
->headOfStreamPlaceHolder
!= NULL
)
6319 ULARGE_INTEGER result
;
6320 result
.u
.HighPart
= 0;
6322 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
6323 This
->parentStorage
->smallBlockSize
;
6328 StorageImpl_ReadDirEntry(
6329 This
->parentStorage
,
6330 This
->ownerDirEntry
,
6333 return chainEntry
.size
;
6336 static HRESULT
create_storagefile(
6340 STGOPTIONS
* pStgOptions
,
6344 StorageBaseImpl
* newStorage
= 0;
6345 HANDLE hFile
= INVALID_HANDLE_VALUE
;
6346 HRESULT hr
= STG_E_INVALIDFLAG
;
6350 DWORD fileAttributes
;
6351 WCHAR tempFileName
[MAX_PATH
];
6354 return STG_E_INVALIDPOINTER
;
6356 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
6357 return STG_E_INVALIDPARAMETER
;
6359 /* if no share mode given then DENY_NONE is the default */
6360 if (STGM_SHARE_MODE(grfMode
) == 0)
6361 grfMode
|= STGM_SHARE_DENY_NONE
;
6363 if ( FAILED( validateSTGM(grfMode
) ))
6366 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6367 switch(STGM_ACCESS_MODE(grfMode
))
6370 case STGM_READWRITE
:
6376 /* in direct mode, can only use SHARE_EXCLUSIVE */
6377 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
6380 /* but in transacted mode, any share mode is valid */
6383 * Generate a unique name.
6387 WCHAR tempPath
[MAX_PATH
];
6388 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
6390 memset(tempPath
, 0, sizeof(tempPath
));
6391 memset(tempFileName
, 0, sizeof(tempFileName
));
6393 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
6396 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
6397 pwcsName
= tempFileName
;
6400 hr
= STG_E_INSUFFICIENTMEMORY
;
6404 creationMode
= TRUNCATE_EXISTING
;
6408 creationMode
= GetCreationModeFromSTGM(grfMode
);
6412 * Interpret the STGM value grfMode
6414 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6415 accessMode
= GetAccessModeFromSTGM(grfMode
);
6417 if (grfMode
& STGM_DELETEONRELEASE
)
6418 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
6420 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
6422 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
6426 FIXME("Storage share mode not implemented.\n");
6431 hFile
= CreateFileW(pwcsName
,
6439 if (hFile
== INVALID_HANDLE_VALUE
)
6441 if(GetLastError() == ERROR_FILE_EXISTS
)
6442 hr
= STG_E_FILEALREADYEXISTS
;
6449 * Allocate and initialize the new IStorage32object.
6451 hr
= Storage_Construct(
6458 pStgOptions
->ulSectorSize
,
6466 hr
= IStorage_QueryInterface((IStorage
*)newStorage
, riid
, ppstgOpen
);
6468 IStorage_Release((IStorage
*)newStorage
);
6471 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
6476 /******************************************************************************
6477 * StgCreateDocfile [OLE32.@]
6478 * Creates a new compound file storage object
6481 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6482 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6483 * reserved [ ?] unused?, usually 0
6484 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6487 * S_OK if the file was successfully created
6488 * some STG_E_ value if error
6490 * if pwcsName is NULL, create file with new unique name
6491 * the function can returns
6492 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6495 HRESULT WINAPI
StgCreateDocfile(
6499 IStorage
**ppstgOpen
)
6501 STGOPTIONS stgoptions
= {1, 0, 512};
6503 TRACE("(%s, %x, %d, %p)\n",
6504 debugstr_w(pwcsName
), grfMode
,
6505 reserved
, ppstgOpen
);
6508 return STG_E_INVALIDPOINTER
;
6510 return STG_E_INVALIDPARAMETER
;
6512 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
6515 /******************************************************************************
6516 * StgCreateStorageEx [OLE32.@]
6518 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6520 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6521 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6523 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
6525 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6526 return STG_E_INVALIDPARAMETER
;
6529 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6531 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6532 return STG_E_INVALIDPARAMETER
;
6535 if (stgfmt
== STGFMT_FILE
)
6537 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6538 return STG_E_INVALIDPARAMETER
;
6541 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
6543 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
6547 ERR("Invalid stgfmt argument\n");
6548 return STG_E_INVALIDPARAMETER
;
6551 /******************************************************************************
6552 * StgCreatePropSetStg [OLE32.@]
6554 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
6555 IPropertySetStorage
**ppPropSetStg
)
6559 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
6561 hr
= STG_E_INVALIDPARAMETER
;
6563 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
6564 (void**)ppPropSetStg
);
6568 /******************************************************************************
6569 * StgOpenStorageEx [OLE32.@]
6571 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
6573 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
6574 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
6576 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
6578 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6579 return STG_E_INVALIDPARAMETER
;
6585 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6586 return STG_E_INVALIDPARAMETER
;
6588 case STGFMT_STORAGE
:
6591 case STGFMT_DOCFILE
:
6592 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
6594 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6595 return STG_E_INVALIDPARAMETER
;
6597 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6601 WARN("STGFMT_ANY assuming storage\n");
6605 return STG_E_INVALIDPARAMETER
;
6608 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
6612 /******************************************************************************
6613 * StgOpenStorage [OLE32.@]
6615 HRESULT WINAPI
StgOpenStorage(
6616 const OLECHAR
*pwcsName
,
6617 IStorage
*pstgPriority
,
6621 IStorage
**ppstgOpen
)
6623 StorageBaseImpl
* newStorage
= 0;
6629 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6630 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
6631 snbExclude
, reserved
, ppstgOpen
);
6635 hr
= STG_E_INVALIDNAME
;
6641 hr
= STG_E_INVALIDPOINTER
;
6647 hr
= STG_E_INVALIDPARAMETER
;
6651 if (grfMode
& STGM_PRIORITY
)
6653 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
6654 return STG_E_INVALIDFLAG
;
6655 if (grfMode
& STGM_DELETEONRELEASE
)
6656 return STG_E_INVALIDFUNCTION
;
6657 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
6658 return STG_E_INVALIDFLAG
;
6659 grfMode
&= ~0xf0; /* remove the existing sharing mode */
6660 grfMode
|= STGM_SHARE_DENY_NONE
;
6662 /* STGM_PRIORITY stops other IStorage objects on the same file from
6663 * committing until the STGM_PRIORITY IStorage is closed. it also
6664 * stops non-transacted mode StgOpenStorage calls with write access from
6665 * succeeding. obviously, both of these cannot be achieved through just
6666 * file share flags */
6667 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6671 * Validate the sharing mode
6673 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
6674 switch(STGM_SHARE_MODE(grfMode
))
6676 case STGM_SHARE_EXCLUSIVE
:
6677 case STGM_SHARE_DENY_WRITE
:
6680 hr
= STG_E_INVALIDFLAG
;
6684 if ( FAILED( validateSTGM(grfMode
) ) ||
6685 (grfMode
&STGM_CREATE
))
6687 hr
= STG_E_INVALIDFLAG
;
6691 /* shared reading requires transacted mode */
6692 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6693 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6694 !(grfMode
&STGM_TRANSACTED
) )
6696 hr
= STG_E_INVALIDFLAG
;
6701 * Interpret the STGM value grfMode
6703 shareMode
= GetShareModeFromSTGM(grfMode
);
6704 accessMode
= GetAccessModeFromSTGM(grfMode
);
6708 hFile
= CreateFileW( pwcsName
,
6713 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6716 if (hFile
==INVALID_HANDLE_VALUE
)
6718 DWORD last_error
= GetLastError();
6724 case ERROR_FILE_NOT_FOUND
:
6725 hr
= STG_E_FILENOTFOUND
;
6728 case ERROR_PATH_NOT_FOUND
:
6729 hr
= STG_E_PATHNOTFOUND
;
6732 case ERROR_ACCESS_DENIED
:
6733 case ERROR_WRITE_PROTECT
:
6734 hr
= STG_E_ACCESSDENIED
;
6737 case ERROR_SHARING_VIOLATION
:
6738 hr
= STG_E_SHAREVIOLATION
;
6749 * Refuse to open the file if it's too small to be a structured storage file
6750 * FIXME: verify the file when reading instead of here
6752 if (GetFileSize(hFile
, NULL
) < 0x100)
6755 hr
= STG_E_FILEALREADYEXISTS
;
6760 * Allocate and initialize the new IStorage32object.
6762 hr
= Storage_Construct(
6775 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6777 if(hr
== STG_E_INVALIDHEADER
)
6778 hr
= STG_E_FILEALREADYEXISTS
;
6783 * Get an "out" pointer for the caller.
6785 *ppstgOpen
= (IStorage
*)newStorage
;
6788 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6792 /******************************************************************************
6793 * StgCreateDocfileOnILockBytes [OLE32.@]
6795 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6799 IStorage
** ppstgOpen
)
6801 StorageBaseImpl
* newStorage
= 0;
6804 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6805 return STG_E_INVALIDPOINTER
;
6808 * Allocate and initialize the new IStorage object.
6810 hr
= Storage_Construct(
6826 * Get an "out" pointer for the caller.
6828 *ppstgOpen
= (IStorage
*)newStorage
;
6833 /******************************************************************************
6834 * StgOpenStorageOnILockBytes [OLE32.@]
6836 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6838 IStorage
*pstgPriority
,
6842 IStorage
**ppstgOpen
)
6844 StorageBaseImpl
* newStorage
= 0;
6847 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6848 return STG_E_INVALIDPOINTER
;
6850 if ( FAILED( validateSTGM(grfMode
) ))
6851 return STG_E_INVALIDFLAG
;
6856 * Allocate and initialize the new IStorage object.
6858 hr
= Storage_Construct(
6874 * Get an "out" pointer for the caller.
6876 *ppstgOpen
= (IStorage
*)newStorage
;
6881 /******************************************************************************
6882 * StgSetTimes [ole32.@]
6883 * StgSetTimes [OLE32.@]
6887 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6888 FILETIME
const *patime
, FILETIME
const *pmtime
)
6890 IStorage
*stg
= NULL
;
6893 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6895 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6899 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6900 IStorage_Release(stg
);
6906 /******************************************************************************
6907 * StgIsStorageILockBytes [OLE32.@]
6909 * Determines if the ILockBytes contains a storage object.
6911 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6914 ULARGE_INTEGER offset
;
6916 offset
.u
.HighPart
= 0;
6917 offset
.u
.LowPart
= 0;
6919 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6921 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6927 /******************************************************************************
6928 * WriteClassStg [OLE32.@]
6930 * This method will store the specified CLSID in the specified storage object
6932 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6937 return E_INVALIDARG
;
6940 return STG_E_INVALIDPOINTER
;
6942 hRes
= IStorage_SetClass(pStg
, rclsid
);
6947 /***********************************************************************
6948 * ReadClassStg (OLE32.@)
6950 * This method reads the CLSID previously written to a storage object with
6951 * the WriteClassStg.
6954 * pstg [I] IStorage pointer
6955 * pclsid [O] Pointer to where the CLSID is written
6959 * Failure: HRESULT code.
6961 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6966 TRACE("(%p, %p)\n", pstg
, pclsid
);
6968 if(!pstg
|| !pclsid
)
6969 return E_INVALIDARG
;
6972 * read a STATSTG structure (contains the clsid) from the storage
6974 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6977 *pclsid
=pstatstg
.clsid
;
6982 /***********************************************************************
6983 * OleLoadFromStream (OLE32.@)
6985 * This function loads an object from stream
6987 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6991 LPPERSISTSTREAM xstm
;
6993 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6995 res
=ReadClassStm(pStm
,&clsid
);
6998 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
7001 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
7003 IUnknown_Release((IUnknown
*)*ppvObj
);
7006 res
=IPersistStream_Load(xstm
,pStm
);
7007 IPersistStream_Release(xstm
);
7008 /* FIXME: all refcounts ok at this point? I think they should be:
7011 * xstm : 0 (released)
7016 /***********************************************************************
7017 * OleSaveToStream (OLE32.@)
7019 * This function saves an object with the IPersistStream interface on it
7020 * to the specified stream.
7022 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
7028 TRACE("(%p,%p)\n",pPStm
,pStm
);
7030 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
7032 if (SUCCEEDED(res
)){
7034 res
=WriteClassStm(pStm
,&clsid
);
7038 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
7041 TRACE("Finished Save\n");
7045 /****************************************************************************
7046 * This method validate a STGM parameter that can contain the values below
7048 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7049 * The stgm values contained in 0xffff0000 are bitmasks.
7051 * STGM_DIRECT 0x00000000
7052 * STGM_TRANSACTED 0x00010000
7053 * STGM_SIMPLE 0x08000000
7055 * STGM_READ 0x00000000
7056 * STGM_WRITE 0x00000001
7057 * STGM_READWRITE 0x00000002
7059 * STGM_SHARE_DENY_NONE 0x00000040
7060 * STGM_SHARE_DENY_READ 0x00000030
7061 * STGM_SHARE_DENY_WRITE 0x00000020
7062 * STGM_SHARE_EXCLUSIVE 0x00000010
7064 * STGM_PRIORITY 0x00040000
7065 * STGM_DELETEONRELEASE 0x04000000
7067 * STGM_CREATE 0x00001000
7068 * STGM_CONVERT 0x00020000
7069 * STGM_FAILIFTHERE 0x00000000
7071 * STGM_NOSCRATCH 0x00100000
7072 * STGM_NOSNAPSHOT 0x00200000
7074 static HRESULT
validateSTGM(DWORD stgm
)
7076 DWORD access
= STGM_ACCESS_MODE(stgm
);
7077 DWORD share
= STGM_SHARE_MODE(stgm
);
7078 DWORD create
= STGM_CREATE_MODE(stgm
);
7080 if (stgm
&~STGM_KNOWN_FLAGS
)
7082 ERR("unknown flags %08x\n", stgm
);
7090 case STGM_READWRITE
:
7098 case STGM_SHARE_DENY_NONE
:
7099 case STGM_SHARE_DENY_READ
:
7100 case STGM_SHARE_DENY_WRITE
:
7101 case STGM_SHARE_EXCLUSIVE
:
7110 case STGM_FAILIFTHERE
:
7117 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7119 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
7123 * STGM_CREATE | STGM_CONVERT
7124 * if both are false, STGM_FAILIFTHERE is set to TRUE
7126 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
7130 * STGM_NOSCRATCH requires STGM_TRANSACTED
7132 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
7136 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7137 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7139 if ( (stgm
& STGM_NOSNAPSHOT
) &&
7140 (!(stgm
& STGM_TRANSACTED
) ||
7141 share
== STGM_SHARE_EXCLUSIVE
||
7142 share
== STGM_SHARE_DENY_WRITE
) )
7148 /****************************************************************************
7149 * GetShareModeFromSTGM
7151 * This method will return a share mode flag from a STGM value.
7152 * The STGM value is assumed valid.
7154 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
7156 switch (STGM_SHARE_MODE(stgm
))
7158 case STGM_SHARE_DENY_NONE
:
7159 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
7160 case STGM_SHARE_DENY_READ
:
7161 return FILE_SHARE_WRITE
;
7162 case STGM_SHARE_DENY_WRITE
:
7163 return FILE_SHARE_READ
;
7164 case STGM_SHARE_EXCLUSIVE
:
7167 ERR("Invalid share mode!\n");
7172 /****************************************************************************
7173 * GetAccessModeFromSTGM
7175 * This method will return an access mode flag from a STGM value.
7176 * The STGM value is assumed valid.
7178 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
7180 switch (STGM_ACCESS_MODE(stgm
))
7183 return GENERIC_READ
;
7185 case STGM_READWRITE
:
7186 return GENERIC_READ
| GENERIC_WRITE
;
7188 ERR("Invalid access mode!\n");
7193 /****************************************************************************
7194 * GetCreationModeFromSTGM
7196 * This method will return a creation mode flag from a STGM value.
7197 * The STGM value is assumed valid.
7199 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
7201 switch(STGM_CREATE_MODE(stgm
))
7204 return CREATE_ALWAYS
;
7206 FIXME("STGM_CONVERT not implemented!\n");
7208 case STGM_FAILIFTHERE
:
7211 ERR("Invalid create mode!\n");
7217 /*************************************************************************
7218 * OLECONVERT_LoadOLE10 [Internal]
7220 * Loads the OLE10 STREAM to memory
7223 * pOleStream [I] The OLESTREAM
7224 * pData [I] Data Structure for the OLESTREAM Data
7228 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7229 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7232 * This function is used by OleConvertOLESTREAMToIStorage only.
7234 * Memory allocated for pData must be freed by the caller
7236 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
7239 HRESULT hRes
= S_OK
;
7243 pData
->pData
= NULL
;
7244 pData
->pstrOleObjFileName
= NULL
;
7246 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
7249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7250 if(dwSize
!= sizeof(pData
->dwOleID
))
7252 hRes
= CONVERT10_E_OLESTREAM_GET
;
7254 else if(pData
->dwOleID
!= OLESTREAM_ID
)
7256 hRes
= CONVERT10_E_OLESTREAM_FMT
;
7267 /* Get the TypeID... more info needed for this field */
7268 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7269 if(dwSize
!= sizeof(pData
->dwTypeID
))
7271 hRes
= CONVERT10_E_OLESTREAM_GET
;
7276 if(pData
->dwTypeID
!= 0)
7278 /* Get the length of the OleTypeName */
7279 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7280 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7282 hRes
= CONVERT10_E_OLESTREAM_GET
;
7287 if(pData
->dwOleTypeNameLength
> 0)
7289 /* Get the OleTypeName */
7290 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7291 if(dwSize
!= pData
->dwOleTypeNameLength
)
7293 hRes
= CONVERT10_E_OLESTREAM_GET
;
7299 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
7300 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
7302 hRes
= CONVERT10_E_OLESTREAM_GET
;
7306 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
7307 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
7308 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
7309 if(pData
->pstrOleObjFileName
)
7311 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
7312 if(dwSize
!= pData
->dwOleObjFileNameLength
)
7314 hRes
= CONVERT10_E_OLESTREAM_GET
;
7318 hRes
= CONVERT10_E_OLESTREAM_GET
;
7323 /* Get the Width of the Metafile */
7324 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7325 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7327 hRes
= CONVERT10_E_OLESTREAM_GET
;
7331 /* Get the Height of the Metafile */
7332 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7333 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7335 hRes
= CONVERT10_E_OLESTREAM_GET
;
7341 /* Get the Length of the Data */
7342 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7343 if(dwSize
!= sizeof(pData
->dwDataLength
))
7345 hRes
= CONVERT10_E_OLESTREAM_GET
;
7349 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
7351 if(!bStrem1
) /* if it is a second OLE stream data */
7353 pData
->dwDataLength
-= 8;
7354 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
7355 if(dwSize
!= sizeof(pData
->strUnknown
))
7357 hRes
= CONVERT10_E_OLESTREAM_GET
;
7363 if(pData
->dwDataLength
> 0)
7365 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
7367 /* Get Data (ex. IStorage, Metafile, or BMP) */
7370 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
7371 if(dwSize
!= pData
->dwDataLength
)
7373 hRes
= CONVERT10_E_OLESTREAM_GET
;
7378 hRes
= CONVERT10_E_OLESTREAM_GET
;
7387 /*************************************************************************
7388 * OLECONVERT_SaveOLE10 [Internal]
7390 * Saves the OLE10 STREAM From memory
7393 * pData [I] Data Structure for the OLESTREAM Data
7394 * pOleStream [I] The OLESTREAM to save
7398 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7401 * This function is used by OleConvertIStorageToOLESTREAM only.
7404 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
7407 HRESULT hRes
= S_OK
;
7411 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
7412 if(dwSize
!= sizeof(pData
->dwOleID
))
7414 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7419 /* Set the TypeID */
7420 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
7421 if(dwSize
!= sizeof(pData
->dwTypeID
))
7423 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7427 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
7429 /* Set the Length of the OleTypeName */
7430 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
7431 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
7433 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7438 if(pData
->dwOleTypeNameLength
> 0)
7440 /* Set the OleTypeName */
7441 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
7442 if(dwSize
!= pData
->dwOleTypeNameLength
)
7444 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7451 /* Set the width of the Metafile */
7452 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
7453 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
7455 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7461 /* Set the height of the Metafile */
7462 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
7463 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
7465 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7471 /* Set the length of the Data */
7472 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
7473 if(dwSize
!= sizeof(pData
->dwDataLength
))
7475 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7481 if(pData
->dwDataLength
> 0)
7483 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7484 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
7485 if(dwSize
!= pData
->dwDataLength
)
7487 hRes
= CONVERT10_E_OLESTREAM_PUT
;
7495 /*************************************************************************
7496 * OLECONVERT_GetOLE20FromOLE10[Internal]
7498 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7499 * opens it, and copies the content to the dest IStorage for
7500 * OleConvertOLESTREAMToIStorage
7504 * pDestStorage [I] The IStorage to copy the data to
7505 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7506 * nBufferLength [I] The size of the buffer
7515 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
7519 IStorage
*pTempStorage
;
7520 DWORD dwNumOfBytesWritten
;
7521 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7522 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7524 /* Create a temp File */
7525 GetTempPathW(MAX_PATH
, wstrTempDir
);
7526 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7527 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
7529 if(hFile
!= INVALID_HANDLE_VALUE
)
7531 /* Write IStorage Data to File */
7532 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
7535 /* Open and copy temp storage to the Dest Storage */
7536 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
7539 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
7540 IStorage_Release(pTempStorage
);
7542 DeleteFileW(wstrTempFile
);
7547 /*************************************************************************
7548 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7550 * Saves the OLE10 STREAM From memory
7553 * pStorage [I] The Src IStorage to copy
7554 * pData [I] The Dest Memory to write to.
7557 * The size in bytes allocated for pData
7560 * Memory allocated for pData must be freed by the caller
7562 * Used by OleConvertIStorageToOLESTREAM only.
7565 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
7569 DWORD nDataLength
= 0;
7570 IStorage
*pTempStorage
;
7571 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
7572 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
7576 /* Create temp Storage */
7577 GetTempPathW(MAX_PATH
, wstrTempDir
);
7578 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
7579 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
7583 /* Copy Src Storage to the Temp Storage */
7584 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
7585 IStorage_Release(pTempStorage
);
7587 /* Open Temp Storage as a file and copy to memory */
7588 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7589 if(hFile
!= INVALID_HANDLE_VALUE
)
7591 nDataLength
= GetFileSize(hFile
, NULL
);
7592 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
7593 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
7596 DeleteFileW(wstrTempFile
);
7601 /*************************************************************************
7602 * OLECONVERT_CreateOleStream [Internal]
7604 * Creates the "\001OLE" stream in the IStorage if necessary.
7607 * pStorage [I] Dest storage to create the stream in
7613 * This function is used by OleConvertOLESTREAMToIStorage only.
7615 * This stream is still unknown, MS Word seems to have extra data
7616 * but since the data is stored in the OLESTREAM there should be
7617 * no need to recreate the stream. If the stream is manually
7618 * deleted it will create it with this default data.
7621 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
7625 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
7626 BYTE pOleStreamHeader
[] =
7628 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7629 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7630 0x00, 0x00, 0x00, 0x00
7633 /* Create stream if not present */
7634 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7635 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7639 /* Write default Data */
7640 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
7641 IStream_Release(pStream
);
7645 /* write a string to a stream, preceded by its length */
7646 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7653 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7654 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7659 str
= CoTaskMemAlloc( len
);
7660 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7661 r
= IStream_Write( stm
, str
, len
, NULL
);
7662 CoTaskMemFree( str
);
7666 /* read a string preceded by its length from a stream */
7667 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7670 DWORD len
, count
= 0;
7674 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7677 if( count
!= sizeof(len
) )
7678 return E_OUTOFMEMORY
;
7680 TRACE("%d bytes\n",len
);
7682 str
= CoTaskMemAlloc( len
);
7684 return E_OUTOFMEMORY
;
7686 r
= IStream_Read( stm
, str
, len
, &count
);
7691 CoTaskMemFree( str
);
7692 return E_OUTOFMEMORY
;
7695 TRACE("Read string %s\n",debugstr_an(str
,len
));
7697 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7698 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7700 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7701 CoTaskMemFree( str
);
7709 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7710 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7714 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7716 static const BYTE unknown1
[12] =
7717 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7718 0xFF, 0xFF, 0xFF, 0xFF};
7719 static const BYTE unknown2
[16] =
7720 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7721 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7723 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7724 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7725 debugstr_w(szProgIDName
));
7727 /* Create a CompObj stream */
7728 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7729 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7733 /* Write CompObj Structure to stream */
7734 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7736 if( SUCCEEDED( r
) )
7737 r
= WriteClassStm( pstm
, clsid
);
7739 if( SUCCEEDED( r
) )
7740 r
= STREAM_WriteString( pstm
, lpszUserType
);
7741 if( SUCCEEDED( r
) )
7742 r
= STREAM_WriteString( pstm
, szClipName
);
7743 if( SUCCEEDED( r
) )
7744 r
= STREAM_WriteString( pstm
, szProgIDName
);
7745 if( SUCCEEDED( r
) )
7746 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7748 IStream_Release( pstm
);
7753 /***********************************************************************
7754 * WriteFmtUserTypeStg (OLE32.@)
7756 HRESULT WINAPI
WriteFmtUserTypeStg(
7757 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7760 WCHAR szwClipName
[0x40];
7761 CLSID clsid
= CLSID_NULL
;
7762 LPWSTR wstrProgID
= NULL
;
7765 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7767 /* get the clipboard format name */
7768 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7771 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7773 /* FIXME: There's room to save a CLSID and its ProgID, but
7774 the CLSID is not looked up in the registry and in all the
7775 tests I wrote it was CLSID_NULL. Where does it come from?
7778 /* get the real program ID. This may fail, but that's fine */
7779 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7781 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7783 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7784 lpszUserType
, szwClipName
, wstrProgID
);
7786 CoTaskMemFree(wstrProgID
);
7792 /******************************************************************************
7793 * ReadFmtUserTypeStg [OLE32.@]
7795 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7799 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7800 unsigned char unknown1
[12];
7801 unsigned char unknown2
[16];
7803 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7806 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7808 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7809 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7812 WARN("Failed to open stream r = %08x\n", r
);
7816 /* read the various parts of the structure */
7817 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7818 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7820 r
= ReadClassStm( stm
, &clsid
);
7824 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7828 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7832 r
= STREAM_ReadString( stm
, &szProgIDName
);
7836 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7837 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7840 /* ok, success... now we just need to store what we found */
7842 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7843 CoTaskMemFree( szOleTypeName
);
7845 if( lplpszUserType
)
7846 *lplpszUserType
= szCLSIDName
;
7847 CoTaskMemFree( szProgIDName
);
7850 IStream_Release( stm
);
7856 /*************************************************************************
7857 * OLECONVERT_CreateCompObjStream [Internal]
7859 * Creates a "\001CompObj" is the destination IStorage if necessary.
7862 * pStorage [I] The dest IStorage to create the CompObj Stream
7864 * strOleTypeName [I] The ProgID
7868 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7871 * This function is used by OleConvertOLESTREAMToIStorage only.
7873 * The stream data is stored in the OLESTREAM and there should be
7874 * no need to recreate the stream. If the stream is manually
7875 * deleted it will attempt to create it by querying the registry.
7879 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7882 HRESULT hStorageRes
, hRes
= S_OK
;
7883 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7884 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7885 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7887 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7888 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7890 /* Initialize the CompObj structure */
7891 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7892 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7893 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7896 /* Create a CompObj stream if it doesn't exist */
7897 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7898 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7899 if(hStorageRes
== S_OK
)
7901 /* copy the OleTypeName to the compobj struct */
7902 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7903 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7905 /* copy the OleTypeName to the compobj struct */
7906 /* Note: in the test made, these were Identical */
7907 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7908 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7911 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7912 bufferW
, OLESTREAM_MAX_STR_LEN
);
7913 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7919 /* Get the CLSID Default Name from the Registry */
7920 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7921 if(hErr
== ERROR_SUCCESS
)
7923 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7924 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7925 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7926 if(hErr
== ERROR_SUCCESS
)
7928 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7934 /* Write CompObj Structure to stream */
7935 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7937 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7939 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7940 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7942 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7944 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7945 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7947 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7949 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7950 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7952 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7954 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7955 IStream_Release(pStream
);
7961 /*************************************************************************
7962 * OLECONVERT_CreateOlePresStream[Internal]
7964 * Creates the "\002OlePres000" Stream with the Metafile data
7967 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7968 * dwExtentX [I] Width of the Metafile
7969 * dwExtentY [I] Height of the Metafile
7970 * pData [I] Metafile data
7971 * dwDataLength [I] Size of the Metafile data
7975 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7978 * This function is used by OleConvertOLESTREAMToIStorage only.
7981 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7985 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7986 BYTE pOlePresStreamHeader
[] =
7988 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7989 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7990 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7991 0x00, 0x00, 0x00, 0x00
7994 BYTE pOlePresStreamHeaderEmpty
[] =
7996 0x00, 0x00, 0x00, 0x00,
7997 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7998 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7999 0x00, 0x00, 0x00, 0x00
8002 /* Create the OlePres000 Stream */
8003 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8004 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8009 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
8011 memset(&OlePres
, 0, sizeof(OlePres
));
8012 /* Do we have any metafile data to save */
8013 if(dwDataLength
> 0)
8015 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
8016 nHeaderSize
= sizeof(pOlePresStreamHeader
);
8020 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
8021 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
8023 /* Set width and height of the metafile */
8024 OlePres
.dwExtentX
= dwExtentX
;
8025 OlePres
.dwExtentY
= -dwExtentY
;
8027 /* Set Data and Length */
8028 if(dwDataLength
> sizeof(METAFILEPICT16
))
8030 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
8031 OlePres
.pData
= &(pData
[8]);
8033 /* Save OlePres000 Data to Stream */
8034 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
8035 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
8036 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
8037 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
8038 if(OlePres
.dwSize
> 0)
8040 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
8042 IStream_Release(pStream
);
8046 /*************************************************************************
8047 * OLECONVERT_CreateOle10NativeStream [Internal]
8049 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8052 * pStorage [I] Dest storage to create the stream in
8053 * pData [I] Ole10 Native Data (ex. bmp)
8054 * dwDataLength [I] Size of the Ole10 Native Data
8060 * This function is used by OleConvertOLESTREAMToIStorage only.
8062 * Might need to verify the data and return appropriate error message
8065 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
8069 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8071 /* Create the Ole10Native Stream */
8072 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
8073 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
8077 /* Write info to stream */
8078 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
8079 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
8080 IStream_Release(pStream
);
8085 /*************************************************************************
8086 * OLECONVERT_GetOLE10ProgID [Internal]
8088 * Finds the ProgID (or OleTypeID) from the IStorage
8091 * pStorage [I] The Src IStorage to get the ProgID
8092 * strProgID [I] the ProgID string to get
8093 * dwSize [I] the size of the string
8097 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8100 * This function is used by OleConvertIStorageToOLESTREAM only.
8104 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
8108 LARGE_INTEGER iSeekPos
;
8109 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
8110 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8112 /* Open the CompObj Stream */
8113 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8114 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8118 /*Get the OleType from the CompObj Stream */
8119 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
8120 iSeekPos
.u
.HighPart
= 0;
8122 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8123 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
8124 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
8125 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8126 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
8127 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
8128 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
8130 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
8133 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
8135 IStream_Release(pStream
);
8140 LPOLESTR wstrProgID
;
8142 /* Get the OleType from the registry */
8143 REFCLSID clsid
= &(stat
.clsid
);
8144 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
8145 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
8148 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
8155 /*************************************************************************
8156 * OLECONVERT_GetOle10PresData [Internal]
8158 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8161 * pStorage [I] Src IStroage
8162 * pOleStream [I] Dest OleStream Mem Struct
8168 * This function is used by OleConvertIStorageToOLESTREAM only.
8170 * Memory allocated for pData must be freed by the caller
8174 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8179 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8181 /* Initialize Default data for OLESTREAM */
8182 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8183 pOleStreamData
[0].dwTypeID
= 2;
8184 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8185 pOleStreamData
[1].dwTypeID
= 0;
8186 pOleStreamData
[0].dwMetaFileWidth
= 0;
8187 pOleStreamData
[0].dwMetaFileHeight
= 0;
8188 pOleStreamData
[0].pData
= NULL
;
8189 pOleStreamData
[1].pData
= NULL
;
8191 /* Open Ole10Native Stream */
8192 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8193 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8197 /* Read Size and Data */
8198 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
8199 if(pOleStreamData
->dwDataLength
> 0)
8201 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
8202 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
8204 IStream_Release(pStream
);
8210 /*************************************************************************
8211 * OLECONVERT_GetOle20PresData[Internal]
8213 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8216 * pStorage [I] Src IStroage
8217 * pOleStreamData [I] Dest OleStream Mem Struct
8223 * This function is used by OleConvertIStorageToOLESTREAM only.
8225 * Memory allocated for pData must be freed by the caller
8227 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
8231 OLECONVERT_ISTORAGE_OLEPRES olePress
;
8232 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8234 /* Initialize Default data for OLESTREAM */
8235 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
8236 pOleStreamData
[0].dwTypeID
= 2;
8237 pOleStreamData
[0].dwMetaFileWidth
= 0;
8238 pOleStreamData
[0].dwMetaFileHeight
= 0;
8239 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
8240 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
8241 pOleStreamData
[1].dwTypeID
= 0;
8242 pOleStreamData
[1].dwOleTypeNameLength
= 0;
8243 pOleStreamData
[1].strOleTypeName
[0] = 0;
8244 pOleStreamData
[1].dwMetaFileWidth
= 0;
8245 pOleStreamData
[1].dwMetaFileHeight
= 0;
8246 pOleStreamData
[1].pData
= NULL
;
8247 pOleStreamData
[1].dwDataLength
= 0;
8250 /* Open OlePress000 stream */
8251 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
8252 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8255 LARGE_INTEGER iSeekPos
;
8256 METAFILEPICT16 MetaFilePict
;
8257 static const char strMetafilePictName
[] = "METAFILEPICT";
8259 /* Set the TypeID for a Metafile */
8260 pOleStreamData
[1].dwTypeID
= 5;
8262 /* Set the OleTypeName to Metafile */
8263 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
8264 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
8266 iSeekPos
.u
.HighPart
= 0;
8267 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
8269 /* Get Presentation Data */
8270 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
8271 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
8272 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
8273 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
8275 /*Set width and Height */
8276 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
8277 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
8278 if(olePress
.dwSize
> 0)
8281 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
8283 /* Set MetaFilePict struct */
8284 MetaFilePict
.mm
= 8;
8285 MetaFilePict
.xExt
= olePress
.dwExtentX
;
8286 MetaFilePict
.yExt
= olePress
.dwExtentY
;
8287 MetaFilePict
.hMF
= 0;
8289 /* Get Metafile Data */
8290 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
8291 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
8292 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
8294 IStream_Release(pStream
);
8298 /*************************************************************************
8299 * OleConvertOLESTREAMToIStorage [OLE32.@]
8304 * DVTARGETDEVICE parameter is not handled
8305 * Still unsure of some mem fields for OLE 10 Stream
8306 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8307 * and "\001OLE" streams
8310 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
8311 LPOLESTREAM pOleStream
,
8313 const DVTARGETDEVICE
* ptd
)
8317 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8319 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
8321 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8325 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8328 if(pstg
== NULL
|| pOleStream
== NULL
)
8330 hRes
= E_INVALIDARG
;
8335 /* Load the OLESTREAM to Memory */
8336 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
8341 /* Load the OLESTREAM to Memory (part 2)*/
8342 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
8348 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
8350 /* Do we have the IStorage Data in the OLESTREAM */
8351 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
8353 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8354 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
8358 /* It must be an original OLE 1.0 source */
8359 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8364 /* It must be an original OLE 1.0 source */
8365 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
8368 /* Create CompObj Stream if necessary */
8369 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
8372 /*Create the Ole Stream if necessary */
8373 OLECONVERT_CreateOleStream(pstg
);
8378 /* Free allocated memory */
8379 for(i
=0; i
< 2; i
++)
8381 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8382 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
8383 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
8388 /*************************************************************************
8389 * OleConvertIStorageToOLESTREAM [OLE32.@]
8396 * Still unsure of some mem fields for OLE 10 Stream
8397 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8398 * and "\001OLE" streams.
8401 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
8403 LPOLESTREAM pOleStream
)
8406 HRESULT hRes
= S_OK
;
8408 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
8409 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8411 TRACE("%p %p\n", pstg
, pOleStream
);
8413 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
8415 if(pstg
== NULL
|| pOleStream
== NULL
)
8417 hRes
= E_INVALIDARG
;
8421 /* Get the ProgID */
8422 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
8423 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
8427 /* Was it originally Ole10 */
8428 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
8431 IStream_Release(pStream
);
8432 /* Get Presentation Data for Ole10Native */
8433 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
8437 /* Get Presentation Data (OLE20) */
8438 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
8441 /* Save OLESTREAM */
8442 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
8445 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
8450 /* Free allocated memory */
8451 for(i
=0; i
< 2; i
++)
8453 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
8459 /***********************************************************************
8460 * GetConvertStg (OLE32.@)
8462 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
8463 FIXME("unimplemented stub!\n");
8467 /******************************************************************************
8468 * StgIsStorageFile [OLE32.@]
8469 * Verify if the file contains a storage object
8475 * S_OK if file has magic bytes as a storage object
8476 * S_FALSE if file is not storage
8479 StgIsStorageFile(LPCOLESTR fn
)
8485 TRACE("%s\n", debugstr_w(fn
));
8486 hf
= CreateFileW(fn
, GENERIC_READ
,
8487 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
8488 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
8490 if (hf
== INVALID_HANDLE_VALUE
)
8491 return STG_E_FILENOTFOUND
;
8493 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
8495 WARN(" unable to read file\n");
8502 if (bytes_read
!= 8) {
8503 TRACE(" too short\n");
8507 if (!memcmp(magic
,STORAGE_magic
,8)) {
8512 TRACE(" -> Invalid header.\n");
8516 /***********************************************************************
8517 * WriteClassStm (OLE32.@)
8519 * Writes a CLSID to a stream.
8522 * pStm [I] Stream to write to.
8523 * rclsid [I] CLSID to write.
8527 * Failure: HRESULT code.
8529 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
8531 TRACE("(%p,%p)\n",pStm
,rclsid
);
8533 if (!pStm
|| !rclsid
)
8534 return E_INVALIDARG
;
8536 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
8539 /***********************************************************************
8540 * ReadClassStm (OLE32.@)
8542 * Reads a CLSID from a stream.
8545 * pStm [I] Stream to read from.
8546 * rclsid [O] CLSID to read.
8550 * Failure: HRESULT code.
8552 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
8557 TRACE("(%p,%p)\n",pStm
,pclsid
);
8559 if (!pStm
|| !pclsid
)
8560 return E_INVALIDARG
;
8562 /* clear the output args */
8563 *pclsid
= CLSID_NULL
;
8565 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
8570 if (nbByte
!= sizeof(CLSID
))
8571 return STG_E_READFAULT
;